add
This commit is contained in:
parent
3ecec5fd77
commit
61fb85befa
@ -1,6 +1,4 @@
|
|||||||
package io.modules.sys.controller;
|
package io.modules.sys.controller;
|
||||||
|
|
||||||
|
|
||||||
import io.common.annotation.LogOperation;
|
import io.common.annotation.LogOperation;
|
||||||
import io.common.constant.Constant;
|
import io.common.constant.Constant;
|
||||||
import io.common.page.PageData;
|
import io.common.page.PageData;
|
||||||
@ -22,20 +20,11 @@ import io.swagger.v3.oas.annotations.Parameter;
|
|||||||
import io.swagger.v3.oas.annotations.Parameters;
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 吐槽墙
|
|
||||||
*
|
|
||||||
* @author Mark #
|
|
||||||
* @since 1.0.0 2024-12-12
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("item/wall")
|
@RequestMapping("sys/book")
|
||||||
@Tag(name="吐槽墙")
|
public class BookController {
|
||||||
public class WallController {
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BookService wallService;
|
private BookService bookService;
|
||||||
|
|
||||||
@GetMapping("page")
|
@GetMapping("page")
|
||||||
@Operation(summary = "分页")
|
@Operation(summary = "分页")
|
||||||
@ -45,58 +34,45 @@ public class WallController {
|
|||||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
@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")
|
||||||
})
|
})
|
||||||
@RequiresPermissions("item:wall:page")
|
|
||||||
public Result<PageData<BookDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
public Result<PageData<BookDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||||
PageData<BookDTO> page = wallService.page(params);
|
PageData<BookDTO> page = bookService.page(params);
|
||||||
|
|
||||||
return new Result<PageData<BookDTO>>().ok(page);
|
return new Result<PageData<BookDTO>>().ok(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("{id}")
|
@GetMapping("{id}")
|
||||||
@Operation(summary = "信息")
|
@Operation(summary = "信息")
|
||||||
@RequiresPermissions("item:wall:info")
|
|
||||||
public Result<BookDTO> get(@PathVariable("id") Long id){
|
public Result<BookDTO> get(@PathVariable("id") Long id){
|
||||||
BookDTO data = wallService.get(id);
|
BookDTO data = bookService.get(id);
|
||||||
|
|
||||||
return new Result<BookDTO>().ok(data);
|
return new Result<BookDTO>().ok(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "保存")
|
@Operation(summary = "保存")
|
||||||
@LogOperation("保存")
|
@LogOperation("保存")
|
||||||
@RequiresPermissions("item:wall:save")
|
|
||||||
public Result save(@RequestBody BookDTO dto){
|
public Result save(@RequestBody BookDTO dto){
|
||||||
//效验数据
|
//效验数据
|
||||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||||
|
bookService.save(dto);
|
||||||
wallService.save(dto);
|
|
||||||
|
|
||||||
return new Result();
|
return new Result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
@Operation(summary = "修改")
|
@Operation(summary = "修改")
|
||||||
@LogOperation("修改")
|
@LogOperation("修改")
|
||||||
@RequiresPermissions("item:wall:update")
|
|
||||||
public Result update(@RequestBody BookDTO dto){
|
public Result update(@RequestBody BookDTO dto){
|
||||||
//效验数据
|
//效验数据
|
||||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||||
|
bookService.update(dto);
|
||||||
wallService.update(dto);
|
|
||||||
|
|
||||||
return new Result();
|
return new Result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
@Operation(summary = "删除")
|
@Operation(summary = "删除")
|
||||||
@LogOperation("删除")
|
@LogOperation("删除")
|
||||||
@RequiresPermissions("item:wall:delete")
|
|
||||||
public Result delete(@RequestBody Long[] ids){
|
public Result delete(@RequestBody Long[] ids){
|
||||||
//效验数据
|
//效验数据
|
||||||
AssertUtils.isArrayEmpty(ids, "id");
|
AssertUtils.isArrayEmpty(ids, "id");
|
||||||
|
bookService.delete(ids);
|
||||||
wallService.delete(ids);
|
|
||||||
|
|
||||||
return new Result();
|
return new Result();
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
#MySQL
|
#MySQL
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://localhost:33060/block_house?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
url: jdbc:mysql://localhost:3306/block-chaincopyright?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||||
username: root
|
username: root
|
||||||
password: 123456
|
password: 123456
|
||||||
initial-size: 10
|
initial-size: 10
|
||||||
|
@ -12,6 +12,6 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
* @since 1.0.0 2024-12-12
|
* @since 1.0.0 2024-12-12
|
||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface WallDao extends BaseDao<BookEntity> {
|
public interface BookDao extends BaseDao<BookEntity> {
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package io.modules.item.dto;
|
package io.modules.item.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.media.SchemaProperty;
|
import io.swagger.v3.oas.annotations.media.SchemaProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -17,20 +18,24 @@ public class BookDTO implements Serializable {
|
|||||||
|
|
||||||
private Long id; // 主键ID
|
private Long id; // 主键ID
|
||||||
private Long userId; // 主键ID
|
private Long userId; // 主键ID
|
||||||
private String img; // 封面
|
private String image; // 封面
|
||||||
private String isbn; // ISBN编号
|
private String isbn; // ISBN编号
|
||||||
private String title; // 图书标题
|
private String title; // 图书标题
|
||||||
private String author; // 作者
|
private String author; // 作者
|
||||||
private String publisher; // 出版社
|
private String publisher; // 出版社
|
||||||
|
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
|
||||||
private Date publishDate; // 出版日期
|
private Date publishDate; // 出版日期
|
||||||
private String copyrightOwner; // 版权持有人
|
private String copyrightOwner; // 版权持有人
|
||||||
private Integer copyrightStartYear; // 版权起始年份
|
|
||||||
private Integer copyrightEndYear; // 版权到期年份
|
private String copyrightStartYear; // 版权起始年份
|
||||||
|
private String copyrightEndYear; // 版权到期年份
|
||||||
private String edition; // 版次
|
private String edition; // 版次
|
||||||
private String language; // 语言
|
private String language; // 语言
|
||||||
private BigDecimal price; // 图书定价
|
private BigDecimal price; // 图书定价
|
||||||
private String hex; // 上链哈希值
|
private String hex; // 上链哈希值
|
||||||
private Date createTime; // 创建时间
|
private Date createTime; // 创建时间
|
||||||
private String file; // 电子数据文件地址
|
private String file; // 电子数据文件地址
|
||||||
|
private String status;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ public class UserDTO implements Serializable {
|
|||||||
@SchemaProperty(name = "昵称")
|
@SchemaProperty(name = "昵称")
|
||||||
private String nickName;
|
private String nickName;
|
||||||
|
|
||||||
@SchemaProperty(name = "介绍")
|
@SchemaProperty(name = "手机号")
|
||||||
private String introduce;
|
private String phone;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.modules.item.entity;
|
package io.modules.item.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -12,19 +13,22 @@ public class BookEntity {
|
|||||||
|
|
||||||
private Long id; // 主键ID
|
private Long id; // 主键ID
|
||||||
private Long userId;
|
private Long userId;
|
||||||
private String img; // 封面
|
private String image; // 封面
|
||||||
private String isbn; // ISBN编号
|
private String isbn; // ISBN编号
|
||||||
private String title; // 图书标题
|
private String title; // 图书标题
|
||||||
private String author; // 作者
|
private String author; // 作者
|
||||||
private String publisher; // 出版社
|
private String publisher; // 出版社
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
|
||||||
private Date publishDate; // 出版日期
|
private Date publishDate; // 出版日期
|
||||||
private String copyrightOwner; // 版权持有人
|
private String copyrightOwner; // 版权持有人
|
||||||
private Integer copyrightStartYear; // 版权起始年份
|
|
||||||
private Integer copyrightEndYear; // 版权到期年份
|
private String copyrightStartYear; // 版权起始年份
|
||||||
|
private String copyrightEndYear; // 版权到期年份
|
||||||
private String edition; // 版次
|
private String edition; // 版次
|
||||||
private String language; // 语言
|
private String language; // 语言
|
||||||
private BigDecimal price; // 图书定价
|
private BigDecimal price; // 图书定价
|
||||||
private String hex; // 上链哈希值
|
private String hex; // 上链哈希值
|
||||||
private Date createTime; // 创建时间
|
private Date createTime; // 创建时间
|
||||||
private String file; // 电子数据文件地址
|
private String file; // 电子数据文件地址
|
||||||
|
private String status; // 电子数据文件地址
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class FrontUserEntity {
|
|||||||
*/
|
*/
|
||||||
private String nickName;
|
private String nickName;
|
||||||
/**
|
/**
|
||||||
* 介绍
|
* 手机号
|
||||||
*/
|
*/
|
||||||
private String introduce;
|
private String phone;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.common.service.impl.CrudServiceImpl;
|
import io.common.service.impl.CrudServiceImpl;
|
||||||
import io.modules.item.dao.WallDao;
|
import io.modules.item.dao.BookDao;
|
||||||
import io.modules.item.dto.BookDTO;
|
import io.modules.item.dto.BookDTO;
|
||||||
import io.modules.item.entity.BookEntity;
|
import io.modules.item.entity.BookEntity;
|
||||||
import io.modules.item.service.BookService;
|
import io.modules.item.service.BookService;
|
||||||
@ -14,19 +14,20 @@ import java.util.Map;
|
|||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class BookServiceImpl extends CrudServiceImpl<WallDao, BookEntity, BookDTO> implements BookService {
|
public class BookServiceImpl extends CrudServiceImpl<BookDao, BookEntity, BookDTO> implements BookService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryWrapper<BookEntity> getWrapper(Map<String, Object> params){
|
public QueryWrapper<BookEntity> getWrapper(Map<String, Object> params){
|
||||||
String id = (String)params.get("id");
|
String id = (String)params.get("id");
|
||||||
String userId = (String)params.get("userId");
|
String userId = (String)params.get("userId");
|
||||||
|
String title = (String)params.get("title");
|
||||||
|
String status = (String)params.get("status");
|
||||||
QueryWrapper<BookEntity> wrapper = new QueryWrapper<>();
|
QueryWrapper<BookEntity> wrapper = new QueryWrapper<>();
|
||||||
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
|
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
|
||||||
wrapper.eq(StrUtil.isNotBlank(userId), "userId", userId);
|
wrapper.eq(StrUtil.isNotBlank(userId), "user_id", userId);
|
||||||
|
wrapper.like(StrUtil.isNotBlank(title), "title", title);
|
||||||
|
wrapper.like(StrUtil.isNotBlank(status), "status", status);
|
||||||
|
wrapper.orderByDesc("create_time");
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
package io.controller;
|
package io.controller;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import io.annotation.Login;
|
import io.annotation.Login;
|
||||||
import io.annotation.LoginUser;
|
import io.annotation.LoginUser;
|
||||||
import io.common.page.PageData;
|
import io.common.page.PageData;
|
||||||
@ -7,7 +8,10 @@ import io.common.validator.ValidatorUtils;
|
|||||||
import io.common.validator.group.AddGroup;
|
import io.common.validator.group.AddGroup;
|
||||||
import io.common.validator.group.DefaultGroup;
|
import io.common.validator.group.DefaultGroup;
|
||||||
import io.entity.UserEntity;
|
import io.entity.UserEntity;
|
||||||
|
import io.modules.item.dao.BookDao;
|
||||||
import io.modules.item.dto.BookDTO;
|
import io.modules.item.dto.BookDTO;
|
||||||
|
import io.modules.item.dto.OrderDTO;
|
||||||
|
import io.modules.item.entity.BookEntity;
|
||||||
import io.modules.item.service.DictService;
|
import io.modules.item.service.DictService;
|
||||||
import io.modules.item.service.BookService;
|
import io.modules.item.service.BookService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -29,6 +33,9 @@ public class BookController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private BookService bookService;
|
private BookService bookService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BookDao bookDao;
|
||||||
|
|
||||||
@Login
|
@Login
|
||||||
@GetMapping("page")
|
@GetMapping("page")
|
||||||
@Operation(summary = "分页")
|
@Operation(summary = "分页")
|
||||||
@ -49,5 +56,26 @@ public class BookController {
|
|||||||
return new Result();
|
return new Result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("{id}")
|
||||||
|
@Operation(summary = "信息")
|
||||||
|
public Result<BookDTO> get(@PathVariable("id") Long id){
|
||||||
|
BookDTO data = bookService.get(id);
|
||||||
|
return new Result<BookDTO>().ok(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("hex")
|
||||||
|
@Operation(summary = "分页")
|
||||||
|
public Result<BookEntity> hex(@Parameter(hidden = true) @RequestParam String hex){
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BookEntity> lwq = new LambdaQueryWrapper<>();
|
||||||
|
lwq.eq(BookEntity::getHex,hex);
|
||||||
|
|
||||||
|
BookEntity bookEntity = bookDao.selectOne(lwq);
|
||||||
|
if (bookEntity == null){
|
||||||
|
return new Result<BookEntity>().error("查询不到信息");
|
||||||
|
}
|
||||||
|
return new Result<BookEntity>().ok(bookEntity);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ public class UserController {
|
|||||||
user.setId(dto.getId());
|
user.setId(dto.getId());
|
||||||
user.setUsername(dto.getUsername());
|
user.setUsername(dto.getUsername());
|
||||||
user.setNickName(dto.getNickName());
|
user.setNickName(dto.getNickName());
|
||||||
user.setIntroduce(dto.getIntroduce());
|
user.setPhone(dto.getPhone());
|
||||||
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
|
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
|
||||||
userService.updateById(user);
|
userService.updateById(user);
|
||||||
return new Result();
|
return new Result();
|
||||||
|
@ -30,6 +30,6 @@ public class RegisterDTO {
|
|||||||
@Schema(title = "昵称")
|
@Schema(title = "昵称")
|
||||||
private String nickName;
|
private String nickName;
|
||||||
|
|
||||||
@Schema(title = "介绍")
|
@Schema(title = "手机号")
|
||||||
private String introduce;
|
private String phone;
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ public class UserEntity implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String nickName;
|
private String nickName;
|
||||||
/**
|
/**
|
||||||
* 介绍
|
* 手机号
|
||||||
*/
|
*/
|
||||||
private String introduce;
|
private String phone;
|
||||||
/**
|
/**
|
||||||
* 用户名
|
* 用户名
|
||||||
*/
|
*/
|
||||||
|
@ -6,7 +6,7 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
druid:
|
druid:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://localhost:33060/block_house?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
url: jdbc:mysql://localhost:3306/block-chaincopyright?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||||
username: root
|
username: root
|
||||||
password: 123456
|
password: 123456
|
||||||
initial-size: 10
|
initial-size: 10
|
||||||
@ -33,7 +33,7 @@ spring:
|
|||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
web:
|
web:
|
||||||
resources:
|
resources:
|
||||||
static-locations: "file:D:/202505/block-chaincopyright/upload/"
|
static-locations: "file:D:/20250519/block-chaincopyright/upload/"
|
||||||
upload:
|
upload:
|
||||||
path: D:\202505\block-chaincopyright\upload
|
path: D:\20250519\block-chaincopyright\upload
|
||||||
url: http://localhost:18081/
|
url: http://localhost:18081/
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
<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,97 +0,0 @@
|
|||||||
<!--详情底部-->
|
|
||||||
<template>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
|
||||||
|
|
||||||
<div class="comment" v-for="item in state.commentList">
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="3" ><span style="color: #598bd3;">{{ item.nickName }}</span></el-col>
|
|
||||||
<el-col :span="6">
|
|
||||||
<span class="time">
|
|
||||||
{{item.createTime}}
|
|
||||||
</span>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<div class="content">
|
|
||||||
{{ item.content }}
|
|
||||||
</div>
|
|
||||||
<el-divider />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</el-col>
|
|
||||||
<!--热门-->
|
|
||||||
<el-col :span="12">
|
|
||||||
<div v-html="state.info.description"></div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
const route = useRoute();
|
|
||||||
const state =reactive(<any>{
|
|
||||||
commentList:[],
|
|
||||||
content:"",
|
|
||||||
info:""
|
|
||||||
})
|
|
||||||
|
|
||||||
const itemId = route.params.id;
|
|
||||||
function init() {
|
|
||||||
frontRequest.get(`/api/item/${itemId}`).then(response =>{
|
|
||||||
state.info = response.data
|
|
||||||
})
|
|
||||||
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>
|
|
||||||
.comment{
|
|
||||||
color: #666;
|
|
||||||
font-size: 13px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
.content{
|
|
||||||
padding-top: 15px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #111;
|
|
||||||
text-indent: 2em; /* 中文缩进 */
|
|
||||||
}
|
|
||||||
.time{
|
|
||||||
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>
|
|
@ -1,265 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- 搜索框 -->
|
|
||||||
<el-row v-if="isPage" justify="center">
|
|
||||||
<el-col :span="24" class="search-container">
|
|
||||||
<el-input
|
|
||||||
v-model="state.page.title"
|
|
||||||
placeholder="搜索厨房用具"
|
|
||||||
class="search-input"
|
|
||||||
clearable
|
|
||||||
@input="init"
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<el-button :icon="Search" @click="init" />
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 品牌标签云 -->
|
|
||||||
<el-row v-if="isPage" class="brand-container">
|
|
||||||
<el-col :span="24">
|
|
||||||
<div class="brand-list">
|
|
||||||
<el-tag
|
|
||||||
v-for="brand in state.brands"
|
|
||||||
:key="brand"
|
|
||||||
class="brand-item"
|
|
||||||
effect="plain"
|
|
||||||
round
|
|
||||||
>
|
|
||||||
{{ brand.name }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 房源卡片 -->
|
|
||||||
<el-row class="product-container">
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-space wrap :size="20">
|
|
||||||
<el-card
|
|
||||||
v-for="item in getList"
|
|
||||||
:key="item.id"
|
|
||||||
class="product-card"
|
|
||||||
@click="to(item.id)"
|
|
||||||
shadow="hover"
|
|
||||||
>
|
|
||||||
<div class="image-container">
|
|
||||||
<el-image :src="item.image" class="product-image" />
|
|
||||||
<el-tag v-if="item.tag" class="product-tag">{{ item.tag }}</el-tag>
|
|
||||||
</div>
|
|
||||||
<div class="product-info">
|
|
||||||
<div class="product-title">{{ item.title }}</div>
|
|
||||||
<div class="price-container">
|
|
||||||
<span class="current-price">¥{{ item.price }}</span>
|
|
||||||
<el-tag type="success" size="small">区块溯源</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-space>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<el-row v-if="isPage" class="pagination-container">
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-pagination
|
|
||||||
background
|
|
||||||
layout="prev, pager, next"
|
|
||||||
:total="state.total"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
class="custom-pagination"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { itemPage } from '~/api/itemApi'
|
|
||||||
import { Search } from '@element-plus/icons-vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { computed, reactive, onMounted } from '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: '',
|
|
||||||
limit:12
|
|
||||||
},
|
|
||||||
getList: [],
|
|
||||||
brands:[]
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化
|
|
||||||
*/
|
|
||||||
function init() {
|
|
||||||
if (props.isPage) {
|
|
||||||
itemPage(state.page).then((res) => {
|
|
||||||
state.getList = res.data.list
|
|
||||||
state.total = res.data.total
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
frontRequest.get("/api/categories/page").then((res) => {
|
|
||||||
state.brands = res.data.list
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页
|
|
||||||
*/
|
|
||||||
const handleCurrentChange = (val: number) => {
|
|
||||||
state.page.page = val
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跳转
|
|
||||||
*/
|
|
||||||
const to = (id: number) => {
|
|
||||||
router.push(`/info/${id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
/* 搜索框样式 */
|
|
||||||
.search-container {
|
|
||||||
padding: 20px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.search-input {
|
|
||||||
width: 652px;
|
|
||||||
height: 48px;
|
|
||||||
border: 2px solid #ef1f1f;
|
|
||||||
border-radius: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 品牌标签样式 */
|
|
||||||
.brand-container {
|
|
||||||
padding: 0 20px 20px;
|
|
||||||
}
|
|
||||||
.brand-list {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 12px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.brand-item {
|
|
||||||
padding: 8px 20px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.brand-item:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
background-color: #fef0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 房源卡片样式 */
|
|
||||||
.product-container {
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
.product-card {
|
|
||||||
width: 240px;
|
|
||||||
border-radius: 12px;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.product-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
position: relative;
|
|
||||||
height: 260px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
.product-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
.product-image:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
.product-tag {
|
|
||||||
position: absolute;
|
|
||||||
left: 10px;
|
|
||||||
|
|
||||||
background-color: #b9ccf6;
|
|
||||||
color: #010110;
|
|
||||||
}
|
|
||||||
:deep(.el-card__body){
|
|
||||||
padding-top: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 房源信息 */
|
|
||||||
.product-info {
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
.product-title {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
height: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
.product-title:hover {
|
|
||||||
color: #c27006;
|
|
||||||
}
|
|
||||||
.price-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.current-price {
|
|
||||||
color: #ff5722;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分页样式 */
|
|
||||||
.pagination-container {
|
|
||||||
padding: 30px 0;
|
|
||||||
}
|
|
||||||
.custom-pagination {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,90 +0,0 @@
|
|||||||
<!--房源展示风格2-->
|
|
||||||
<template>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-space wrap>
|
|
||||||
<el-card
|
|
||||||
v-for="item in state.commit" :key="state.id"
|
|
||||||
class="box-card"
|
|
||||||
@click="to(item.id)"
|
|
||||||
shadow="hover" >
|
|
||||||
<!--图片-->
|
|
||||||
<img class="img" :src="item.largePic" fit="fill" />
|
|
||||||
<!-- 标题-->
|
|
||||||
<div class="item-title ">{{item.title}}</div>
|
|
||||||
<!-- 价格&& 评分-->
|
|
||||||
<div class="price-box">
|
|
||||||
<span class="price-now ft16 " >{{item.movieType}}</span>
|
|
||||||
<span class="ft14 c999 price-old" >{{item.ratingValue}} 分</span>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-space>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
const router = useRouter()
|
|
||||||
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{
|
|
||||||
cursor: pointer;
|
|
||||||
width: 232px;
|
|
||||||
height: 300px;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
}
|
|
||||||
.img{
|
|
||||||
display: block;
|
|
||||||
width: 170px;
|
|
||||||
height: 180px;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.item-title{
|
|
||||||
color: #333;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 164px;
|
|
||||||
margin: 0 auto 8px auto;
|
|
||||||
line-height: 1;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.price-box{
|
|
||||||
text-align: center;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.price-now{
|
|
||||||
color: #FF0000;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
.ft16 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.ft14 {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.price-old{
|
|
||||||
color: #fce04b;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,114 +0,0 @@
|
|||||||
<!--房源-->
|
|
||||||
<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">
|
|
||||||
<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"
|
|
||||||
@click="to()"
|
|
||||||
shadow="hover" >
|
|
||||||
<!-- 图片-->
|
|
||||||
<el-image src="https://img2.epetbar.com/common/upload/commonfile/2020/03/20/0104650_205628.jpg" fit="fill" />
|
|
||||||
<!-- 标题-->
|
|
||||||
<div class="item-title ">澳大利亚原装进口自然馈赠Natures Gift 牛肉配方成犬粮 18kg</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>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</el-space>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
const router = useRouter()
|
|
||||||
// const props = defineProps({
|
|
||||||
// isPage: {
|
|
||||||
// type: Boolean,
|
|
||||||
// default: false,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
|
|
||||||
const to = () => {
|
|
||||||
router.push("/front/info")
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
.containa{
|
|
||||||
padding-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.box-card{
|
|
||||||
cursor: pointer;
|
|
||||||
width: 232px;
|
|
||||||
height: 300px
|
|
||||||
}
|
|
||||||
.box-card1{
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
height: 300px
|
|
||||||
}
|
|
||||||
:deep(.el-image){
|
|
||||||
display: block;
|
|
||||||
width: 170px;
|
|
||||||
height: 180px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
}
|
|
||||||
.item-title{
|
|
||||||
color: #333;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 164px;
|
|
||||||
margin: 0 auto 8px auto;
|
|
||||||
line-height: 1;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.price-box{
|
|
||||||
text-align: center;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.price-now{
|
|
||||||
color: #FF0000;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
.ft16 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.ft14 {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-container{
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -32,8 +32,9 @@
|
|||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item @click="to('/user')">个人中心</el-dropdown-item>
|
<el-dropdown-item @click="to('/user')">个人中心</el-dropdown-item>
|
||||||
<el-dropdown-item @click="to('/collect')">我的收藏</el-dropdown-item>
|
<el-dropdown-item @click="to('/upload')">发布版权</el-dropdown-item>
|
||||||
<el-dropdown-item @click="to('/order')">我的换住</el-dropdown-item>
|
<el-dropdown-item @click="to('/topic')">版权溯源</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="to('/push')">版权管理</el-dropdown-item>
|
||||||
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-chart class="chart" :option="option" autoresize />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { use } from 'echarts/core'
|
|
||||||
import { PieChart } from 'echarts/charts' // 导入饼图模块
|
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
|
||||||
import {
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
} from 'echarts/components'
|
|
||||||
import VChart from 'vue-echarts'
|
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
import axios from 'axios' // 导入 axios
|
|
||||||
|
|
||||||
use([
|
|
||||||
CanvasRenderer,
|
|
||||||
PieChart, // 使用饼图
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
])
|
|
||||||
|
|
||||||
// 定义 option 用来存储饼图配置
|
|
||||||
const option = ref<any>({
|
|
||||||
title: {
|
|
||||||
text: '入住品牌分析',
|
|
||||||
left: 'center',
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item', // 饼图的 tooltip 触发方式为 'item'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
orient: 'vertical', // 图例垂直排列
|
|
||||||
left: 'left',
|
|
||||||
data: [], // 图例数据为日期,初始为空
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '销售数量',
|
|
||||||
type: 'pie', // 设置图表类型为饼图
|
|
||||||
radius: '50%', // 饼图半径
|
|
||||||
data: [], // 饼图数据,初始为空
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取近七天的日期
|
|
||||||
function getLastSevenDays() {
|
|
||||||
const dates = []
|
|
||||||
for (let i = 6; i >= 0; i--) {
|
|
||||||
const date = new Date()
|
|
||||||
date.setDate(date.getDate() - i)
|
|
||||||
const formattedDate = date.toISOString().split('T')[0]
|
|
||||||
dates.push(formattedDate)
|
|
||||||
}
|
|
||||||
return dates
|
|
||||||
}
|
|
||||||
|
|
||||||
// 请求数据并更新饼图
|
|
||||||
async function fetchData() {
|
|
||||||
try {
|
|
||||||
const response = await adminRequest.get('/sys/item/view1')
|
|
||||||
const data = response.data
|
|
||||||
|
|
||||||
const pieData = data.map((item: { name: string, value: number }) => ({
|
|
||||||
value: item.value,
|
|
||||||
name: item.name,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 更新 option 中的图例数据和饼图数据
|
|
||||||
option.value.legend.data = data
|
|
||||||
option.value.series[0].data = pieData
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('数据加载失败', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在组件挂载后请求数据
|
|
||||||
onMounted(() => {
|
|
||||||
fetchData()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.chart {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,87 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-chart class="chart" :option="option" autoresize />
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { use } from 'echarts/core'
|
|
||||||
import { LineChart } from 'echarts/charts'
|
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
|
||||||
import {
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
GridComponent,
|
|
||||||
AxisPointerComponent,
|
|
||||||
} from 'echarts/components'
|
|
||||||
import VChart from 'vue-echarts'
|
|
||||||
use([
|
|
||||||
CanvasRenderer,
|
|
||||||
LineChart,
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
GridComponent,
|
|
||||||
AxisPointerComponent, // 如果需要显示坐标轴指示器,也可以导入
|
|
||||||
])
|
|
||||||
function getLastSevenDays() {
|
|
||||||
const dates = [];
|
|
||||||
for (let i = 6; i >= 0; i--) {
|
|
||||||
const date = new Date();
|
|
||||||
date.setDate(date.getDate() - i);
|
|
||||||
const formattedDate = date.toISOString().split('T')[0];
|
|
||||||
dates.push(formattedDate);
|
|
||||||
}
|
|
||||||
return dates;
|
|
||||||
}
|
|
||||||
|
|
||||||
const option = ref<any>({
|
|
||||||
title: {
|
|
||||||
text: '近几天房源销售分析',
|
|
||||||
left: 'center',
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: ['数量'],
|
|
||||||
left: 'left',
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: '10%',
|
|
||||||
right: '10%',
|
|
||||||
bottom: '10%',
|
|
||||||
containLabel: true, // 包含标签
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: getLastSevenDays(),
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '数量',
|
|
||||||
type: 'line', // 设置图表类型为折线图
|
|
||||||
data: [820, 932, 901, 934, 1290, 1330, 1320, 1010, 1100, 1230, 1300, 1420],
|
|
||||||
itemStyle: {
|
|
||||||
color: '#66b3ff',
|
|
||||||
},
|
|
||||||
lineStyle: {
|
|
||||||
width: 2,
|
|
||||||
},
|
|
||||||
smooth: true, // 平滑曲线
|
|
||||||
areaStyle: { // 添加面积背景色
|
|
||||||
origin: 'start',
|
|
||||||
color: 'rgba(102, 179, 255, 0.2)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.chart {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -35,8 +35,7 @@ const state = reactive({
|
|||||||
userMenu:[
|
userMenu:[
|
||||||
{name: '个人中心', path: '/user'},
|
{name: '个人中心', path: '/user'},
|
||||||
{name: '我的收藏', path: '/collect'},
|
{name: '我的收藏', path: '/collect'},
|
||||||
{name: '我的换住', path: '/order'},
|
{name: '版权管理', path: '/push'},
|
||||||
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,232 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!--增、查交易记录的按钮-->
|
|
||||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
|
||||||
|
|
||||||
<!--表格-->
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-table v-loading="loading" :data="state.getList">
|
|
||||||
<!-- 列表结开始-->
|
|
||||||
<el-table-column prop="recipientName" label="收货人" align="center" />
|
|
||||||
<el-table-column prop="phone" label="电话/手机" align="center" />
|
|
||||||
<el-table-column prop="city" label="所在地区" align="center" />
|
|
||||||
<el-table-column prop="address" 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-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="recipientName" :rules="[{ required: true, message: '请输入收货人', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.recipientName"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="电话/手机" prop="phone" :rules="[{ required: true, message: '请输入电话/手机', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.phone"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="所在地区" prop="city" :rules="[{ required: true, message: '请输入所在地区', trigger: 'blur' }]">
|
|
||||||
<!-- <el-input v-model.number="state.formData.city"/>-->
|
|
||||||
<el-cascader
|
|
||||||
style="width: 260px"
|
|
||||||
v-model="value"
|
|
||||||
:options="state.options"
|
|
||||||
:props="props"
|
|
||||||
@change="handleChange"
|
|
||||||
placeholder="请选择地区"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="详细地址" prop="address" :rules="[{ required: true, message: '请输入详细地址', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.address"/>
|
|
||||||
</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, TabsPaneContext} from 'element-plus'
|
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
const loading = ref(true)
|
|
||||||
const state = reactive(<any>{
|
|
||||||
route:"api/address",
|
|
||||||
dialogVisible:false,
|
|
||||||
getList: [],//获取数据
|
|
||||||
query:{
|
|
||||||
total: 0, // 总记录数
|
|
||||||
page: 1, // 当前页码
|
|
||||||
limit: 5, // 每页显示的记录数
|
|
||||||
title:"",
|
|
||||||
status:0,
|
|
||||||
},
|
|
||||||
formData:{},
|
|
||||||
cateList:[],
|
|
||||||
options:[],
|
|
||||||
})
|
|
||||||
const value = ref('')
|
|
||||||
const props = {
|
|
||||||
expandTrigger: 'hover' as const,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转成列表
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
const handleChange = (value:any) => {
|
|
||||||
const labels = getLabelsFromValue(value, state.options);
|
|
||||||
state.formData.city = labels
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 递归查找 label
|
|
||||||
const getLabelsFromValue = (value:any, options:any) => {
|
|
||||||
const labels:any = [];
|
|
||||||
const findLabels = (value:any, options:any) => {
|
|
||||||
for (const item of options) {
|
|
||||||
if (item.value === value[0]) {
|
|
||||||
labels.push(item.label);
|
|
||||||
if (value.length > 1 && item.children) {
|
|
||||||
findLabels(value.slice(1), item.children);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
findLabels(value, options);
|
|
||||||
return labels;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 获取交易记录
|
|
||||||
const init = () => {
|
|
||||||
frontRequest.get(`${state.route}/page`, {
|
|
||||||
params: state.query
|
|
||||||
}).then((res:any) => {
|
|
||||||
state.getList = res.data.list
|
|
||||||
state.query.total = res.data.total
|
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
// 新增交易记录
|
|
||||||
const openAddDialog = () => {
|
|
||||||
state.formData = {
|
|
||||||
sort:1
|
|
||||||
}
|
|
||||||
state.dialogVisible = true
|
|
||||||
}
|
|
||||||
// 编辑交易记录
|
|
||||||
const edit = (row: any) => {
|
|
||||||
state.formData = row
|
|
||||||
state.dialogVisible = true
|
|
||||||
}
|
|
||||||
// 保存记录
|
|
||||||
const saveTransaction = async (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return
|
|
||||||
formEl.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
state.formData.city = state.formData.city.toString()
|
|
||||||
if (state.formData.id) {
|
|
||||||
// 更新交易记录
|
|
||||||
frontRequest.put(`${state.route}`, state.formData).then(() =>{
|
|
||||||
init()
|
|
||||||
state.dialogVisible = false
|
|
||||||
ElMessage.success("提交成功~")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// 新增交易记录
|
|
||||||
frontRequest.post(`${state.route}`, state.formData).then(() =>{
|
|
||||||
init()
|
|
||||||
state.dialogVisible = false
|
|
||||||
ElMessage.success("提交成功~")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 删除交易记录
|
|
||||||
const del = async (id: number) => {
|
|
||||||
try {
|
|
||||||
await frontRequest.delete(`${state.route}/${id}`)
|
|
||||||
init()
|
|
||||||
ElMessage.success("删除成功~")
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error("删除失败~")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页码改变时的处理函数
|
|
||||||
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()
|
|
||||||
frontRequest.get(`/api/area/tree`).then((res:any) => {
|
|
||||||
console.log(res)
|
|
||||||
state.options = res.data
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</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);
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"layout": "front"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
@ -1,207 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-form :inline="true" >
|
|
||||||
<el-form-item label="分类名称">
|
|
||||||
<el-input v-model="state.query.name" placeholder="分类名称" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item >
|
|
||||||
<el-button type="primary" @click="init" >查询</el-button>
|
|
||||||
<el-button type="primary" @click="openAddDialog" >添加</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<div style="height: 30px"></div>
|
|
||||||
<!--表格-->
|
|
||||||
<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:{}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取交易记录
|
|
||||||
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>
|
|
@ -1,128 +0,0 @@
|
|||||||
<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="item.id" label="编号" align="center" width="120px"/>
|
|
||||||
<el-table-column prop="item.title" label="名称" align="center" width="120px"/>
|
|
||||||
<el-table-column prop="userEntity.nickName" label="用户昵称" align="center" width="120px" />
|
|
||||||
<el-table-column prop="itemDTO.largePic" label="图片" align="center" width="100" >
|
|
||||||
<template #default="{ row }">
|
|
||||||
<!-- 点击图片后显示弹框预览 -->
|
|
||||||
<el-image
|
|
||||||
:src="row.item?.image"
|
|
||||||
fit="cover"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="score" label="评分" align="center" />
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
route:"sys/order",
|
|
||||||
dialogVisible:false,
|
|
||||||
getList: [],//获取数据
|
|
||||||
query:{
|
|
||||||
total: 0, // 总记录数
|
|
||||||
page: 1, // 当前页码
|
|
||||||
limit: 10, // 每页显示的记录数
|
|
||||||
title:"",
|
|
||||||
type:"0",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取交易记录
|
|
||||||
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 handlePageChange = (page: number) => {
|
|
||||||
state.query.page = page
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
// 页面加载时获取交易记录
|
|
||||||
onMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
</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);
|
|
||||||
}
|
|
||||||
|
|
||||||
: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,390 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="house-management-container">
|
|
||||||
<!-- 搜索区域 -->
|
|
||||||
<div class="search-area">
|
|
||||||
<el-form :inline="true" :model="state.query" class="search-form">
|
|
||||||
<el-form-item label="房源名称:" class="search-item">
|
|
||||||
<el-input
|
|
||||||
v-model="state.query.title"
|
|
||||||
placeholder="请输入房源名称"
|
|
||||||
clearable
|
|
||||||
@input="handleSearch"
|
|
||||||
class="search-input"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="状态:" class="search-item">
|
|
||||||
<el-select clearable
|
|
||||||
v-model="state.query.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
@change="handleSearch"
|
|
||||||
style="width: 120px">
|
|
||||||
<el-option
|
|
||||||
v-for="item in state.statusOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item class="action-buttons">
|
|
||||||
<el-button type="primary" @click="init" class="query-button">
|
|
||||||
<el-icon><Search /></el-icon>
|
|
||||||
<span>查询</span>
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="openAddDialog" class="add-button">
|
|
||||||
<el-icon><Plus /></el-icon>
|
|
||||||
<span>添加</span>
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格区域 -->
|
|
||||||
<div class="table-area">
|
|
||||||
<el-table
|
|
||||||
v-loading="state.loading"
|
|
||||||
:data="state.list"
|
|
||||||
stripe
|
|
||||||
border
|
|
||||||
class="data-table"
|
|
||||||
empty-text="暂无数据"
|
|
||||||
>
|
|
||||||
<el-table-column prop="id" label="编号" width="80" align="center"/>
|
|
||||||
<el-table-column prop="title" label="标题" min-width="120" align="center"/>
|
|
||||||
<el-table-column prop="name" label="小区名称" min-width="120" align="center"/>
|
|
||||||
<el-table-column prop="city" label="城市" width="100" align="center"/>
|
|
||||||
<el-table-column prop="type" label="户型" width="120" align="center"/>
|
|
||||||
<el-table-column prop="area" label="面积" width="100" align="center"/>
|
|
||||||
<el-table-column prop="floor" label="楼层" width="80" align="center"/>
|
|
||||||
<el-table-column prop="face" label="朝向" width="100" align="center"/>
|
|
||||||
<el-table-column prop="decoration" label="装修" width="100" align="center"/>
|
|
||||||
<el-table-column label="封面图" width="100" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-image
|
|
||||||
:src="row.image"
|
|
||||||
fit="cover"
|
|
||||||
class="house-image"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="price" label="价格 (元/㎡)" width="120" align="center"/>
|
|
||||||
<el-table-column prop="tag" label="标签" min-width="100" align="center"/>
|
|
||||||
<el-table-column prop="view" label="点击量" width="100" align="center"/>
|
|
||||||
<el-table-column prop="createTime" label="创建时间" width="180" align="center"/>
|
|
||||||
<el-table-column prop="userId" label="用户ID" width="100" align="center"/>
|
|
||||||
<el-table-column label="操作" fixed="right" width="180" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button size="small" type="primary" plain @click="edit(row)">编辑</el-button>
|
|
||||||
<el-popconfirm title="确认删除该房源?" @confirm="del(row.id)">
|
|
||||||
<template #reference>
|
|
||||||
<el-button size="small" type="danger" plain>删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<div class="pagination-container" v-if="state.query.total > 0">
|
|
||||||
<el-pagination
|
|
||||||
:current-page="state.query.page"
|
|
||||||
:page-size="state.query.limit"
|
|
||||||
:total="state.query.total"
|
|
||||||
:page-sizes="[5,10,20,50]"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
background
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handlePageChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 新增/编辑 对话框 -->
|
|
||||||
<el-dialog
|
|
||||||
v-model="state.dialogVisible"
|
|
||||||
:title="state.formData.id ? '编辑房源' : '新增房源'"
|
|
||||||
width="700px"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
>
|
|
||||||
<el-form
|
|
||||||
:model="state.formData"
|
|
||||||
ref="formRef"
|
|
||||||
label-width="120px"
|
|
||||||
label-position="right"
|
|
||||||
>
|
|
||||||
<el-form-item label="用户" prop="userId" :rules="[{ required: true, message: '请选择用户', trigger: 'blur' }]">
|
|
||||||
<el-select v-model="state.formData.userId" placeholder="请选择用户" filterable clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="user in state.userList"
|
|
||||||
:key="user.id"
|
|
||||||
:label="`${user.username}(姓名: ${user.nickName})`"
|
|
||||||
:value="user.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.title" placeholder="请输入标题" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="小区名称" prop="name" :rules="[{ required: true, message: '请输入小区名称', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.name" placeholder="请输入小区名称" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="城市" prop="city" :rules="[{ required: true, message: '请输入城市', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.city" placeholder="请输入城市" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="户型" prop="type" :rules="[{ required: true, message: '请输入户型', trigger: 'blur' }]">
|
|
||||||
<el-select v-model="state.formData.type" placeholder="请选择户型" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="item in getHouseType()"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="面积(㎡)" prop="area">
|
|
||||||
<el-input v-model="state.formData.area" placeholder="请输入面积" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="楼层" prop="floor">
|
|
||||||
<el-input-number v-model="state.formData.floor" :min="0" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="朝向" prop="face">
|
|
||||||
<el-select v-model="state.formData.face" placeholder="请选择朝向">
|
|
||||||
<el-option v-for="item in getFace()" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="装修" prop="decoration">
|
|
||||||
<el-select v-model="state.formData.decoration" placeholder="请选择装修">
|
|
||||||
<el-option v-for="item in getDecoration()" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="封面图" prop="image" :rules="[{ required: true, message: '请上传封面图', trigger: 'blur' }]">
|
|
||||||
<image-upload
|
|
||||||
class="image-uploader"
|
|
||||||
:image-url="state.formData.image"
|
|
||||||
@update:imageUrl="handleImageUrl"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="价格(元/㎡)" prop="price">
|
|
||||||
<el-input-number v-model="state.formData.price" :min="0" :step="0.01" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="标签" prop="tag">
|
|
||||||
<el-input v-model="state.formData.tag" placeholder="请输入标签,用逗号分隔" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="排序" prop="sort">
|
|
||||||
<el-input-number v-model="state.formData.sort" :min="1" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="描述" prop="description">
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="state.formData.description"
|
|
||||||
rows="3"
|
|
||||||
placeholder="请输入描述"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="状态" prop="status">
|
|
||||||
<el-select v-model="state.formData.status" placeholder="请选择状态">
|
|
||||||
<el-option
|
|
||||||
v-for="item in state.statusOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="closeDialog">取消</el-button>
|
|
||||||
<el-button type="primary" @click="saveTransaction">保存</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
|
||||||
import type { FormInstance } from 'element-plus'
|
|
||||||
import { Search, Plus, Picture } from '@element-plus/icons-vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { getDecoration, getFace, getHouseType } from '~/utils/utils'
|
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
const state = reactive({
|
|
||||||
route: 'sys/house',
|
|
||||||
loading: false,
|
|
||||||
list: [] as any[],
|
|
||||||
dialogVisible: false,
|
|
||||||
// 搜索 / 分页
|
|
||||||
query: {
|
|
||||||
total: 0,
|
|
||||||
page: 1,
|
|
||||||
limit: 5,
|
|
||||||
title: '',
|
|
||||||
status: null as number | null,
|
|
||||||
},
|
|
||||||
// 下拉状态选项
|
|
||||||
statusOptions: [
|
|
||||||
{ label: '未审核', value: 0 },
|
|
||||||
{ label: '上架', value: 1 },
|
|
||||||
{ label: '下架', value: 2 },
|
|
||||||
{ label: '审核失败', value: 3 },
|
|
||||||
],
|
|
||||||
// 表单数据
|
|
||||||
formData: {} as Record<string, any>,
|
|
||||||
userList:[]
|
|
||||||
})
|
|
||||||
|
|
||||||
// 拉取列表
|
|
||||||
const init = () => {
|
|
||||||
state.loading = true
|
|
||||||
adminRequest
|
|
||||||
.get(`${state.route}/page`, { params: state.query })
|
|
||||||
.then((res: any) => {
|
|
||||||
state.list = res.data.list
|
|
||||||
state.query.total = res.data.total
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
state.loading = false
|
|
||||||
})
|
|
||||||
|
|
||||||
adminRequest.get(`sys/user-front/page`, {
|
|
||||||
params: { limit:999 }
|
|
||||||
}).then((res:any) => {
|
|
||||||
state.userList = res.data.list
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索重置页码
|
|
||||||
const handleSearch = () => {
|
|
||||||
state.query.page = 1
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开新增
|
|
||||||
const openAddDialog = () => {
|
|
||||||
state.formData = {
|
|
||||||
title: '',
|
|
||||||
name: '',
|
|
||||||
city: '',
|
|
||||||
type: '',
|
|
||||||
area: '',
|
|
||||||
floor: 1,
|
|
||||||
face: '',
|
|
||||||
decoration: '',
|
|
||||||
image: '',
|
|
||||||
price: 0,
|
|
||||||
description: '',
|
|
||||||
tag: '',
|
|
||||||
hex: '',
|
|
||||||
sort: 1,
|
|
||||||
status: 0,
|
|
||||||
view: 0,
|
|
||||||
createTime: '',
|
|
||||||
userId: null
|
|
||||||
}
|
|
||||||
state.dialogVisible = true
|
|
||||||
nextTick(() => formRef.value?.clearValidate())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭对话框
|
|
||||||
const closeDialog = () => {
|
|
||||||
state.dialogVisible = false
|
|
||||||
state.formData = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
const edit = (row: any) => {
|
|
||||||
state.formData = { ...row }
|
|
||||||
state.dialogVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存(新增/更新)
|
|
||||||
const saveTransaction = () => {
|
|
||||||
formRef.value?.validate(async valid => {
|
|
||||||
if (!valid) return
|
|
||||||
try {
|
|
||||||
const req = state.formData.id
|
|
||||||
? adminRequest.put(`${state.route}`, state.formData)
|
|
||||||
: adminRequest.post(`${state.route}`, state.formData)
|
|
||||||
await req
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
closeDialog()
|
|
||||||
init()
|
|
||||||
} catch {
|
|
||||||
ElMessage.error('操作失败')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
const del = async (id: number) => {
|
|
||||||
try {
|
|
||||||
await adminRequest.delete(`${state.route}/${id}`)
|
|
||||||
ElMessage.success('删除成功')
|
|
||||||
init()
|
|
||||||
} catch {
|
|
||||||
ElMessage.error('删除失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const handlePageChange = (page: number) => {
|
|
||||||
state.query.page = page
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
const handleSizeChange = (size: number) => {
|
|
||||||
state.query.limit = size
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
// 处理图片上传回调
|
|
||||||
const handleImageUrl = (url: string) => {
|
|
||||||
state.formData.image = url
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.house-management-container {
|
|
||||||
padding: 20px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
.search-area {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
.search-form {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.search-item { margin-right: 16px; }
|
|
||||||
.search-input { width: 220px; }
|
|
||||||
.action-buttons { margin-left: auto; display: flex; gap: 8px; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.table-area {
|
|
||||||
.data-table {
|
|
||||||
width: 100%;
|
|
||||||
.house-image {
|
|
||||||
width: 60px; height: 60px; border-radius: 4px;
|
|
||||||
&:hover { transform: scale(1.05); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.pagination-container {
|
|
||||||
margin-top: 16px;
|
|
||||||
display: flex; justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.form-dialog {
|
|
||||||
:deep(.el-dialog__body) { padding: 20px 30px; }
|
|
||||||
.image-uploader { width: 100%; }
|
|
||||||
.dialog-footer { text-align: center; padding-top: 16px; border-top: 1px solid #eee; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"layout": "admin",
|
|
||||||
"title": "房源管理"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
@ -4,26 +4,19 @@
|
|||||||
<div class="quick-access">
|
<div class="quick-access">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-card shadow="hover" class="quick-card" @click="navigateTo('house')">
|
<el-card shadow="hover" class="quick-card" @click="navigateTo('book')">
|
||||||
<div class="quick-content">
|
<div class="quick-content">
|
||||||
<el-icon :size="30" color="#409EFF"><House /></el-icon>
|
<el-icon :size="30" color="#409EFF"><House /></el-icon>
|
||||||
<span>房源管理</span>
|
<span>版权管理</span>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- <el-col :span="6">-->
|
|
||||||
<!-- <el-card shadow="hover" class="quick-card" @click="navigateTo('transactions')">-->
|
|
||||||
<!-- <div class="quick-content">-->
|
|
||||||
<!-- <el-icon :size="30" color="#67C23A"><Money /></el-icon>-->
|
|
||||||
<!-- <span>交易记录</span>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </el-card>-->
|
|
||||||
<!-- </el-col>-->
|
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-card shadow="hover" class="quick-card" @click="navigateTo('house')">
|
<el-card shadow="hover" class="quick-card" @click="navigateTo('slides')">
|
||||||
<div class="quick-content">
|
<div class="quick-content">
|
||||||
<el-icon :size="30" color="#E6A23C"><DocumentChecked /></el-icon>
|
<el-icon :size="30" color="#E6A23C"><DocumentChecked /></el-icon>
|
||||||
<span>认证管理</span>
|
<span>轮播</span>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -40,62 +33,12 @@
|
|||||||
|
|
||||||
<!-- 数据概览 -->
|
<!-- 数据概览 -->
|
||||||
<el-row :gutter="20" class="mt-20">
|
<el-row :gutter="20" class="mt-20">
|
||||||
<el-col :span="12">
|
|
||||||
|
<el-col :span="24">
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>系统数据概览</span>
|
<span>发布趋势</span>
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="data-overview">
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.totalProperties }}</div>
|
|
||||||
<div class="data-label">总房源</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.activeUsers }}</div>
|
|
||||||
<div class="data-label">活跃用户</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.totalTransactions }}</div>
|
|
||||||
<div class="data-label">总交易</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="20" class="mt-20">
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.pendingCertifications }}</div>
|
|
||||||
<div class="data-label">待认证</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.availableProperties }}</div>
|
|
||||||
<div class="data-label">可换住房源</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-value">{{ overviewData.blockHeight }}</div>
|
|
||||||
<div class="data-label">区块链高度</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>交易趋势</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div ref="chart" style="height: 220px;"></div>
|
<div ref="chart" style="height: 220px;"></div>
|
||||||
@ -103,67 +46,6 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- 最近交易和待认证 -->
|
|
||||||
<el-row :gutter="20" class="mt-20">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>最近交易记录</span>
|
|
||||||
<el-button type="text" @click="navigateTo('house')">查看全部</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-table :data="recentTransactions" style="width: 100%" height="300">
|
|
||||||
<el-table-column prop="txHash" label="交易哈希" width="180">
|
|
||||||
<template #default="{row}">
|
|
||||||
<el-tooltip :content="row.txHash" placement="top">
|
|
||||||
<span class="text-ellipsis">{{ shortenHash(row.txHash) }}</span>
|
|
||||||
</el-tooltip>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="property" label="房源" width="120" />
|
|
||||||
<el-table-column prop="fromUser" label="换出方" width="120" />
|
|
||||||
<el-table-column prop="toUser" label="换入方" width="120" />
|
|
||||||
|
|
||||||
<el-table-column prop="status" label="状态" width="100">
|
|
||||||
<template #default="{row}">
|
|
||||||
<el-tag :type="getStatusTagType(row.status)">
|
|
||||||
{{ row.status }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>待认证申请</span>
|
|
||||||
<el-button type="text" @click="navigateTo('house')">查看全部</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-table :data="pendingCertifications" style="width: 100%" height="300">
|
|
||||||
<el-table-column prop="id" label="ID" width="80" />
|
|
||||||
<el-table-column prop="user" label="用户" width="120" />
|
|
||||||
<el-table-column prop="type" label="认证类型" width="120">
|
|
||||||
<template #default="{row}">
|
|
||||||
<el-tag :type="row.type === '身份认证' ? 'success' : 'warning'">
|
|
||||||
{{ row.type }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="submitTime" label="提交时间" width="160" />
|
|
||||||
<el-table-column label="操作" width="120">
|
|
||||||
<template #default="{row}">
|
|
||||||
<el-button size="small" @click="handleCertify(row.id, 'approve')">通过</el-button>
|
|
||||||
<el-button size="small" type="danger" @click="handleCertify(row.id, 'reject')">拒绝</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -181,59 +63,6 @@ import {
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// 数据概览
|
|
||||||
const overviewData = ref({
|
|
||||||
totalProperties: 1245,
|
|
||||||
activeUsers: 876,
|
|
||||||
totalTransactions: 3421,
|
|
||||||
pendingCertifications: 23,
|
|
||||||
availableProperties: 567,
|
|
||||||
blockHeight: 124567
|
|
||||||
})
|
|
||||||
|
|
||||||
// 最近交易
|
|
||||||
const recentTransactions = ref([
|
|
||||||
{
|
|
||||||
txHash: '0x4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6',
|
|
||||||
property: '阳光公寓-302',
|
|
||||||
fromUser: '用户A',
|
|
||||||
toUser: '用户B',
|
|
||||||
date: '2023-05-15',
|
|
||||||
status: '已完成'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
txHash: '0x5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7',
|
|
||||||
property: '绿洲小区-1201',
|
|
||||||
fromUser: '用户C',
|
|
||||||
toUser: '用户D',
|
|
||||||
date: '2023-05-14',
|
|
||||||
status: '进行中'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
txHash: '0x6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8',
|
|
||||||
property: '湖畔别墅-8',
|
|
||||||
fromUser: '用户E',
|
|
||||||
toUser: '用户F',
|
|
||||||
date: '2023-05-13',
|
|
||||||
status: '已完成'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
txHash: '0x7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9',
|
|
||||||
property: '城市花园-502',
|
|
||||||
fromUser: '用户G',
|
|
||||||
toUser: '用户H',
|
|
||||||
date: '2023-05-12',
|
|
||||||
status: '已取消'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
txHash: '0x8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0',
|
|
||||||
property: '星空大厦-2103',
|
|
||||||
fromUser: '用户I',
|
|
||||||
toUser: '用户J',
|
|
||||||
date: '2023-05-11',
|
|
||||||
status: '已完成'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 待认证申请
|
// 待认证申请
|
||||||
const pendingCertifications = ref([
|
const pendingCertifications = ref([
|
||||||
@ -269,13 +98,6 @@ const pendingCertifications = ref([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// 区块链信息
|
|
||||||
const blockchainInfo = ref({
|
|
||||||
height: 124567,
|
|
||||||
nodes: 12,
|
|
||||||
pendingTxs: 8,
|
|
||||||
status: '运行正常'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 图表引用
|
// 图表引用
|
||||||
const chart = ref(null)
|
const chart = ref(null)
|
||||||
@ -292,7 +114,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ['换住交易', '认证交易']
|
data: ['用户量', '发布量']
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: '3%',
|
left: '3%',
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
<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>
|
|
@ -1,173 +0,0 @@
|
|||||||
<!--用户详情-->
|
|
||||||
<template>
|
|
||||||
<el-tabs v-model="state.page.orderStatus" @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="id" label="编号" />
|
|
||||||
<el-table-column prop="item.title" label="房源名称" />
|
|
||||||
<el-table-column prop="price" label="图片" >
|
|
||||||
<template #default="{ row }">
|
|
||||||
<img :src="row.item.image" style="width: 100px; height: 100px;">
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="price" label="购买单价" />
|
|
||||||
<el-table-column prop="quantity" label="购买数量" />
|
|
||||||
<el-table-column prop="totalAmount" label="成交总价" />
|
|
||||||
<el-table-column prop="createdTime" label="购买时间" width="220" />
|
|
||||||
<el-table-column prop="addressDTO.city" label="收货地址" width="220" />
|
|
||||||
<el-table-column label="操作" min-width="120">
|
|
||||||
<template #default="scope" >
|
|
||||||
<el-button link type="primary" size="small" v-if="state.page.orderStatus == 0" @click="update(scope.row,1)">开始发货</el-button>
|
|
||||||
<el-button link type="primary" size="small" v-if="state.page.orderStatus == 1" @click="update(scope.row,2)">确认收货</el-button>
|
|
||||||
<el-button link type="primary" size="small" v-if="state.page.orderStatus == 2" @click="update(scope.row,3)">我要评价</el-button>
|
|
||||||
<el-button link type="primary" size="small" v-if="state.page.orderStatus == 3" @click="del(scope.row.id)">删除订单</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
v-model="dialogVisible"
|
|
||||||
title="我要评价"
|
|
||||||
width="500"
|
|
||||||
>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
style="max-width: 600px"
|
|
||||||
:model="state.orderForm"
|
|
||||||
label-width="auto"
|
|
||||||
label-position="top"
|
|
||||||
>
|
|
||||||
<el-form-item
|
|
||||||
prop="evaluate"
|
|
||||||
label="评论内容"
|
|
||||||
:rules="[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '评论内容不能为空',
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<el-input v-model="state.orderForm.evaluate" :rows="4"
|
|
||||||
type="textarea"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
prop="score"
|
|
||||||
label="评分"
|
|
||||||
>
|
|
||||||
<el-rate v-model="state.orderForm.score" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="review(formRef)" >
|
|
||||||
提 交
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive } from 'vue'
|
|
||||||
import { FormInstance, TabsPaneContext } from 'element-plus'
|
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
const state = reactive({
|
|
||||||
getOrderStatus:[
|
|
||||||
{name:"未发货",status:0},
|
|
||||||
{name:"已发货",status:1},
|
|
||||||
{name:"未评价",status:2},
|
|
||||||
{name:"已完成",status:3},
|
|
||||||
],
|
|
||||||
getList:[],
|
|
||||||
orderForm:{
|
|
||||||
score: 0
|
|
||||||
},
|
|
||||||
page:{
|
|
||||||
page:1,
|
|
||||||
limit:10,
|
|
||||||
order:'desc',
|
|
||||||
orderStatus:0,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除订单
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
const del = (id:any) => {
|
|
||||||
frontRequest.delete(`/sys/order/${id}`).then(res =>{
|
|
||||||
toast.success("删除成功~")
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取列表数据
|
|
||||||
*/
|
|
||||||
function getList() {
|
|
||||||
adminRequest.get("/sys/order/page",{ params: state.page}).then(res =>{
|
|
||||||
state.getList = res.data.list;
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
|
||||||
const selectedStatus = state.getOrderStatus[tab.index].status;
|
|
||||||
state.page.orderStatus = selectedStatus;
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
const update = (data:any,status:number) => {
|
|
||||||
|
|
||||||
console.log(status)
|
|
||||||
|
|
||||||
if(status === 3){
|
|
||||||
dialogVisible.value = true
|
|
||||||
state.orderForm = {...data,evaluate:''}
|
|
||||||
state.orderForm.status = 4
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data.orderStatus = status
|
|
||||||
adminRequest.put("/sys/order",data).then(() =>{
|
|
||||||
toast.success("修改成功~")
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(()=>{
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
const review = (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return
|
|
||||||
formEl.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
updateOrderItemApi(state.orderForm).then(res =>{
|
|
||||||
toast.success("修改成功~")
|
|
||||||
dialogVisible.value = false
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"layout": "admin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
@ -16,11 +16,11 @@
|
|||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-table :data="state.getList">
|
<el-table :data="state.getList">
|
||||||
<!-- 列表结开始-->
|
<!-- 列表结开始-->
|
||||||
<el-table-column prop="id" label="id"/>
|
|
||||||
<el-table-column prop="username" label="账号"/>
|
<el-table-column prop="username" label="账号"/>
|
||||||
<el-table-column prop="createDate" label="创建时间"/>
|
<el-table-column prop="createDate" label="创建时间"/>
|
||||||
<el-table-column prop="nickName" label="昵称"/>
|
<el-table-column prop="nickName" label="昵称"/>
|
||||||
<el-table-column prop="introduce" label="介绍"/>
|
<el-table-column prop="phone" label="手机号"/>
|
||||||
<!--列表结束-->
|
<!--列表结束-->
|
||||||
<el-table-column label="操作" align="center" >
|
<el-table-column label="操作" align="center" >
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -65,8 +65,8 @@
|
|||||||
<el-form-item label="昵称" prop="nickName" :rules="[{ required: true, message: '请输入昵称', trigger: 'blur' }]">
|
<el-form-item label="昵称" prop="nickName" :rules="[{ required: true, message: '请输入昵称', trigger: 'blur' }]">
|
||||||
<el-input v-model="state.formData.nickName"/>
|
<el-input v-model="state.formData.nickName"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="介绍" prop="introduce" :rules="[{ required: true, message: '请输入介绍', trigger: 'blur' }]">
|
<el-form-item label="手机号" prop="phone" :rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }]">
|
||||||
<el-input v-model="state.formData.introduce"/>
|
<el-input v-model="state.formData.phone"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!--表单结束===============================================================================================================================-->
|
<!--表单结束===============================================================================================================================-->
|
||||||
</el-form>
|
</el-form>
|
||||||
|
@ -1,339 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cart-container">
|
|
||||||
<h1 class="cart-title">我的购物车</h1>
|
|
||||||
<div v-if="state.getList.length > 0">
|
|
||||||
<div
|
|
||||||
class="cart-item"
|
|
||||||
v-for="(item, index) in state.getList"
|
|
||||||
:key="item.id"
|
|
||||||
>
|
|
||||||
<img :src="item.item.image" class="item-image" />
|
|
||||||
<div class="item-details">
|
|
||||||
<h3>{{ item.item.title }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="quantity-control">
|
|
||||||
<button
|
|
||||||
class="quantity-btn"
|
|
||||||
@click="updateQuantity(item, -1)"
|
|
||||||
:disabled="item.quantity === 1"
|
|
||||||
>
|
|
||||||
<span class="icon">−</span>
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="quantity-input"
|
|
||||||
v-model.number="item.quantity"
|
|
||||||
min="1"
|
|
||||||
/>
|
|
||||||
<button class="quantity-btn" @click="updateQuantity(item, 1)">
|
|
||||||
<span class="icon">+</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="item-price">
|
|
||||||
¥{{ (item.price * item.quantity).toFixed(2) }}
|
|
||||||
</div>
|
|
||||||
<button class="delete-btn" @click="removeItem(item.id)">
|
|
||||||
<span class="trash-icon">🗑️</span>
|
|
||||||
删除
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="total-section">
|
|
||||||
<div class="total-price">
|
|
||||||
总计:¥{{ totalPrice.toFixed(2) }}
|
|
||||||
</div>
|
|
||||||
<!-- 收货地址选择 -->
|
|
||||||
<div class="address-section">
|
|
||||||
<label for="shipping-address">收货地址:</label>
|
|
||||||
<select id="shipping-address" v-model="selectedAddress">
|
|
||||||
<option value="">请选择收货地址</option>
|
|
||||||
<option
|
|
||||||
v-for="addr in state.addressList"
|
|
||||||
:key="addr.id"
|
|
||||||
:value="addr.id"
|
|
||||||
>
|
|
||||||
{{addr.recipientName}}- {{ addr.city }}-{{addr.address}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="checkout-btn"
|
|
||||||
:disabled="!selectedAddress"
|
|
||||||
@click="checkout"
|
|
||||||
>
|
|
||||||
<span class="payment-icon">💳</span>
|
|
||||||
立即结算
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="empty-cart">
|
|
||||||
<span class="cart-icon">🛒</span>
|
|
||||||
购物车是空的,快去选购房源吧!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive, ref, computed, onMounted } from "vue";
|
|
||||||
// 购物车数据
|
|
||||||
const state = reactive(<any>{
|
|
||||||
getList: [],
|
|
||||||
addressList:[]
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
* 计算总价
|
|
||||||
*/
|
|
||||||
const totalPrice = computed(() =>
|
|
||||||
state.getList.reduce((sum:any, item:any) => sum + item.price * item.quantity, 0)
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* 修改房源数量
|
|
||||||
*/
|
|
||||||
const updateQuantity = (data: any, sum: number) => {
|
|
||||||
data.quantity = Number(data.quantity) + sum;
|
|
||||||
frontRequest.put("/api/cart", data).then(() => {
|
|
||||||
init();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除购物车房源
|
|
||||||
*/
|
|
||||||
const removeItem = (id: number) => {
|
|
||||||
frontRequest.delete(`/api/cart/${id}`).then(() => {
|
|
||||||
ElMessage.success("删除成功");
|
|
||||||
init();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化购物车数据
|
|
||||||
*/
|
|
||||||
async function init() {
|
|
||||||
try {
|
|
||||||
const res = await frontRequest.get("/api/cart/page");
|
|
||||||
const address = await frontRequest.get("/api/address/page",{params:{limit:999}});
|
|
||||||
state.getList = res.data.list;
|
|
||||||
state.addressList = address.data.list;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求出错:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(async () => {
|
|
||||||
await init();
|
|
||||||
});
|
|
||||||
// 选中的收货地址,默认未选择
|
|
||||||
const selectedAddress = ref("");
|
|
||||||
|
|
||||||
// 结算处理方法
|
|
||||||
const checkout = () => {
|
|
||||||
if (!selectedAddress.value) {
|
|
||||||
ElMessage.error("请先选择收货地址!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let resList = []
|
|
||||||
for (let i = 0; i < state.getList.length; i++) {
|
|
||||||
resList.push({id:state.getList[i].item.id,quantity:state.getList[i].quantity,cartId:state.getList[i].id})
|
|
||||||
}
|
|
||||||
let data = {
|
|
||||||
dtoList:resList,
|
|
||||||
addressId:selectedAddress.value
|
|
||||||
}
|
|
||||||
frontRequest.post("/api/order/cart",data).then(res =>{
|
|
||||||
init()
|
|
||||||
ElMessage.success('下单成功~')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 海鲜市场风格:清新海洋蓝绿色调 */
|
|
||||||
.cart-container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 2rem auto;
|
|
||||||
background: linear-gradient(135deg, #e0f7fa, #b2ebf2);
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: 0 0 20px rgba(0, 150, 136, 0.2);
|
|
||||||
padding: 2rem;
|
|
||||||
border: 1px solid #4db6ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-title {
|
|
||||||
font-size: 2.8rem;
|
|
||||||
color: #006064;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-item {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 100px 2fr 1fr 1fr 120px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2rem;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin: 1.5rem 0;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #ffffff;
|
|
||||||
border: 1px solid #b2dfdb;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 150, 136, 0.15);
|
|
||||||
transition: transform 0.3s, box-shadow 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-item:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 150, 136, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-image {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid #4db6ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-details h3 {
|
|
||||||
color: #006064;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-control {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-btn {
|
|
||||||
background: #4db6ac;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 0.5rem 0.8rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-btn:disabled {
|
|
||||||
background: #b2dfdb;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-btn:hover:enabled {
|
|
||||||
background: #00897b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-input {
|
|
||||||
background: #e0f2f1;
|
|
||||||
border: 1px solid #4db6ac;
|
|
||||||
color: #006064;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0.3rem;
|
|
||||||
text-align: center;
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-price {
|
|
||||||
color: #006064;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-btn {
|
|
||||||
background: #ff8a80;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 0.5rem 0.8rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-btn:hover {
|
|
||||||
background: #ff5252;
|
|
||||||
}
|
|
||||||
|
|
||||||
.total-section {
|
|
||||||
background: #ffffff;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
margin-top: 2rem;
|
|
||||||
border: 1px solid #b2dfdb;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.total-price {
|
|
||||||
color: #006064;
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.address-section {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.address-section label {
|
|
||||||
color: #006064;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.address-section select {
|
|
||||||
background: #e0f2f1;
|
|
||||||
border: 1px solid #4db6ac;
|
|
||||||
color: #006064;
|
|
||||||
padding: 0.6rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkout-btn {
|
|
||||||
background: #4db6ac;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.8rem 1.5rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkout-btn:disabled {
|
|
||||||
background: #b2dfdb;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkout-btn:hover:enabled {
|
|
||||||
background: #00897b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-cart {
|
|
||||||
color: #006064;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移动端响应式 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.cart-item {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-image {
|
|
||||||
width: 80%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-btn {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"layout": "front"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
@ -1,90 +0,0 @@
|
|||||||
<!--用户详情-->
|
|
||||||
<template>
|
|
||||||
<el-tabs v-model="state.activeName" >
|
|
||||||
<el-tab-pane v-for="status in state.getOrderStatus" :label="status.name" :name="status.status">
|
|
||||||
<el-table :data="state.getList">
|
|
||||||
<!-- 列表结开始-->
|
|
||||||
<el-table-column prop="title" 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.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="area" label="面积" align="center" />
|
|
||||||
<el-table-column prop="type" label="房屋类型" align="center" />
|
|
||||||
<el-table-column prop="city" label="城市" align="center" width="160"/>
|
|
||||||
<!--列表结束-->
|
|
||||||
<el-table-column label="操作" align="center" width="160" >
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button @click="del(scope.row.id)" type="danger" size="small">取消</el-button>
|
|
||||||
<el-button @click="to(scope.row.id)" 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 { useRouter } from 'vue-router'
|
|
||||||
const router = useRouter()
|
|
||||||
import { reactive } from 'vue'
|
|
||||||
const state = reactive({
|
|
||||||
activeName:1,
|
|
||||||
getOrderStatus:[
|
|
||||||
{name:"全部",status:1},
|
|
||||||
],
|
|
||||||
getList:[]
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* 跳转
|
|
||||||
*/
|
|
||||||
const to = (id:number) => {
|
|
||||||
router.push(`/info/${id}`)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取列表数据
|
|
||||||
*/
|
|
||||||
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": "front"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
@ -1,61 +1,287 @@
|
|||||||
<template>
|
<template>
|
||||||
<!--轮播图-->
|
<div class="blockchain-library-container">
|
||||||
<el-row :gutter="20">
|
|
||||||
|
|
||||||
<el-col :span="24">
|
|
||||||
<div class="h-300px">
|
|
||||||
<carousel></carousel>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<div style="padding-top: 20px"></div>
|
<!-- 主要内容区 -->
|
||||||
<!-- 推荐房源列表-->
|
<main class="library-main">
|
||||||
<item :get-list=state.getList></item>
|
<!-- 轮播图区域 -->
|
||||||
|
<section class="banner-section">
|
||||||
|
<el-carousel height="360px" :interval="5000" arrow="always">
|
||||||
|
<el-carousel-item v-for="(item, index) in state.banners" :key="index">
|
||||||
|
<el-image
|
||||||
|
:src="item.path"
|
||||||
|
fit="cover"
|
||||||
|
class="banner-image"
|
||||||
|
:preview-src-list="[item.path]"
|
||||||
|
/>
|
||||||
|
<div class="banner-content">
|
||||||
|
<h3 class="banner-title">{{ item.name }}</h3>
|
||||||
|
</div>
|
||||||
|
</el-carousel-item>
|
||||||
|
</el-carousel>
|
||||||
|
</section>
|
||||||
|
<!-- 新书上架 -->
|
||||||
|
<section class="new-books-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2 class="section-title">
|
||||||
|
<el-icon><Calendar /></el-icon>
|
||||||
|
<span>最新发布</span>
|
||||||
|
</h2>
|
||||||
|
<el-link type="primary" :underline="false" class="view-all" @click="to('/list')">
|
||||||
|
更多 <el-icon><ArrowRight /></el-icon>
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="books-grid">
|
||||||
|
<div
|
||||||
|
v-for="book in state.newBooks"
|
||||||
|
:key="book.id"
|
||||||
|
class="book-card"
|
||||||
|
>
|
||||||
|
<div class="book-cover">
|
||||||
|
<el-image
|
||||||
|
:src="book.image"
|
||||||
|
fit="cover"
|
||||||
|
class="cover-image"
|
||||||
|
:preview-src-list="[book.image]"
|
||||||
|
/>
|
||||||
|
<div class="book-badge" v-if="book.isNew">
|
||||||
|
<span>NEW</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="book-info" @click="to(`/info/${book.id}`)">
|
||||||
|
<h3 class="book-title" :title="book.title">{{ book.title }}</h3>
|
||||||
|
<p class="book-author">{{ book.author }}</p>
|
||||||
|
<div class="book-meta">
|
||||||
|
<span class="book-price">¥{{ book.price }}</span>
|
||||||
|
<el-tag size="small" type="success" class="blockchain-tag">
|
||||||
|
<el-icon><Connection /></el-icon>
|
||||||
|
链上版权
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Carousel from '~/components/front/carousel.vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import Item from '~/components/front/item.vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const state = reactive({
|
import {
|
||||||
getList: [],
|
ArrowRight,
|
||||||
commit: [],
|
Calendar,
|
||||||
|
Connection
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
const state = reactive(<any>{
|
||||||
|
newBooks:[],
|
||||||
|
banners:[]
|
||||||
})
|
})
|
||||||
|
|
||||||
// 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) => {
|
function init() {
|
||||||
router.push(`/info/${id}`)
|
frontRequest.get("/api/book/page",{
|
||||||
|
params: { limit:5,}
|
||||||
|
}).then((res) => {
|
||||||
|
state.newBooks = res.data.list
|
||||||
|
})
|
||||||
|
|
||||||
|
frontRequest.get("/api/slides/page",{
|
||||||
|
params: { limit:5,}
|
||||||
|
}).then((res) => {
|
||||||
|
state.banners = res.data.list
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
|
||||||
|
function to(path:string) {
|
||||||
|
router.push(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.hot {
|
/* 全局样式 */
|
||||||
background-color: rgba(0, 0, 0, 0.2); /* 更浅的黑色 */
|
.blockchain-library-container {
|
||||||
border-radius: 10px; /* 四周圆弧,值可以根据需要调整 */
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
color: #333;
|
||||||
|
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
:deep(.el-button){
|
|
||||||
color: #2e191e;
|
/* 主要内容区样式 */
|
||||||
font-weight: 700;
|
.library-main {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
:deep(.el-button:hover) {
|
|
||||||
background-color: black;
|
/* 轮播图样式 */
|
||||||
|
.banner-section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-content {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 30px;
|
||||||
|
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-title {
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-desc {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
max-width: 60%;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title .el-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-all {
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-all .el-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 新书区域样式 */
|
||||||
|
.new-books-section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-card {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.book-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-info {
|
||||||
|
padding: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-author {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-price {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #e74c3c;
|
||||||
|
}
|
||||||
|
.blockchain-tag {
|
||||||
|
background-color: #e8f4fc;
|
||||||
|
color: #3498db;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.book-cover {
|
||||||
|
margin-right: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.book-title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-author {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.link-column h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
|
@ -3,18 +3,10 @@
|
|||||||
<!-- 头部区块信息 -->
|
<!-- 头部区块信息 -->
|
||||||
<div class="blockchain-header">
|
<div class="blockchain-header">
|
||||||
<div class="nav-logo">
|
<div class="nav-logo">
|
||||||
<span class="logo-icon">⛓</span>
|
<span class="logo-icon">📚</span>
|
||||||
<span class="logo-text">链家溯源 · 房源详情</span>
|
<span class="logo-text">区块链版权</span>
|
||||||
</div>
|
|
||||||
<div class="blockchain-breadcrumb">
|
|
||||||
<el-breadcrumb separator="/">
|
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item>房源详情</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item>{{ state.info.title || '加载中...' }}</el-breadcrumb-item>
|
|
||||||
</el-breadcrumb>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主要内容区 -->
|
<!-- 主要内容区 -->
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
<el-row :gutter="30">
|
<el-row :gutter="30">
|
||||||
@ -32,32 +24,52 @@
|
|||||||
<span class="badge-text">链上验证</span>
|
<span class="badge-text">链上验证</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-tags">
|
<div class="image-tags">
|
||||||
<el-tag v-if="state.info.tag" type="info" class="location-tag">
|
<el-tag v-if="state.info.language" type="info" class="language-tag">
|
||||||
<i class="el-icon-location-information"></i>
|
<i class="el-icon-chat-line-round"></i>
|
||||||
{{ state.info.tag }}
|
{{ state.info.language }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag v-if="state.info.type" type="success" class="type-tag">
|
<el-tag v-if="state.info.status" :type="getStatusTagType(state.info.status)" class="status-tag">
|
||||||
{{ state.info.type }}
|
{{ state.info.status }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 电子文件下载区 -->
|
||||||
|
<div class="file-section" v-if="state.info.file">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
class="download-button"
|
||||||
|
@click="handleDownload"
|
||||||
|
>
|
||||||
|
<el-icon><Download /></el-icon>
|
||||||
|
下载电子文件
|
||||||
|
</el-button>
|
||||||
|
<div class="file-info">
|
||||||
|
<el-icon><Document /></el-icon>
|
||||||
|
<span>已上传电子版权文件</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :xs="24" :sm="24" :md="12" :lg="14">
|
<el-col :xs="24" :sm="24" :md="12" :lg="14">
|
||||||
<!-- 信息展示区 -->
|
<!-- 信息展示区 -->
|
||||||
<div class="info-section">
|
<div class="info-section">
|
||||||
<h1 class="property-title">{{ state.info.title }}</h1>
|
<h1 class="property-title">{{ state.info.title }}</h1>
|
||||||
|
<div class="author-info">
|
||||||
|
<el-icon><User /></el-icon>
|
||||||
|
<span>{{ state.info.author }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="property-meta">
|
<div class="property-meta">
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<span class="meta-label">房源地址:</span>
|
<span class="meta-label">ISBN编号:</span>
|
||||||
<span class="meta-value">{{ state.info.city || '暂无' }}</span>
|
<span class="meta-value">{{ state.info.isbn || '暂无' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="price-section">
|
<div class="price-section">
|
||||||
<span class="price-label">价格:</span>
|
<span class="price-label">定价:</span>
|
||||||
<span class="price-value">{{ state.info.price || '0' }}</span>
|
<span class="price-value">{{ state.info.price || '0' }}</span>
|
||||||
<span class="price-unit">元/平方</span>
|
<span class="price-unit">元</span>
|
||||||
<el-tag type="success" size="small" class="blockchain-tag">
|
<el-tag type="success" size="small" class="blockchain-tag">
|
||||||
<span class="tag-icon">⛓</span>
|
<span class="tag-icon">⛓</span>
|
||||||
<span>链上价格</span>
|
<span>链上价格</span>
|
||||||
@ -66,25 +78,31 @@
|
|||||||
|
|
||||||
<div class="verification-badge">
|
<div class="verification-badge">
|
||||||
<el-icon><SuccessFilled /></el-icon>
|
<el-icon><SuccessFilled /></el-icon>
|
||||||
<span>房源信息已通过区块链验证</span>
|
<span>版权信息已通过区块链验证</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="property-details">
|
<div class="property-details">
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-label">小区名称:</span>
|
<span class="detail-label">出版社:</span>
|
||||||
<span class="detail-value">{{ state.info.name || '暂无' }}</span>
|
<span class="detail-value">{{ state.info.publisher || '暂无' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-label">朝向:</span>
|
<span class="detail-label">出版日期:</span>
|
||||||
<span class="detail-value">{{ state.info.face || '暂无' }}</span>
|
<span class="detail-value">{{ formatDate(state.info.publishDate) || '暂无' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-label">面积:</span>
|
<span class="detail-label">版权持有人:</span>
|
||||||
<span class="detail-value">{{ state.info.area || '0' }} 平方</span>
|
<span class="detail-value">{{ state.info.copyrightOwner || '暂无' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-label">楼层:</span>
|
<span class="detail-label">版权期限:</span>
|
||||||
<span class="detail-value">{{ state.info.floor || '0' }}层</span>
|
<span class="detail-value">
|
||||||
|
{{ state.info.copyrightStartYear }} - {{ state.info.copyrightEndYear || '至今' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">版次:</span>
|
||||||
|
<span class="detail-value">{{ state.info.edition || '暂无' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -94,9 +112,9 @@
|
|||||||
<el-icon><Connection /></el-icon>
|
<el-icon><Connection /></el-icon>
|
||||||
<span>区块链编号</span>
|
<span>区块链编号</span>
|
||||||
</div>
|
</div>
|
||||||
<el-tooltip :content="state.info.hex || '暂无'" placement="top">
|
<el-tooltip :content="state.info.hex || '暂无'" placement="top">
|
||||||
<div class="hash-value">
|
<div class="hash-value">
|
||||||
{{ shortenHash(state.info.hex) || '暂无区块链信息' }}
|
{{ shortenHash(state.info.hex) || '暂无区块链信息' }}
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-button
|
<el-button
|
||||||
@ -105,123 +123,17 @@
|
|||||||
class="view-transaction"
|
class="view-transaction"
|
||||||
v-if="state.info.hex"
|
v-if="state.info.hex"
|
||||||
>
|
>
|
||||||
查看交易记录 <el-icon><ArrowRight /></el-icon>
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<div class="action-buttons">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
class="buy-button"
|
|
||||||
@click="handleBuy"
|
|
||||||
v-if="state.info.price"
|
|
||||||
>
|
|
||||||
<el-icon><Refresh /></el-icon>
|
|
||||||
换住
|
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<el-button
|
|
||||||
:type="state.info.isFavorite ? 'danger' : 'info'"
|
|
||||||
class="favorite-button"
|
|
||||||
@click="handleFavorite"
|
|
||||||
>
|
|
||||||
<el-icon>{{ state.info.isFavorite ? 'StarFilled' : 'Star' }}</el-icon>
|
|
||||||
{{ state.info.isFavorite ? '已收藏' : '收藏房源' }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 购买抽屉 -->
|
|
||||||
<el-drawer
|
|
||||||
v-model="buyDrawer"
|
|
||||||
title="区块链换住确认"
|
|
||||||
size="40%"
|
|
||||||
|
|
||||||
>
|
|
||||||
<div class="drawer-content">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="state.buyForm"
|
|
||||||
label-position="top"
|
|
||||||
class="blockchain-form"
|
|
||||||
>
|
|
||||||
<el-form-item label="选择您需要交换的房源"
|
|
||||||
:rules="[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择您需要交换的房源',
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
]" prop="userHouseId">
|
|
||||||
<el-select
|
|
||||||
style="width:100%"
|
|
||||||
v-model="state.buyForm.userHouseId"
|
|
||||||
placeholder="请选择"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-option v-for="item in state.houseList"
|
|
||||||
:label="`${item.name}-${item.type}`"
|
|
||||||
:value="item.id" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="房源名称" prop="title">
|
|
||||||
<el-input v-model="state.buyForm.title" disabled>
|
|
||||||
<template #prefix>
|
|
||||||
<el-icon><OfficeBuilding /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="房源单价(元)" prop="price">
|
|
||||||
<el-input v-model="state.buyForm.price" disabled>
|
|
||||||
<template #prefix>
|
|
||||||
<el-icon><Coin /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="总价(元)" prop="totalAmount">
|
|
||||||
<el-input v-model="state.buyForm.totalAmount" disabled>
|
|
||||||
<template #prefix>
|
|
||||||
<el-icon><Money /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="用户姓名" prop="title">
|
|
||||||
<el-input v-model="state.userInfo.nickName" disabled>
|
|
||||||
<template #prefix>
|
|
||||||
<el-icon><User /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="联系方式" prop="title">
|
|
||||||
<el-input v-model="state.userInfo.introduce" disabled>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="区块链确认" prop="blockchain">
|
|
||||||
<el-alert
|
|
||||||
title="此交易将通过区块链网络确认,交易记录将永久保存"
|
|
||||||
type="info"
|
|
||||||
:closable="false"
|
|
||||||
show-icon
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
class="confirm-button"
|
|
||||||
@click="buySubmitForm(formRef)"
|
|
||||||
>
|
|
||||||
<el-icon><Checked /></el-icon>
|
|
||||||
确认申请交换
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</el-drawer>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -231,11 +143,12 @@ import { useRoute } from 'vue-router'
|
|||||||
import {
|
import {
|
||||||
SuccessFilled,
|
SuccessFilled,
|
||||||
ShoppingCart,
|
ShoppingCart,
|
||||||
Goods,
|
Download,
|
||||||
|
Document,
|
||||||
|
Notebook,
|
||||||
|
User,
|
||||||
Connection,
|
Connection,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
OfficeBuilding,
|
|
||||||
Coin,
|
Coin,
|
||||||
Money,
|
Money,
|
||||||
Checked
|
Checked
|
||||||
@ -256,31 +169,58 @@ const state = reactive(<any>{
|
|||||||
price: 0,
|
price: 0,
|
||||||
totalAmount: 0
|
totalAmount: 0
|
||||||
},
|
},
|
||||||
cateList: [],
|
userInfo: {}
|
||||||
userInfo:{},
|
|
||||||
houseList:[]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期
|
||||||
|
*/
|
||||||
|
function formatDate(dateStr: string) {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
return date.toLocaleDateString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取状态标签类型
|
||||||
|
*/
|
||||||
|
function getStatusTagType(status: string) {
|
||||||
|
switch(status) {
|
||||||
|
case '已通过': return 'success'
|
||||||
|
case '审核中': return 'warning'
|
||||||
|
case '已拒绝': return 'danger'
|
||||||
|
default: return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缩短哈希显示
|
* 缩短哈希显示
|
||||||
*/
|
*/
|
||||||
function shortenHash(hash: string) {
|
function shortenHash(hash: string) {
|
||||||
if (!hash) return ''
|
if (!hash) return ''
|
||||||
return hash.substring(0, 6) + '...' + hash.substring(hash.length - 4)
|
return hash.substring(0, 6) + '...' + hash.substring(hash.length - 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新总价
|
||||||
|
*/
|
||||||
|
function updateTotalAmount() {
|
||||||
|
state.buyForm.totalAmount = (state.buyForm.price * state.buyForm.quantity).toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化数据
|
||||||
|
*/
|
||||||
function init() {
|
function init() {
|
||||||
frontRequest.get(`/api/item/${itemId}`).then(response => {
|
frontRequest.get(`/api/book/${itemId}`).then(response => {
|
||||||
state.info = response.data
|
state.info = response.data
|
||||||
})
|
state.buyForm.title = state.info.title
|
||||||
frontRequest.get(`api/address/page`).then(response => {
|
state.buyForm.price = state.info.price
|
||||||
state.cateList = response.data.list
|
updateTotalAmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
frontRequest.get("/api/item/push").then(res =>{
|
frontRequest.get("/api/user/userInfo").then(res => {
|
||||||
state.houseList = res.data
|
state.userInfo = res.data
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,8 +228,8 @@ function init() {
|
|||||||
* 用户收藏
|
* 用户收藏
|
||||||
*/
|
*/
|
||||||
function handleFavorite() {
|
function handleFavorite() {
|
||||||
if (state.info.isFavorite) {
|
if (state.info.isFavorite) {
|
||||||
frontRequest.post("/api/behavior/delete", {itemId: itemId, type: 1}).then(res => {
|
frontRequest.post("/api/behavior/delete", {itemId: itemId, type: 1}).then(res => {
|
||||||
ElMessage.success({
|
ElMessage.success({
|
||||||
message: '已取消收藏',
|
message: '已取消收藏',
|
||||||
offset: 80
|
offset: 80
|
||||||
@ -297,7 +237,7 @@ function handleFavorite() {
|
|||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
frontRequest.post("/api/behavior", {itemId: itemId, type: 1}).then(res => {
|
frontRequest.post("/api/behavior", {itemId: itemId, type: 1}).then(res => {
|
||||||
ElMessage.success({
|
ElMessage.success({
|
||||||
message: '收藏成功',
|
message: '收藏成功',
|
||||||
offset: 80
|
offset: 80
|
||||||
@ -308,47 +248,44 @@ function handleFavorite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 购买按钮提交
|
* 下载电子文件
|
||||||
*/
|
*/
|
||||||
const buySubmitForm = (formEl: FormInstance | undefined) => {
|
function handleDownload() {
|
||||||
if (!formEl) return
|
if (!state.info.file) {
|
||||||
formEl.validate((valid) => {
|
ElMessage.warning(' 该图书暂无电子文件')
|
||||||
if (valid) {
|
return
|
||||||
frontRequest.post("/api/order",{
|
}
|
||||||
itemId:itemId, //房源编号
|
window.open(state.info.file, '_blank')
|
||||||
userHouseId:state.buyForm.userHouseId, //换住房源编号
|
|
||||||
}).then(() => {
|
|
||||||
ElMessage.success({
|
|
||||||
message: '换住信息已提交区块链网络',
|
|
||||||
offset: 80
|
|
||||||
})
|
|
||||||
buyDrawer.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 购买按钮
|
* 购买按钮
|
||||||
*/
|
*/
|
||||||
const handleBuy = () => {
|
function handlePurchase() {
|
||||||
//判断是否是自己房源
|
if (state.info.userId === state.userInfo.id) {
|
||||||
frontRequest.get("/api/user/userInfo").then(res =>{
|
ElMessage.warning(" 不能购买自己的图书版权")
|
||||||
if (state.info.userId === res.data.id){
|
return
|
||||||
ElMessage.warning("自己房源不能换住")
|
}
|
||||||
return
|
buyDrawer.value = true
|
||||||
}
|
|
||||||
state.userInfo = res.data
|
|
||||||
buyDrawer.value = true
|
|
||||||
state.buyForm.title = state.info.title
|
|
||||||
state.buyForm.id = state.info.id
|
|
||||||
state.buyForm.price = state.info.price
|
|
||||||
updateTotalAmount()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 购买按钮提交
|
||||||
|
*/
|
||||||
|
const buySubmitForm = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
frontRequest.post("/api/order", state.buyForm).then(() => {
|
||||||
|
ElMessage.success({
|
||||||
|
message: '版权购买信息已提交区块链网络',
|
||||||
|
offset: 80
|
||||||
|
})
|
||||||
|
buyDrawer.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init()
|
init()
|
||||||
@ -384,7 +321,7 @@ onMounted(() => {
|
|||||||
.logo-icon {
|
.logo-icon {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #1abc9c;
|
color: #3498db;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blockchain-breadcrumb {
|
.blockchain-breadcrumb {
|
||||||
@ -405,6 +342,7 @@ onMounted(() => {
|
|||||||
height: 400px;
|
height: 400px;
|
||||||
display: block;
|
display: block;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
background-color: #f5f7fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-image:hover {
|
.main-image:hover {
|
||||||
@ -415,7 +353,7 @@ onMounted(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
background-color: rgba(26, 188, 156, 0.9);
|
background-color: rgba(52, 152, 219, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -439,18 +377,43 @@ onMounted(() => {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.location-tag {
|
.language-tag {
|
||||||
background-color: rgba(52, 152, 219, 0.9);
|
background-color: rgba(41, 128, 185, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-tag {
|
.status-tag {
|
||||||
background-color: rgba(46, 204, 113, 0.9);
|
background-color: rgba(39, 174, 96, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 电子文件下载区 */
|
||||||
|
.file-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-button {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: #7f8c8d;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info .el-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 信息区域 */
|
/* 信息区域 */
|
||||||
.info-section {
|
.info-section {
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
@ -460,10 +423,22 @@ onMounted(() => {
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 10px;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.author-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-info .el-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.property-meta {
|
.property-meta {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
@ -478,7 +453,7 @@ onMounted(() => {
|
|||||||
.meta-label {
|
.meta-label {
|
||||||
color: #7f8c8d;
|
color: #7f8c8d;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
min-width: 80px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta-value {
|
.meta-value {
|
||||||
@ -556,7 +531,7 @@ onMounted(() => {
|
|||||||
.detail-label {
|
.detail-label {
|
||||||
color: #7f8c8d;
|
color: #7f8c8d;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
min-width: 50px;
|
min-width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-value {
|
.detail-value {
|
||||||
@ -570,7 +545,7 @@ onMounted(() => {
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
border-left: 4px solid #1abc9c;
|
border-left: 4px solid #3498db;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hash-title {
|
.hash-title {
|
||||||
@ -583,7 +558,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.hash-title .el-icon {
|
.hash-title .el-icon {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
color: #1abc9c;
|
color: #3498db;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hash-value {
|
.hash-value {
|
||||||
@ -604,7 +579,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.view-transaction {
|
.view-transaction {
|
||||||
color: #1abc9c !important;
|
color: #3498db !important;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,7 +596,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buy-button {
|
.buy-button {
|
||||||
background-color: #e74c3c;
|
background-color: #3498db;
|
||||||
border: none;
|
border: none;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -631,32 +606,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buy-button:hover {
|
.buy-button:hover {
|
||||||
background-color: #c0392b;
|
background-color: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buy-button .el-icon {
|
.buy-button .el-icon {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-button {
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0 25px;
|
|
||||||
flex: 1;
|
|
||||||
border: 1px solid #1abc9c;
|
|
||||||
color: #1abc9c;
|
|
||||||
background-color: rgba(26, 188, 156, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-button:hover {
|
|
||||||
background-color: rgba(26, 188, 156, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-button .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.favorite-button {
|
.favorite-button {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -689,6 +645,12 @@ onMounted(() => {
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
background-color: #3498db;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-button:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-button .el-icon {
|
.confirm-button .el-icon {
|
||||||
@ -704,7 +666,6 @@ onMounted(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.buy-button,
|
.buy-button,
|
||||||
.cart-button,
|
|
||||||
.favorite-button {
|
.favorite-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<el-col :span="24" class="search-container">
|
<el-col :span="24" class="search-container">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="state.page.title"
|
v-model="state.page.title"
|
||||||
placeholder="搜索房源信息"
|
placeholder="搜索版权信息"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
clearable
|
clearable
|
||||||
@input="init"
|
@input="init"
|
||||||
@ -16,24 +16,6 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- <!– 品牌标签云 –>-->
|
|
||||||
<!-- <el-row class="brand-container">-->
|
|
||||||
<!-- <el-col :span="24">-->
|
|
||||||
<!-- <div class="brand-list">-->
|
|
||||||
<!-- <el-tag-->
|
|
||||||
<!-- v-for="brand in state.brands"-->
|
|
||||||
<!-- :key="brand"-->
|
|
||||||
<!-- class="brand-item"-->
|
|
||||||
<!-- effect="plain"-->
|
|
||||||
<!-- round-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- {{ brand.name }}-->
|
|
||||||
<!-- </el-tag>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </el-col>-->
|
|
||||||
<!-- </el-row>-->
|
|
||||||
|
|
||||||
<!-- 房源卡片 -->
|
<!-- 房源卡片 -->
|
||||||
<el-row class="product-container">
|
<el-row class="product-container">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@ -51,9 +33,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<div class="product-title">{{ item.title }}</div>
|
<div class="product-title">{{ item.title }}</div>
|
||||||
<div class="product-sub">{{ item.name}}/{{item.face}}/{{ item.type}}/{{item.face}}/{{ item.area}}平方</div>
|
<div class="product-sub">{{ item.edition}}/{{item.language}}/{{ item.publisher}}/{{item.copyrightStartYear}}/{{ item.copyrightEndYear}}</div>
|
||||||
<div class="price-container">
|
<div class="price-container">
|
||||||
<span class="current-price">{{ item.price }}元/平方</span>
|
<span class="current-price">{{ item.publisher }}</span>
|
||||||
<el-tag type="success" size="small">区块溯源</el-tag>
|
<el-tag type="success" size="small">区块溯源</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -84,50 +66,23 @@ const state = reactive(<any>{
|
|||||||
page: {
|
page: {
|
||||||
page: 1,
|
page: 1,
|
||||||
title: '',
|
title: '',
|
||||||
limit:12
|
limit:8
|
||||||
},
|
},
|
||||||
getList: [{
|
getList: [],
|
||||||
"id": "9787559485054",
|
|
||||||
"title": "房东急售近地铁 近医院 诚心出售",
|
|
||||||
"name": "学府小区",
|
|
||||||
"city": "深圳",
|
|
||||||
"type": "学区房",
|
|
||||||
"area": "73.0",
|
|
||||||
"floor": 6,
|
|
||||||
"face": "西",
|
|
||||||
"decoration": "简装修",
|
|
||||||
"image": "http://localhost:18081/360x312c (1).webp",
|
|
||||||
"price": 6600,
|
|
||||||
"description": "<p>临近重点小学,交通便利</p>",
|
|
||||||
"tag": "深圳",
|
|
||||||
"hex": "0x389d64c05c566eafd726cf75af4b474250aefea760969b9ed04429ef520eb7f8730c176e2e39a429",
|
|
||||||
"sort": 3,
|
|
||||||
"status": 1,
|
|
||||||
"view": 8,
|
|
||||||
"createTime": "2025-05-19 16:42:57",
|
|
||||||
"userId": "1863920777825390593",
|
|
||||||
"user": null,
|
|
||||||
"orderEntityList": [],
|
|
||||||
"isFavorite": null
|
|
||||||
}],
|
|
||||||
brands:[]
|
brands:[]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
*/
|
*/
|
||||||
function init() {
|
function init() {
|
||||||
frontRequest.get("/api/item/page",{
|
frontRequest.get("/api/book/page",{
|
||||||
params: state.page
|
params: state.page
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
state.getList = res.data.list
|
state.getList = res.data.list
|
||||||
state.total = res.data.total
|
state.total = res.data.total
|
||||||
})
|
})
|
||||||
frontRequest.get("/api/categories/page").then((res) => {
|
|
||||||
state.brands = res.data.list
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 分页
|
* 分页
|
||||||
@ -136,7 +91,6 @@ const handleCurrentChange = (val: number) => {
|
|||||||
state.page.page = val
|
state.page.page = val
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转
|
* 跳转
|
||||||
*/
|
*/
|
||||||
@ -162,25 +116,7 @@ onMounted(() => {
|
|||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
/* 品牌标签样式 */
|
|
||||||
.brand-container {
|
|
||||||
padding: 0 20px 20px;
|
|
||||||
}
|
|
||||||
.brand-list {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 12px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.brand-item {
|
|
||||||
padding: 8px 20px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.brand-item:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
background-color: #fef0f0;
|
|
||||||
}
|
|
||||||
/* 房源卡片样式 */
|
/* 房源卡片样式 */
|
||||||
.product-container {
|
.product-container {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
@ -261,7 +197,6 @@ onMounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
class="order-table"
|
class="order-table"
|
||||||
>
|
>
|
||||||
<el-table-column prop="user.nickName" label="申请人名称" align="center"/>
|
<el-table-column prop="user.nickName" label="申请人名称" align="center"/>
|
||||||
<el-table-column prop="user.introduce" label="申请人联系方式" align="center"/>
|
<el-table-column prop="user.phone" label="申请人联系方式" align="center"/>
|
||||||
<el-table-column prop="item.name" label="申请人房源名称" align="center"/>
|
<el-table-column prop="item.name" label="申请人房源名称" align="center"/>
|
||||||
<el-table-column label="申请人房源图片" >
|
<el-table-column label="申请人房源图片" >
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
@ -1,107 +1,304 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
|
<el-form :inline="true" :model="state.query" class="demo-form-inline mb-4">
|
||||||
|
<el-form-item label="书籍名称">
|
||||||
|
<el-input v-model="state.query.title" placeholder="书籍名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="init()">查询</el-button>
|
||||||
|
<el-button @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
<el-tabs v-model="state.query.status" @tab-click="handleClick">
|
||||||
<el-form-item label="房屋名称">
|
<el-tab-pane v-for="status in state.status" :label="status.name" :name="status.status">
|
||||||
<el-input v-model="state.query.title" placeholder="房屋名称" clearable />
|
<el-table
|
||||||
</el-form-item>
|
v-loading="state.loading"
|
||||||
<el-form-item label="选择状态">
|
:data="state.list"
|
||||||
<el-select
|
stripe
|
||||||
style="width: 120px"
|
border
|
||||||
v-model="state.query.status"
|
class="data-table"
|
||||||
placeholder="请选择"
|
empty-text="暂无数据"
|
||||||
clearable
|
|
||||||
>
|
>
|
||||||
<el-option label="未审核" value="未审核" />
|
<el-table-column label="封面图" width="100" align="center">
|
||||||
<el-option label="已同意" value="已同意" />
|
<template #default="{ row }">
|
||||||
<el-option label="已拒绝 " value="已拒绝 " />
|
<el-image :src="row.image" fit="cover" class="book-image" />
|
||||||
<el-option label="已评价 " value="已评价 " />
|
</template>
|
||||||
</el-select>
|
</el-table-column>
|
||||||
</el-form-item>
|
<el-table-column prop="isbn" label="ISBN" align="center" />
|
||||||
<el-form-item>
|
<el-table-column prop="title" label="书名" align="center" />
|
||||||
<el-button type="primary" @click="init()">查询</el-button>
|
<el-table-column prop="author" label="作者" align="center" />
|
||||||
</el-form-item>
|
<el-table-column prop="publisher" label="出版社" align="center" />
|
||||||
</el-form>
|
<el-table-column prop="publishDate" label="出版日期" align="center" />
|
||||||
|
<el-table-column prop="copyrightOwner" label="版权持有人" align="center" />
|
||||||
|
<el-table-column prop="copyrightStartYear" label="版权开始" align="center" />
|
||||||
|
<el-table-column prop="copyrightEndYear" label="版权到期" align="center" />
|
||||||
|
<el-table-column prop="edition" label="版次" align="center" />
|
||||||
|
<el-table-column prop="price" label="定价(元)" align="center" />
|
||||||
|
<el-table-column label="状态" align="center" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" fixed="right" width="240" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" type="primary" plain @click="showDetail(row)">详情</el-button>
|
||||||
|
<el-button size="small" type="success" plain @click="showCertificate(row)">查看凭证</el-button>
|
||||||
|
<el-button size="small" type="warning" plain @click="downloadFile(row)">下载文件</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-table
|
<!-- 分页器 -->
|
||||||
v-loading="state.loading"
|
<el-pagination
|
||||||
:data="state.list"
|
class="pagination"
|
||||||
stripe
|
background
|
||||||
border
|
layout="prev, pager, next, jumper, ->, total"
|
||||||
class="data-table"
|
:current-page="state.query.page"
|
||||||
empty-text="暂无数据"
|
:page-size="state.query.limit"
|
||||||
>
|
:total="state.total"
|
||||||
<el-table-column prop="id" label="换住编号" width="80" align="center"/>
|
@current-change="handlePageChange"
|
||||||
<el-table-column prop="title" label="标题" min-width="120" align="center"/>
|
/>
|
||||||
<el-table-column prop="name" label="小区名称" min-width="120" align="center"/>
|
|
||||||
<el-table-column prop="city" label="城市" width="100" align="center"/>
|
|
||||||
<el-table-column prop="type" label="户型" width="120" align="center"/>
|
|
||||||
<el-table-column prop="area" label="面积" width="100" align="center"/>
|
|
||||||
<el-table-column prop="floor" label="楼层" width="80" align="center"/>
|
|
||||||
<el-table-column prop="face" label="朝向" width="100" align="center"/>
|
|
||||||
<el-table-column prop="decoration" label="装修" width="100" align="center"/>
|
|
||||||
<el-table-column label="封面图" width="100" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-image
|
|
||||||
:src="row.image"
|
|
||||||
fit="cover"
|
|
||||||
class="house-image"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="price" label="价格 (元/㎡)" width="120" align="center"/>
|
|
||||||
<el-table-column prop="tag" label="标签" min-width="100" align="center"/>
|
|
||||||
<el-table-column prop="view" label="点击量" width="100" align="center"/>
|
|
||||||
<el-table-column prop="createTime" label="创建时间" width="180" align="center"/>
|
|
||||||
<el-table-column prop="userId" label="用户ID" width="100" align="center"/>
|
|
||||||
<el-table-column label="操作" fixed="right" width="180" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button size="small" type="primary" plain @click="edit(row)">编辑</el-button>
|
|
||||||
<el-popconfirm title="确认删除该房源?" @confirm="del(row.id)">
|
|
||||||
<template #reference>
|
|
||||||
<el-button size="small" type="danger" plain>删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive } from 'vue'
|
|
||||||
|
|
||||||
const state = reactive(<any>{
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog v-model="state.detailVisible" title="书籍详情" width="50%">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="封面图">
|
||||||
|
<el-image :src="state.currentBook.image" fit="cover" class="detail-image" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="ISBN">{{ state.currentBook.isbn || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="书名">{{ state.currentBook.title || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="作者">{{ state.currentBook.author || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="出版社">{{ state.currentBook.publisher || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="出版日期">{{ state.currentBook.publishDate || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="版权持有人">{{ state.currentBook.copyrightOwner || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="版权开始">{{ state.currentBook.copyrightStartYear || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="版权到期">{{ state.currentBook.copyrightEndYear || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="版次">{{ state.currentBook.edition || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="定价">{{ state.currentBook.price ? `${state.currentBook.price}元` : '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
<el-tag :type="getStatusTagType(state.currentBook.status)">{{ state.currentBook.status || '-' }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="简介" :span="2">
|
||||||
|
<div class="book-description">{{ state.currentBook.description || '暂无简介' }}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="state.detailVisible = false">关闭</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 凭证对话框 -->
|
||||||
|
<el-dialog v-model="state.certificateVisible" title="版权凭证" width="60%">
|
||||||
|
<div class="certificate-container">
|
||||||
|
<div class="certificate-content">
|
||||||
|
<pre>{{ state.currentCertificate }}</pre>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" @click="copyCertificate" class="copy-btn">复制凭证</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, TabsPaneContext } from 'element-plus'
|
||||||
|
|
||||||
|
const state = reactive<any>({
|
||||||
|
status:[
|
||||||
|
{name:"全部",status:null},
|
||||||
|
{name:"未审核",status:"未审核"},
|
||||||
|
{name:"已同意",status:"已同意"},
|
||||||
|
{name:"已拒绝",status:"已拒绝"},
|
||||||
|
],
|
||||||
loading: false,
|
loading: false,
|
||||||
list: [],
|
list: [],
|
||||||
// 搜索
|
total: 0,
|
||||||
query: {
|
query: {
|
||||||
title: '',
|
title: '',
|
||||||
status: null,
|
status: null,
|
||||||
|
page: 1,
|
||||||
|
limit: 5,
|
||||||
},
|
},
|
||||||
|
// 标签页相关
|
||||||
|
activeTab: 'all',
|
||||||
|
// 详情相关
|
||||||
|
detailVisible: false,
|
||||||
|
currentBook: {},
|
||||||
|
// 凭证相关
|
||||||
|
certificateVisible: false,
|
||||||
|
currentCertificate: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
state.loading = true
|
state.loading = true
|
||||||
frontRequest.get("/api/item/push",{params:state.query}).then(res =>{
|
frontRequest.get('/api/book/page', { params: state.query }).then((res) => {
|
||||||
state.list = res.data
|
state.list = res.data.list
|
||||||
|
state.total = res.data.total
|
||||||
state.loading = false
|
state.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
function handlePageChange(page: number) {
|
||||||
|
state.query.page = page
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||||
|
state.query.status = state.status[tab.index].status;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
state.query.title = ''
|
||||||
|
state.query.status = null
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态标签类型
|
||||||
|
function getStatusTagType(status: string) {
|
||||||
|
switch (status) {
|
||||||
|
case '已同意':
|
||||||
|
return 'success'
|
||||||
|
case '已拒绝':
|
||||||
|
return 'danger'
|
||||||
|
case '未审核':
|
||||||
|
return 'warning'
|
||||||
|
default:
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示详情
|
||||||
|
function showDetail(row: any) {
|
||||||
|
state.currentBook = { ...row }
|
||||||
|
state.detailVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示凭证
|
||||||
|
function showCertificate(row: any) {
|
||||||
|
state.currentCertificate = row.hex || '暂无凭证数据'
|
||||||
|
state.certificateVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制凭证
|
||||||
|
function copyCertificate() {
|
||||||
|
navigator.clipboard.writeText(state.currentCertificate)
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success('凭证已复制到剪贴板')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage.error('复制失败,请手动复制')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
function downloadFile(row: any) {
|
||||||
|
if (!row.file) {
|
||||||
|
ElMessage.warning('该书籍没有可下载的文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建隐藏的可下载链接
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = row.file
|
||||||
|
link.download = `${row.title || 'book'}_${new Date().getTime()}${getFileExtension(row.file)}`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
|
||||||
|
ElMessage.success('文件下载已开始')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
function getFileExtension(url: string) {
|
||||||
|
const match = url.match(/\.([0-9a-z]+)(?:[\?#]|$)/i)
|
||||||
|
return match ? `.${match[1]}` : '.pdf' // 默认返回pdf
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.book-image {
|
||||||
|
width: 60px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-image {
|
||||||
|
width: 120px;
|
||||||
|
height: 160px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-description {
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-content {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-content pre {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
<div class="item-trace-container">
|
<div class="item-trace-container">
|
||||||
<!-- 顶部导航 -->
|
<!-- 顶部导航 -->
|
||||||
<div class="trace-header">
|
<div class="trace-header">
|
||||||
<h1 class="title">换住源溯源系统</h1>
|
<h1 class="title">版权源溯源系统</h1>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
placeholder="请输入换住区块编号"
|
placeholder="请输入版权区块编号"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter="handleSearch"
|
@keyup.enter="handleSearch"
|
||||||
@ -16,230 +16,151 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-row :gutter="20" >
|
<!-- 版权详情展示 -->
|
||||||
<el-col :span="12" v-if="!isEmpty(state.userA)">
|
<div v-if="state.bookData.hex" class="copyright-detail">
|
||||||
<el-descriptions title="甲方用户信息" :column="3" border>
|
<el-card shadow="hover" class="detail-card">
|
||||||
<el-descriptions-item
|
<div class="info-header">
|
||||||
label="姓名"
|
<h2>图书版权信息</h2>
|
||||||
label-align="right"
|
<el-tag :type="getStatusTagType(state.bookData.status)" size="large">
|
||||||
align="center"
|
{{ state.bookData.status || '未知状态' }}
|
||||||
label-class-name="my-label"
|
</el-tag>
|
||||||
class-name="my-content"
|
</div>
|
||||||
width="150px"
|
|
||||||
>{{ state.userA.nickName }}</el-descriptions-item
|
|
||||||
>
|
|
||||||
<el-descriptions-item label="手机号" label-align="right" align="center">{{ state.userA.introduce }}</el-descriptions-item>
|
|
||||||
|
|
||||||
</el-descriptions>
|
<el-divider />
|
||||||
|
<div class="info-grid">
|
||||||
</el-col>
|
|
||||||
<el-col :span="12" v-if="!isEmpty(state.userB)">
|
|
||||||
|
|
||||||
<el-descriptions title="甲方用户信息" :column="3" border>
|
|
||||||
<el-descriptions-item
|
|
||||||
label="姓名"
|
|
||||||
label-align="right"
|
|
||||||
align="center"
|
|
||||||
label-class-name="my-label"
|
|
||||||
class-name="my-content"
|
|
||||||
width="150px"
|
|
||||||
>{{ state.userB.nickName }}</el-descriptions-item
|
|
||||||
>
|
|
||||||
<el-descriptions-item label="手机号" label-align="right" align="center">{{ state.userB.introduce }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<div style="height: 40px"></div>
|
|
||||||
|
|
||||||
<!-- A-->
|
|
||||||
<el-row :gutter="20" v-if="!isEmpty(state.houseA)">
|
|
||||||
<el-col :span="6">
|
|
||||||
<el-image
|
|
||||||
:src=" state.houseA.image"
|
|
||||||
fit="cover"
|
|
||||||
class="main-image"
|
|
||||||
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="18">
|
|
||||||
<el-col :xs="24" :sm="24" :md="12" :lg="14">
|
|
||||||
<!-- 信息展示区 -->
|
|
||||||
<div class="info-section">
|
<div class="info-section">
|
||||||
<h1 class="property-title">{{ state.houseA.title}}</h1>
|
<h3>图书基本信息</h3>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
<div class="property-meta">
|
<el-descriptions-item label="封面">
|
||||||
<div class="meta-item">
|
<el-image
|
||||||
<span class="meta-label">房源地址:</span>
|
:src="state.bookData.image || 'https://via.placeholder.com/150x200?text=No+Cover'"
|
||||||
<span class="meta-value">{{ state.houseA.city || '暂无' }}</span>
|
style="width: 100px; height: 140px;"
|
||||||
</div>
|
fit="cover"
|
||||||
|
/>
|
||||||
<div class="price-section">
|
</el-descriptions-item>
|
||||||
<span class="price-label">价格:</span>
|
<el-descriptions-item label="ISBN编号">{{ state.bookData.isbn || '-' }}</el-descriptions-item>
|
||||||
<span class="price-value">{{ state.houseA.price || '0' }}</span>
|
<el-descriptions-item label="图书标题">{{ state.bookData.title || '-' }}</el-descriptions-item>
|
||||||
<span class="price-unit">元/平方</span>
|
<el-descriptions-item label="作者">{{ state.bookData.author || '-' }}</el-descriptions-item>
|
||||||
<el-tag type="success" size="small" class="blockchain-tag">
|
<el-descriptions-item label="出版社">{{ state.bookData.publisher || '-' }}</el-descriptions-item>
|
||||||
<span class="tag-icon">⛓</span>
|
<el-descriptions-item label="出版日期">{{state.bookData.publishDate || '-' }}</el-descriptions-item>
|
||||||
<span>链上价格</span>
|
<el-descriptions-item label="版次">{{ state.bookData.edition || '-' }}</el-descriptions-item>
|
||||||
</el-tag>
|
<el-descriptions-item label="语言">{{ state.bookData.language || '中文' }}</el-descriptions-item>
|
||||||
</div>
|
<el-descriptions-item label="定价">{{ state.bookData.price ? `${state.bookData.price}元` : '-' }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
<div class="verification-badge">
|
|
||||||
<el-icon><SuccessFilled /></el-icon>
|
|
||||||
<span>房源信息已通过区块链验证</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="property-details">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">小区名称:</span>
|
|
||||||
<span class="detail-value">{{ state.houseA.name || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">朝向:</span>
|
|
||||||
<span class="detail-value">{{ state.houseA.face || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">面积:</span>
|
|
||||||
<span class="detail-value">{{ state.houseA.area || '0' }} 平方</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">楼层:</span>
|
|
||||||
<span class="detail-value">{{ state.houseA.floor || '0' }}层</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 区块链哈希展示 -->
|
|
||||||
<div class="blockchain-hash">
|
|
||||||
<div class="hash-title">
|
|
||||||
<el-icon><Connection /></el-icon>
|
|
||||||
<span>区块链编号</span>
|
|
||||||
</div>
|
|
||||||
<el-tooltip :content="state.houseA.hex || '暂无'" placement="top">
|
|
||||||
<div class="hash-value">
|
|
||||||
{{ state.houseA.hex}}
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="20" v-if="!isEmpty(state.houseB)">
|
|
||||||
<el-col :span="6">
|
|
||||||
<el-image
|
|
||||||
:src=" state.houseB.image"
|
|
||||||
fit="cover"
|
|
||||||
class="main-image"
|
|
||||||
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="18">
|
|
||||||
<el-col :xs="24" :sm="24" :md="12" :lg="14">
|
|
||||||
<!-- 信息展示区 -->
|
|
||||||
<div class="info-section">
|
<div class="info-section">
|
||||||
<h1 class="property-title">{{ state.houseB.title}}</h1>
|
<h3>版权信息</h3>
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
<div class="property-meta">
|
<el-descriptions-item label="版权区块编号">{{ state.bookData.hex || '-' }}</el-descriptions-item>
|
||||||
<div class="meta-item">
|
<el-descriptions-item label="版权持有人">{{ state.bookData.copyrightOwner || '-' }}</el-descriptions-item>
|
||||||
<span class="meta-label">房源地址:</span>
|
<el-descriptions-item label="版权起始年份">{{ state.bookData.copyrightStartYear || '-' }}</el-descriptions-item>
|
||||||
<span class="meta-value">{{ state.houseB.city || '暂无' }}</span>
|
<el-descriptions-item label="版权到期年份">{{ state.bookData.copyrightEndYear || '-' }}</el-descriptions-item>
|
||||||
</div>
|
<el-descriptions-item label="上链时间">{{state.bookData.createTime || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="电子文件">
|
||||||
<div class="price-section">
|
<el-button
|
||||||
<span class="price-label">价格:</span>
|
v-if="state.bookData.file"
|
||||||
<span class="price-value">{{ state.houseB.price || '0' }}</span>
|
type="primary"
|
||||||
<span class="price-unit">元/平方</span>
|
size="small"
|
||||||
<el-tag type="success" size="small" class="blockchain-tag">
|
@click="downloadFile(state.bookData.file)"
|
||||||
<span class="tag-icon">⛓</span>
|
>
|
||||||
<span>链上价格</span>
|
下载文件
|
||||||
</el-tag>
|
</el-button>
|
||||||
</div>
|
<span v-else>-</span>
|
||||||
|
</el-descriptions-item>
|
||||||
<div class="verification-badge">
|
</el-descriptions>
|
||||||
<el-icon><SuccessFilled /></el-icon>
|
|
||||||
<span>房源信息已通过区块链验证</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="property-details">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">小区名称:</span>
|
|
||||||
<span class="detail-value">{{ state.houseB.name || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">朝向:</span>
|
|
||||||
<span class="detail-value">{{ state.houseB.face || '暂无' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">面积:</span>
|
|
||||||
<span class="detail-value">{{ state.houseB.area || '0' }} 平方</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">楼层:</span>
|
|
||||||
<span class="detail-value">{{ state.houseB.floor || '0' }}层</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 区块链哈希展示 -->
|
|
||||||
<div class="blockchain-hash">
|
|
||||||
<div class="hash-title">
|
|
||||||
<el-icon><Connection /></el-icon>
|
|
||||||
<span>区块链编号</span>
|
|
||||||
</div>
|
|
||||||
<el-tooltip :content="state.houseA.hex || '暂无'" placement="top">
|
|
||||||
<div class="hash-value">
|
|
||||||
{{ state.houseB.hex}}
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</div>
|
||||||
</el-col>
|
|
||||||
|
|
||||||
</el-row>
|
<el-divider />
|
||||||
|
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else class="empty-state">
|
||||||
|
<el-empty description="请输入版权区块编号查询详情" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { ArrowRight, Connection, Search, SuccessFilled } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
|
||||||
// 搜索查询
|
// 搜索查询
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
|
|
||||||
function isEmpty(obj) {
|
const state = reactive({
|
||||||
return Object.keys(obj).length === 0;
|
bookData: {} as any
|
||||||
}
|
|
||||||
|
|
||||||
const state = reactive(<any>{
|
|
||||||
dto:{},
|
|
||||||
houseA:{},
|
|
||||||
houseB:{},
|
|
||||||
userA:{},
|
|
||||||
userB:{},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 搜索房源
|
// 获取状态标签类型
|
||||||
|
const getStatusTagType = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case '已通过':
|
||||||
|
return 'success'
|
||||||
|
case '待审核':
|
||||||
|
return 'warning'
|
||||||
|
case '已拒绝':
|
||||||
|
return 'danger'
|
||||||
|
default:
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索版权信息
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
if (!searchQuery.value.trim()) {
|
if (!searchQuery.value.trim()) {
|
||||||
ElMessage.warning('请输入查询内容')
|
ElMessage.warning('请输入查询内容')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frontRequest.get(`/api/order/hex?hex=${searchQuery.value}`).then(response =>{
|
try {
|
||||||
|
const response = await frontRequest.get(`/api/book/hex?hex=${searchQuery.value}`)
|
||||||
ElMessage.success('查询成功')
|
ElMessage.success('查询成功')
|
||||||
state.dto = response.data.dto
|
state.bookData = response.data || {}
|
||||||
state.houseA = response.data.houseA
|
} catch (error) {
|
||||||
state.houseB = response.data.houseB
|
ElMessage.error('查询失败,请检查区块编号是否正确')
|
||||||
state.userA = response.data.userA
|
console.error('查询失败:', error)
|
||||||
state.userB = response.data.userB
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = (fileUrl: string) => {
|
||||||
|
if (!fileUrl) {
|
||||||
|
ElMessage.warning('没有可下载的文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = fileUrl
|
||||||
|
link.download = `book_${state.bookData.isbn || 'file'}_${new Date().getTime()}`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看版权凭证
|
||||||
|
const viewCertificate = (hex: string) => {
|
||||||
|
if (!hex) {
|
||||||
|
ElMessage.warning('无有效的版权凭证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 这里可以打开新窗口或弹窗显示版权凭证
|
||||||
|
window.open(`https://blockchain-explorer.com/tx/${hex}`, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看区块链交易
|
||||||
|
const viewTransaction = (hex: string) => {
|
||||||
|
if (!hex) {
|
||||||
|
ElMessage.warning('无有效的交易哈希')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 这里可以打开区块链浏览器查看交易详情
|
||||||
|
window.open(`https://blockchain-explorer.com/tx/${hex}`, '_blank')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -265,11 +186,34 @@ const handleSearch = async () => {
|
|||||||
.search-input {
|
.search-input {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copyright-detail {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.info-header h2 {
|
.info-header h2 {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.info-section h3 {
|
.info-section h3 {
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -278,363 +222,27 @@ const handleSearch = async () => {
|
|||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 全局样式 */
|
|
||||||
.blockchain-detail-container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 头部样式 */
|
|
||||||
.blockchain-header {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
border-bottom: 1px solid #eaeaea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-logo {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-icon {
|
|
||||||
margin-right: 10px;
|
|
||||||
font-size: 24px;
|
|
||||||
color: #1abc9c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockchain-breadcrumb {
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图片区域 */
|
|
||||||
.image-section {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 400px;
|
|
||||||
display: block;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-image:hover {
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockchain-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 15px;
|
|
||||||
right: 15px;
|
|
||||||
background-color: rgba(26, 188, 156, 0.9);
|
|
||||||
color: white;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-tags {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 15px;
|
|
||||||
left: 15px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-tag {
|
|
||||||
background-color: rgba(52, 152, 219, 0.9);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-tag {
|
|
||||||
background-color: rgba(46, 204, 113, 0.9);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 信息区域 */
|
|
||||||
.info-section {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.property-title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.property-meta {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-label {
|
|
||||||
color: #7f8c8d;
|
|
||||||
margin-right: 10px;
|
|
||||||
min-width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-value {
|
|
||||||
color: #34495e;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price-section {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 25px 0;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
border-bottom: 1px dashed #eaeaea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price-label {
|
|
||||||
color: #7f8c8d;
|
|
||||||
margin-right: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price-value {
|
|
||||||
color: #e74c3c;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price-unit {
|
|
||||||
color: #95a5a6;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockchain-tag {
|
|
||||||
background-color: #e8f8f5;
|
|
||||||
color: #1abc9c;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verification-badge {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
color: #1abc9c;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verification-badge .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.property-details {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 15px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-label {
|
|
||||||
color: #7f8c8d;
|
|
||||||
margin-right: 8px;
|
|
||||||
min-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-value {
|
|
||||||
color: #34495e;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 区块链哈希区域 */
|
|
||||||
.blockchain-hash {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
border-left: 4px solid #1abc9c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hash-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: #2c3e50;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hash-title .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
color: #1abc9c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hash-value {
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
background-color: #2c3e50;
|
|
||||||
color: #ecf0f1;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
word-break: break-all;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hash-value:hover {
|
|
||||||
background-color: #1a252f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-transaction {
|
|
||||||
color: #1abc9c !important;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-transaction .el-icon {
|
|
||||||
margin-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 操作按钮 */
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
justify-content: center;
|
||||||
margin-top: 30px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buy-button {
|
|
||||||
background-color: #e74c3c;
|
|
||||||
border: none;
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0 25px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buy-button:hover {
|
|
||||||
background-color: #c0392b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buy-button .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-button {
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0 25px;
|
|
||||||
flex: 1;
|
|
||||||
border: 1px solid #1abc9c;
|
|
||||||
color: #1abc9c;
|
|
||||||
background-color: rgba(26, 188, 156, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-button:hover {
|
|
||||||
background-color: rgba(26, 188, 156, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cart-button .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.favorite-button {
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0 25px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.favorite-button .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 抽屉样式 */
|
|
||||||
.drawer-content {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockchain-form {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockchain-form :deep(.el-form-item__label) {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.confirm-button {
|
|
||||||
width: 100%;
|
|
||||||
height: 48px;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-button .el-icon {
|
.empty-state {
|
||||||
margin-right: 8px;
|
margin-top: 100px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式调整 */
|
:deep(.el-descriptions__body) {
|
||||||
@media (max-width: 768px) {
|
background-color: #f9f9f9;
|
||||||
.property-details {
|
}
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
:deep(.el-descriptions__label) {
|
||||||
.action-buttons {
|
width: 120px;
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.buy-button,
|
|
||||||
.cart-button,
|
|
||||||
.favorite-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
|
@ -1,142 +1,397 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="house-upload-container">
|
<div class="copyright-upload-container">
|
||||||
<h2 class="upload-title">上传房屋信息</h2>
|
<h2 class="upload-title">图书版权信息登记</h2>
|
||||||
<el-form
|
<el-form
|
||||||
:model="state.formData"
|
:model="formData"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-width="120px"
|
label-width="120px"
|
||||||
label-position="right"
|
label-position="right"
|
||||||
class="house-upload-form"
|
class="copyright-upload-form"
|
||||||
|
:rules="rules"
|
||||||
>
|
>
|
||||||
|
<!-- 基本信息部分 -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<h3 class="section-title">基本信息</h3>
|
<h3 class="section-title">
|
||||||
|
<el-icon><Document /></el-icon>
|
||||||
|
基本信息
|
||||||
|
</h3>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]">
|
<el-form-item label="图书标题" prop="title">
|
||||||
<el-input v-model="state.formData.title" placeholder="请输入标题" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="小区名称" prop="name" :rules="[{ required: true, message: '请输入小区名称', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.name" placeholder="请输入小区名称" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="城市" prop="city" :rules="[{ required: true, message: '请输入城市', trigger: 'blur' }]">
|
|
||||||
<el-input v-model="state.formData.city" placeholder="请输入城市" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">房屋详情</h3>
|
|
||||||
<div class="form-grid">
|
|
||||||
<el-form-item label="户型" prop="type" :rules="[{ required: true, message: '请输入户型', trigger: 'blur' }]">
|
|
||||||
<el-select v-model="state.formData.type" placeholder="请选择户型" clearable class="form-input">
|
|
||||||
<el-option
|
|
||||||
v-for="item in getHouseType()"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="面积(㎡)" prop="area">
|
|
||||||
<el-input v-model="state.formData.area" placeholder="请输入面积" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="楼层" prop="floor">
|
|
||||||
<el-input-number v-model="state.formData.floor" :min="0" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="朝向" prop="face">
|
|
||||||
<el-select v-model="state.formData.face" placeholder="请选择朝向" class="form-input">
|
|
||||||
<el-option v-for="item in getFace()" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="装修" prop="decoration">
|
|
||||||
<el-select v-model="state.formData.decoration" placeholder="请选择装修" class="form-input">
|
|
||||||
<el-option v-for="item in getDecoration()" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="价格(元/㎡)" prop="price">
|
|
||||||
<el-input-number v-model="state.formData.price" :min="0" :step="0.01" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">房屋展示</h3>
|
|
||||||
<div class="form-grid">
|
|
||||||
<el-form-item label="封面图" prop="image" :rules="[{ required: true, message: '请上传封面图', trigger: 'blur' }]">
|
|
||||||
<image-upload
|
|
||||||
class="image-uploader"
|
|
||||||
:image-url="state.formData.image"
|
|
||||||
@update:imageUrl="handleImageUrl"
|
|
||||||
/>
|
|
||||||
<p class="upload-tip">建议上传高清图片,尺寸比例16:9</p>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">其他信息</h3>
|
|
||||||
<div class="form-grid">
|
|
||||||
<el-form-item label="标签" prop="tag">
|
|
||||||
<el-input v-model="state.formData.tag" placeholder="请输入标签,用逗号分隔" class="form-input" />
|
|
||||||
<p class="input-tip">例如:地铁房,学区房,精装修</p>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="排序" prop="sort">
|
|
||||||
<el-input-number v-model="state.formData.sort" :min="1" class="form-input" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="描述" prop="description">
|
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
v-model="formData.title"
|
||||||
v-model="state.formData.description"
|
placeholder="请输入图书标题"
|
||||||
rows="5"
|
clearable
|
||||||
placeholder="请输入房屋详细描述"
|
/>
|
||||||
class="form-textarea"
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="ISBN编号" prop="isbn">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.isbn"
|
||||||
|
placeholder="请输入ISBN编号"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<div class="input-tip">国际标准书号,如978-7-121-12345-6</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.author"
|
||||||
|
placeholder="请输入作者姓名"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="出版社" prop="publisher">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.publisher"
|
||||||
|
placeholder="请输入出版社名称"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 版权信息部分 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<el-icon><Lock /></el-icon>
|
||||||
|
版权信息
|
||||||
|
</h3>
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-form-item label="出版日期" prop="publishDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.publishDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择出版日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="版权持有人" prop="copyrightOwner">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.copyrightOwner"
|
||||||
|
placeholder="请输入版权持有人"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="版权起始年份" prop="copyrightStartYear">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.copyrightStartYear"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择起始年份"
|
||||||
|
value-format="YYYY"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="版权到期年份" prop="copyrightEndYear">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.copyrightEndYear"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择到期年份"
|
||||||
|
value-format="YYYY"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="版次" prop="edition">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.edition"
|
||||||
|
placeholder="如:第一版"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="语言" prop="language">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.language"
|
||||||
|
placeholder="请选择语言"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option label="中文" value="中文" />
|
||||||
|
<el-option label="英文" value="英文" />
|
||||||
|
<el-option label="日文" value="日文" />
|
||||||
|
<el-option label="其他" value="其他" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件上传部分 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<el-icon><UploadFilled /></el-icon>
|
||||||
|
文件上传
|
||||||
|
</h3>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="电子文件" prop="file" class="file-upload-item">
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
:action="state.path"
|
||||||
|
:limit="1"
|
||||||
|
:on-success="handleFileSuccess"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:before-upload="beforeFileUpload"
|
||||||
|
:file-list="fileList"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
将图书电子文件拖到此处,或<em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">
|
||||||
|
支持PDF、EPUB等格式,大小不超过50MB
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="封面图片" prop="image" class="image-upload-item">
|
||||||
|
<el-upload
|
||||||
|
:action="state.path"
|
||||||
|
list-type="picture-card"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-success="handleImageSuccess"
|
||||||
|
:before-upload="beforeImageUpload"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="formData.image"
|
||||||
|
:src="formData.image"
|
||||||
|
class="uploaded-image"
|
||||||
|
alt="封面图片"
|
||||||
|
/>
|
||||||
|
<el-icon v-else class="uploader-icon">
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
<div class="upload-tip">建议尺寸16:9,大小不超过5MB</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 其他信息部分 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<el-icon><PriceTag /></el-icon>
|
||||||
|
其他信息
|
||||||
|
</h3>
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-form-item label="图书定价" prop="price">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.price"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
<span class="price-unit">元</span>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单操作按钮 -->
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<el-button type="primary" @click="saveTransaction" class="submit-btn">保存并发布</el-button>
|
<el-button
|
||||||
<el-button @click="resetForm" class="reset-btn">重置</el-button>
|
type="primary"
|
||||||
|
@click="submitForm"
|
||||||
|
:loading="submitting"
|
||||||
|
class="submit-btn"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<el-icon><Upload /></el-icon>
|
||||||
|
</template>
|
||||||
|
提交版权信息
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
@click="resetForm"
|
||||||
|
class="reset-btn"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<el-icon><Refresh /></el-icon>
|
||||||
|
</template>
|
||||||
|
重置表单
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getDecoration, getFace, getHouseType } from '~/utils/utils'
|
import { ref, reactive } from 'vue'
|
||||||
import { reactive, ref } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance, FormRules, UploadProps } from 'element-plus'
|
||||||
const formRef = ref<FormInstance>()
|
import {
|
||||||
const state = reactive(<any>{
|
Document,
|
||||||
formData: {}
|
Lock,
|
||||||
|
UploadFilled,
|
||||||
|
PriceTag,
|
||||||
|
Upload,
|
||||||
|
Refresh,
|
||||||
|
Plus
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
path:import.meta.env.VITE_API_FRONT_BASE_URL+"/api/upload"
|
||||||
})
|
})
|
||||||
|
|
||||||
// 保存(新增/更新)
|
// 表单引用
|
||||||
const saveTransaction = () => {
|
const formRef = ref<FormInstance>()
|
||||||
formRef.value?.validate(async valid => {
|
|
||||||
if (!valid) return
|
// 表单数据
|
||||||
state.formData.status = 0
|
const formData = reactive({
|
||||||
frontRequest.post(`/api/item`, state.formData).then(res => {
|
title: '',
|
||||||
// 处理成功逻辑
|
isbn: '',
|
||||||
ElMessage.success("上传成功")
|
author: '',
|
||||||
})
|
publisher: '',
|
||||||
|
publishDate: '',
|
||||||
|
copyrightOwner: '',
|
||||||
|
copyrightStartYear: '',
|
||||||
|
copyrightEndYear: '',
|
||||||
|
edition: '',
|
||||||
|
language: '中文',
|
||||||
|
price: 0,
|
||||||
|
image: '',
|
||||||
|
file: '',
|
||||||
|
useBlockchain: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 文件列表
|
||||||
|
const fileList = ref([])
|
||||||
|
|
||||||
|
// 提交状态
|
||||||
|
const submitting = ref(false)
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = reactive<FormRules>({
|
||||||
|
title: [
|
||||||
|
{ required: true, message: '请输入图书标题', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 100, message: '长度在2到100个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
isbn: [
|
||||||
|
{ required: true, message: '请输入ISBN编号', trigger: 'blur' },
|
||||||
|
{ pattern: /^(?:\d{3}-)?\d{1,5}-\d{1,7}-\d{1,7}-\d$/, message: 'ISBN格式不正确', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
author: [
|
||||||
|
{ required: true, message: '请输入作者', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 50, message: '长度在2到50个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
publisher: [
|
||||||
|
{ required: true, message: '请输入出版社', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 100, message: '长度在2到100个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
copyrightOwner: [
|
||||||
|
{ required: true, message: '请输入版权持有人', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
image: [
|
||||||
|
{ required: true, message: '请上传封面图片', trigger: 'change' }
|
||||||
|
],
|
||||||
|
file: [
|
||||||
|
{ required: true, message: '请上传电子文件', trigger: 'change' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 图片上传成功处理
|
||||||
|
const handleImageSuccess: UploadProps['onSuccess'] = (response) => {
|
||||||
|
formData.image = response.data.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传成功处理
|
||||||
|
const handleFileSuccess: UploadProps['onSuccess'] = (response) => {
|
||||||
|
formData.file = response.data.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传错误处理
|
||||||
|
const handleUploadError: UploadProps['onError'] = () => {
|
||||||
|
ElMessage.error('文件上传失败,请重试')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片上传前校验
|
||||||
|
const beforeImageUpload: UploadProps['beforeUpload'] = (file) => {
|
||||||
|
const isImage = file.type.startsWith('image/')
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5
|
||||||
|
|
||||||
|
if (!isImage) {
|
||||||
|
ElMessage.error('只能上传图片文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!isLt5M) {
|
||||||
|
ElMessage.error('图片大小不能超过5MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传前校验
|
||||||
|
const beforeFileUpload: UploadProps['beforeUpload'] = (file) => {
|
||||||
|
const allowedTypes = ['application/pdf', 'application/epub+zip']
|
||||||
|
const isAllowedType = allowedTypes.includes(file.type)
|
||||||
|
const isLt50M = file.size / 1024 / 1024 < 50
|
||||||
|
|
||||||
|
if (!isAllowedType) {
|
||||||
|
ElMessage.error('只能上传PDF或EPUB文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!isLt50M) {
|
||||||
|
ElMessage.error('文件大小不能超过50MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 提交表单
|
||||||
|
const submitForm = () => {
|
||||||
|
formRef.value?.validate(async (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
ElMessage.warning('请填写完整的表单信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
// 这里应该是实际的API调用
|
||||||
|
const response = await frontRequest.post('/api/book', formData)
|
||||||
|
// 模拟API调用
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1500))
|
||||||
|
ElMessage.success('版权信息提交成功')
|
||||||
|
resetForm()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交失败:', error)
|
||||||
|
ElMessage.error('提交失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formRef.value?.resetFields()
|
ElMessageBox.confirm('确定要重置表单吗?', '提示', {
|
||||||
}
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
const handleImageUrl = (url: string) => {
|
type: 'warning'
|
||||||
state.formData.image = url
|
}).then(() => {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
fileList.value = []
|
||||||
|
formData.image = ''
|
||||||
|
formData.file = ''
|
||||||
|
ElMessage.success('表单已重置')
|
||||||
|
}).catch(() => {
|
||||||
|
// 取消操作
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.house-upload-container {
|
.copyright-upload-container {
|
||||||
max-width: 1000px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 30px 20px;
|
padding: 30px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||||
@ -147,22 +402,34 @@ const handleImageUrl = (url: string) => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.house-upload-form {
|
.copyright-upload-form {
|
||||||
.form-section {
|
.form-section {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f9fafc;
|
background-color: #f9fafc;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
border-left: 4px solid #409EFF;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
color: #409EFF;
|
color: #409EFF;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 1px solid #e6e6e6;
|
.el-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-grid {
|
.form-grid {
|
||||||
@ -171,74 +438,87 @@ const handleImageUrl = (url: string) => {
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
:deep(.el-form-item) {
|
:deep(.el-form-item) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-textarea {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-tip {
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-tip {
|
.input-tip {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-uploader {
|
.price-unit {
|
||||||
width: 100%;
|
margin-left: 8px;
|
||||||
max-width: 400px;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-upload-item {
|
||||||
|
:deep(.el-upload) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload-dragger) {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-top: 30px;
|
margin-top: 40px;
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
padding: 12px 30px;
|
padding: 12px 36px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset-btn {
|
.reset-btn {
|
||||||
padding: 12px 30px;
|
padding: 12px 36px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (max-width: 768px) {
|
|
||||||
.house-upload-container {
|
|
||||||
padding: 20px 15px;
|
|
||||||
|
|
||||||
.house-upload-form {
|
@media (max-width: 768px) {
|
||||||
|
.copyright-upload-container {
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.copyright-upload-form {
|
||||||
|
.form-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.form-section {
|
.form-section {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-grid {
|
.form-actions {
|
||||||
grid-template-columns: 1fr;
|
flex-direction: column;
|
||||||
}
|
gap: 10px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
"layout": "front"
|
"layout": "front",
|
||||||
|
"requiresAuth": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
|
@ -7,8 +7,8 @@ export const getAdminList = () => {
|
|||||||
"icon": "HomeFilled" // 首页
|
"icon": "HomeFilled" // 首页
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/admin/house",
|
"path": "/admin/book",
|
||||||
"name": "房源管理",
|
"name": "版权管理",
|
||||||
"icon": "Discount" // 电影管理
|
"icon": "Discount" // 电影管理
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -34,33 +34,23 @@ export const getFrontList = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/list",
|
"path": "/list",
|
||||||
"name": "供应房源",
|
"name": "版权展示",
|
||||||
"icon": "House",
|
"icon": "House",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/upload",
|
||||||
|
"name": "发布版权",
|
||||||
|
"icon": "Trophy"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"path": "/upload",
|
"path": "/push",
|
||||||
"name": "发布房源",
|
"name": "版权管理",
|
||||||
"icon": "Trophy"
|
"icon": "Trophy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/topic",
|
"path": "/topic",
|
||||||
"name": "房源溯源",
|
"name": "版权溯源",
|
||||||
"icon": "Trophy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/order",
|
|
||||||
"name": "我的换住",
|
|
||||||
"icon": "Trophy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/push",
|
|
||||||
"name": "我的发布",
|
|
||||||
"icon": "Trophy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/collect",
|
|
||||||
"name": "我的收藏",
|
|
||||||
"icon": "Trophy"
|
"icon": "Trophy"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -86,12 +76,4 @@ export const getDecoration = () => {
|
|||||||
const routes =["精装", "简装", "毛坯"]
|
const routes =["精装", "简装", "毛坯"]
|
||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
export const getHouseStatus = () => {
|
|
||||||
const routes =[
|
|
||||||
{name:"未审核",status:0},
|
|
||||||
{name:"上架",status:1},
|
|
||||||
{name:"下架",status:2},
|
|
||||||
{name:"审核失败",status:3},
|
|
||||||
]
|
|
||||||
return routes;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user