add
This commit is contained in:
parent
3ecec5fd77
commit
61fb85befa
@ -1,6 +1,4 @@
|
||||
package io.modules.sys.controller;
|
||||
|
||||
|
||||
import io.common.annotation.LogOperation;
|
||||
import io.common.constant.Constant;
|
||||
import io.common.page.PageData;
|
||||
@ -22,20 +20,11 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 吐槽墙
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2024-12-12
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("item/wall")
|
||||
@Tag(name="吐槽墙")
|
||||
public class WallController {
|
||||
@RequestMapping("sys/book")
|
||||
public class BookController {
|
||||
@Autowired
|
||||
private BookService wallService;
|
||||
private BookService bookService;
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@ -45,58 +34,45 @@ public class WallController {
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
})
|
||||
@RequiresPermissions("item:wall:page")
|
||||
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);
|
||||
}
|
||||
|
||||
@GetMapping("{id}")
|
||||
@Operation(summary = "信息")
|
||||
@RequiresPermissions("item:wall:info")
|
||||
public Result<BookDTO> get(@PathVariable("id") Long id){
|
||||
BookDTO data = wallService.get(id);
|
||||
|
||||
BookDTO data = bookService.get(id);
|
||||
return new Result<BookDTO>().ok(data);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "保存")
|
||||
@LogOperation("保存")
|
||||
@RequiresPermissions("item:wall:save")
|
||||
public Result save(@RequestBody BookDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||
|
||||
wallService.save(dto);
|
||||
|
||||
bookService.save(dto);
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "修改")
|
||||
@LogOperation("修改")
|
||||
@RequiresPermissions("item:wall:update")
|
||||
public Result update(@RequestBody BookDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
|
||||
wallService.update(dto);
|
||||
|
||||
bookService.update(dto);
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
@RequiresPermissions("item:wall:delete")
|
||||
public Result delete(@RequestBody Long[] ids){
|
||||
//效验数据
|
||||
AssertUtils.isArrayEmpty(ids, "id");
|
||||
|
||||
wallService.delete(ids);
|
||||
|
||||
bookService.delete(ids);
|
||||
return new Result();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ spring:
|
||||
druid:
|
||||
#MySQL
|
||||
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
|
||||
password: 123456
|
||||
initial-size: 10
|
||||
|
@ -12,6 +12,6 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
* @since 1.0.0 2024-12-12
|
||||
*/
|
||||
@Mapper
|
||||
public interface WallDao extends BaseDao<BookEntity> {
|
||||
public interface BookDao extends BaseDao<BookEntity> {
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
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.SchemaProperty;
|
||||
import lombok.Data;
|
||||
@ -17,20 +18,24 @@ public class BookDTO implements Serializable {
|
||||
|
||||
private Long id; // 主键ID
|
||||
private Long userId; // 主键ID
|
||||
private String img; // 封面
|
||||
private String image; // 封面
|
||||
private String isbn; // ISBN编号
|
||||
private String title; // 图书标题
|
||||
private String author; // 作者
|
||||
private String publisher; // 出版社
|
||||
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
|
||||
private Date publishDate; // 出版日期
|
||||
private String copyrightOwner; // 版权持有人
|
||||
private Integer copyrightStartYear; // 版权起始年份
|
||||
private Integer copyrightEndYear; // 版权到期年份
|
||||
|
||||
private String copyrightStartYear; // 版权起始年份
|
||||
private String copyrightEndYear; // 版权到期年份
|
||||
private String edition; // 版次
|
||||
private String language; // 语言
|
||||
private BigDecimal price; // 图书定价
|
||||
private String hex; // 上链哈希值
|
||||
private Date createTime; // 创建时间
|
||||
private String file; // 电子数据文件地址
|
||||
private String status;
|
||||
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ public class UserDTO implements Serializable {
|
||||
@SchemaProperty(name = "昵称")
|
||||
private String nickName;
|
||||
|
||||
@SchemaProperty(name = "介绍")
|
||||
private String introduce;
|
||||
@SchemaProperty(name = "手机号")
|
||||
private String phone;
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.modules.item.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -12,19 +13,22 @@ public class BookEntity {
|
||||
|
||||
private Long id; // 主键ID
|
||||
private Long userId;
|
||||
private String img; // 封面
|
||||
private String image; // 封面
|
||||
private String isbn; // ISBN编号
|
||||
private String title; // 图书标题
|
||||
private String author; // 作者
|
||||
private String publisher; // 出版社
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
|
||||
private Date publishDate; // 出版日期
|
||||
private String copyrightOwner; // 版权持有人
|
||||
private Integer copyrightStartYear; // 版权起始年份
|
||||
private Integer copyrightEndYear; // 版权到期年份
|
||||
|
||||
private String copyrightStartYear; // 版权起始年份
|
||||
private String copyrightEndYear; // 版权到期年份
|
||||
private String edition; // 版次
|
||||
private String language; // 语言
|
||||
private BigDecimal price; // 图书定价
|
||||
private String hex; // 上链哈希值
|
||||
private Date createTime; // 创建时间
|
||||
private String file; // 电子数据文件地址
|
||||
private String status; // 电子数据文件地址
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class FrontUserEntity {
|
||||
*/
|
||||
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 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.entity.BookEntity;
|
||||
import io.modules.item.service.BookService;
|
||||
@ -14,19 +14,20 @@ import java.util.Map;
|
||||
|
||||
|
||||
@Service
|
||||
public class BookServiceImpl extends CrudServiceImpl<WallDao, BookEntity, BookDTO> implements BookService {
|
||||
public class BookServiceImpl extends CrudServiceImpl<BookDao, BookEntity, BookDTO> implements BookService {
|
||||
|
||||
@Override
|
||||
public QueryWrapper<BookEntity> getWrapper(Map<String, Object> params){
|
||||
String id = (String)params.get("id");
|
||||
String userId = (String)params.get("userId");
|
||||
|
||||
String title = (String)params.get("title");
|
||||
String status = (String)params.get("status");
|
||||
QueryWrapper<BookEntity> wrapper = new QueryWrapper<>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
package io.controller;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import io.annotation.Login;
|
||||
import io.annotation.LoginUser;
|
||||
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.DefaultGroup;
|
||||
import io.entity.UserEntity;
|
||||
import io.modules.item.dao.BookDao;
|
||||
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.BookService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -29,6 +33,9 @@ public class BookController {
|
||||
@Autowired
|
||||
private BookService bookService;
|
||||
|
||||
@Autowired
|
||||
private BookDao bookDao;
|
||||
|
||||
@Login
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@ -49,5 +56,26 @@ public class BookController {
|
||||
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.setUsername(dto.getUsername());
|
||||
user.setNickName(dto.getNickName());
|
||||
user.setIntroduce(dto.getIntroduce());
|
||||
user.setPhone(dto.getPhone());
|
||||
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
|
||||
userService.updateById(user);
|
||||
return new Result();
|
||||
|
@ -30,6 +30,6 @@ public class RegisterDTO {
|
||||
@Schema(title = "昵称")
|
||||
private String nickName;
|
||||
|
||||
@Schema(title = "介绍")
|
||||
private String introduce;
|
||||
@Schema(title = "手机号")
|
||||
private String phone;
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ public class UserEntity implements Serializable {
|
||||
*/
|
||||
private String nickName;
|
||||
/**
|
||||
* 介绍
|
||||
* 手机号
|
||||
*/
|
||||
private String introduce;
|
||||
private String phone;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
|
@ -6,7 +6,7 @@ spring:
|
||||
datasource:
|
||||
druid:
|
||||
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
|
||||
password: 123456
|
||||
initial-size: 10
|
||||
@ -33,7 +33,7 @@ spring:
|
||||
multi-statement-allow: true
|
||||
web:
|
||||
resources:
|
||||
static-locations: "file:D:/202505/block-chaincopyright/upload/"
|
||||
static-locations: "file:D:/20250519/block-chaincopyright/upload/"
|
||||
upload:
|
||||
path: D:\202505\block-chaincopyright\upload
|
||||
path: D:\20250519\block-chaincopyright\upload
|
||||
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>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click="to('/user')">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item @click="to('/collect')">我的收藏</el-dropdown-item>
|
||||
<el-dropdown-item @click="to('/order')">我的换住</el-dropdown-item>
|
||||
<el-dropdown-item @click="to('/upload')">发布版权</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-menu>
|
||||
</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:[
|
||||
{name: '个人中心', path: '/user'},
|
||||
{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">
|
||||
<el-row :gutter="20">
|
||||
<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">
|
||||
<el-icon :size="30" color="#409EFF"><House /></el-icon>
|
||||
<span>房源管理</span>
|
||||
<span>版权管理</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</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-card shadow="hover" class="quick-card" @click="navigateTo('house')">
|
||||
<el-card shadow="hover" class="quick-card" @click="navigateTo('slides')">
|
||||
<div class="quick-content">
|
||||
<el-icon :size="30" color="#E6A23C"><DocumentChecked /></el-icon>
|
||||
<span>认证管理</span>
|
||||
<span>轮播</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
@ -40,62 +33,12 @@
|
||||
|
||||
<!-- 数据概览 -->
|
||||
<el-row :gutter="20" class="mt-20">
|
||||
<el-col :span="12">
|
||||
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<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>
|
||||
<span>发布趋势</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="chart" style="height: 220px;"></div>
|
||||
@ -103,67 +46,6 @@
|
||||
</el-col>
|
||||
</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>
|
||||
</template>
|
||||
@ -181,59 +63,6 @@ import {
|
||||
|
||||
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([
|
||||
@ -269,13 +98,6 @@ const pendingCertifications = ref([
|
||||
}
|
||||
])
|
||||
|
||||
// 区块链信息
|
||||
const blockchainInfo = ref({
|
||||
height: 124567,
|
||||
nodes: 12,
|
||||
pendingTxs: 8,
|
||||
status: '运行正常'
|
||||
})
|
||||
|
||||
// 图表引用
|
||||
const chart = ref(null)
|
||||
@ -292,7 +114,7 @@ onMounted(() => {
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['换住交易', '认证交易']
|
||||
data: ['用户量', '发布量']
|
||||
},
|
||||
grid: {
|
||||
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-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="id"/>
|
||||
|
||||
<el-table-column prop="username" label="账号"/>
|
||||
<el-table-column prop="createDate" 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" >
|
||||
<template #default="scope">
|
||||
@ -65,8 +65,8 @@
|
||||
<el-form-item label="昵称" prop="nickName" :rules="[{ required: true, message: '请输入昵称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.nickName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="介绍" prop="introduce" :rules="[{ required: true, message: '请输入介绍', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.introduce"/>
|
||||
<el-form-item label="手机号" prop="phone" :rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.phone"/>
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</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>
|
||||
<!--轮播图-->
|
||||
<el-row :gutter="20">
|
||||
<div class="blockchain-library-container">
|
||||
|
||||
<el-col :span="24">
|
||||
<div class="h-300px">
|
||||
<carousel></carousel>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="padding-top: 20px"></div>
|
||||
<!-- 推荐房源列表-->
|
||||
<item :get-list=state.getList></item>
|
||||
<!-- 主要内容区 -->
|
||||
<main class="library-main">
|
||||
<!-- 轮播图区域 -->
|
||||
<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>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Carousel from '~/components/front/carousel.vue'
|
||||
import Item from '~/components/front/item.vue'
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const state = reactive({
|
||||
getList: [],
|
||||
commit: [],
|
||||
import {
|
||||
ArrowRight,
|
||||
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) => {
|
||||
router.push(`/info/${id}`)
|
||||
function init() {
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
.hot {
|
||||
background-color: rgba(0, 0, 0, 0.2); /* 更浅的黑色 */
|
||||
border-radius: 10px; /* 四周圆弧,值可以根据需要调整 */
|
||||
/* 全局样式 */
|
||||
.blockchain-library-container {
|
||||
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>
|
||||
<route lang="json">
|
||||
|
@ -3,18 +3,10 @@
|
||||
<!-- 头部区块信息 -->
|
||||
<div class="blockchain-header">
|
||||
<div class="nav-logo">
|
||||
<span class="logo-icon">⛓</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>
|
||||
<span class="logo-icon">📚</span>
|
||||
<span class="logo-text">区块链版权</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容区 -->
|
||||
<div class="detail-content">
|
||||
<el-row :gutter="30">
|
||||
@ -32,32 +24,52 @@
|
||||
<span class="badge-text">链上验证</span>
|
||||
</div>
|
||||
<div class="image-tags">
|
||||
<el-tag v-if="state.info.tag" type="info" class="location-tag">
|
||||
<i class="el-icon-location-information"></i>
|
||||
{{ state.info.tag }}
|
||||
<el-tag v-if="state.info.language" type="info" class="language-tag">
|
||||
<i class="el-icon-chat-line-round"></i>
|
||||
{{ state.info.language }}
|
||||
</el-tag>
|
||||
<el-tag v-if="state.info.type" type="success" class="type-tag">
|
||||
{{ state.info.type }}
|
||||
<el-tag v-if="state.info.status" :type="getStatusTagType(state.info.status)" class="status-tag">
|
||||
{{ state.info.status }}
|
||||
</el-tag>
|
||||
</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 :xs="24" :sm="24" :md="12" :lg="14">
|
||||
<!-- 信息展示区 -->
|
||||
<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="meta-item">
|
||||
<span class="meta-label">房源地址:</span>
|
||||
<span class="meta-value">{{ state.info.city || '暂无' }}</span>
|
||||
<span class="meta-label">ISBN编号:</span>
|
||||
<span class="meta-value">{{ state.info.isbn || '暂无' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="price-section">
|
||||
<span class="price-label">价格:</span>
|
||||
<span class="price-value">{{ state.info.price || '0' }}</span>
|
||||
<span class="price-unit">元/平方</span>
|
||||
<span class="price-label">定价:</span>
|
||||
<span class="price-value">{{ state.info.price || '0' }}</span>
|
||||
<span class="price-unit">元</span>
|
||||
<el-tag type="success" size="small" class="blockchain-tag">
|
||||
<span class="tag-icon">⛓</span>
|
||||
<span>链上价格</span>
|
||||
@ -66,25 +78,31 @@
|
||||
|
||||
<div class="verification-badge">
|
||||
<el-icon><SuccessFilled /></el-icon>
|
||||
<span>房源信息已通过区块链验证</span>
|
||||
<span>版权信息已通过区块链验证</span>
|
||||
</div>
|
||||
|
||||
<div class="property-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">小区名称:</span>
|
||||
<span class="detail-value">{{ state.info.name || '暂无' }}</span>
|
||||
<span class="detail-label">出版社:</span>
|
||||
<span class="detail-value">{{ state.info.publisher || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">朝向:</span>
|
||||
<span class="detail-value">{{ state.info.face || '暂无' }}</span>
|
||||
<span class="detail-label">出版日期:</span>
|
||||
<span class="detail-value">{{ formatDate(state.info.publishDate) || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">面积:</span>
|
||||
<span class="detail-value">{{ state.info.area || '0' }} 平方</span>
|
||||
<span class="detail-label">版权持有人:</span>
|
||||
<span class="detail-value">{{ state.info.copyrightOwner || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">楼层:</span>
|
||||
<span class="detail-value">{{ state.info.floor || '0' }}层</span>
|
||||
<span class="detail-label">版权期限:</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>
|
||||
|
||||
@ -94,9 +112,9 @@
|
||||
<el-icon><Connection /></el-icon>
|
||||
<span>区块链编号</span>
|
||||
</div>
|
||||
<el-tooltip :content="state.info.hex || '暂无'" placement="top">
|
||||
<el-tooltip :content="state.info.hex || '暂无'" placement="top">
|
||||
<div class="hash-value">
|
||||
{{ shortenHash(state.info.hex) || '暂无区块链信息' }}
|
||||
{{ shortenHash(state.info.hex) || '暂无区块链信息' }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-button
|
||||
@ -105,123 +123,17 @@
|
||||
class="view-transaction"
|
||||
v-if="state.info.hex"
|
||||
>
|
||||
查看交易记录 <el-icon><ArrowRight /></el-icon>
|
||||
|
||||
</el-button>
|
||||
</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>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
@ -231,11 +143,12 @@ import { useRoute } from 'vue-router'
|
||||
import {
|
||||
SuccessFilled,
|
||||
ShoppingCart,
|
||||
Goods,
|
||||
|
||||
Download,
|
||||
Document,
|
||||
Notebook,
|
||||
User,
|
||||
Connection,
|
||||
ArrowRight,
|
||||
OfficeBuilding,
|
||||
Coin,
|
||||
Money,
|
||||
Checked
|
||||
@ -256,31 +169,58 @@ const state = reactive(<any>{
|
||||
price: 0,
|
||||
totalAmount: 0
|
||||
},
|
||||
cateList: [],
|
||||
userInfo:{},
|
||||
houseList:[]
|
||||
userInfo: {}
|
||||
})
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
*/
|
||||
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) {
|
||||
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() {
|
||||
frontRequest.get(`/api/item/${itemId}`).then(response => {
|
||||
state.info = response.data
|
||||
})
|
||||
frontRequest.get(`api/address/page`).then(response => {
|
||||
state.cateList = response.data.list
|
||||
frontRequest.get(`/api/book/${itemId}`).then(response => {
|
||||
state.info = response.data
|
||||
state.buyForm.title = state.info.title
|
||||
state.buyForm.price = state.info.price
|
||||
updateTotalAmount()
|
||||
})
|
||||
|
||||
frontRequest.get("/api/item/push").then(res =>{
|
||||
state.houseList = res.data
|
||||
|
||||
frontRequest.get("/api/user/userInfo").then(res => {
|
||||
state.userInfo = res.data
|
||||
})
|
||||
}
|
||||
|
||||
@ -288,8 +228,8 @@ function init() {
|
||||
* 用户收藏
|
||||
*/
|
||||
function handleFavorite() {
|
||||
if (state.info.isFavorite) {
|
||||
frontRequest.post("/api/behavior/delete", {itemId: itemId, type: 1}).then(res => {
|
||||
if (state.info.isFavorite) {
|
||||
frontRequest.post("/api/behavior/delete", {itemId: itemId, type: 1}).then(res => {
|
||||
ElMessage.success({
|
||||
message: '已取消收藏',
|
||||
offset: 80
|
||||
@ -297,7 +237,7 @@ function handleFavorite() {
|
||||
init()
|
||||
})
|
||||
} else {
|
||||
frontRequest.post("/api/behavior", {itemId: itemId, type: 1}).then(res => {
|
||||
frontRequest.post("/api/behavior", {itemId: itemId, type: 1}).then(res => {
|
||||
ElMessage.success({
|
||||
message: '收藏成功',
|
||||
offset: 80
|
||||
@ -308,47 +248,44 @@ function handleFavorite() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 购买按钮提交
|
||||
* 下载电子文件
|
||||
*/
|
||||
const buySubmitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
frontRequest.post("/api/order",{
|
||||
itemId:itemId, //房源编号
|
||||
userHouseId:state.buyForm.userHouseId, //换住房源编号
|
||||
}).then(() => {
|
||||
ElMessage.success({
|
||||
message: '换住信息已提交区块链网络',
|
||||
offset: 80
|
||||
})
|
||||
buyDrawer.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
function handleDownload() {
|
||||
if (!state.info.file) {
|
||||
ElMessage.warning(' 该图书暂无电子文件')
|
||||
return
|
||||
}
|
||||
window.open(state.info.file, '_blank')
|
||||
}
|
||||
|
||||
/**
|
||||
* 购买按钮
|
||||
*/
|
||||
const handleBuy = () => {
|
||||
//判断是否是自己房源
|
||||
frontRequest.get("/api/user/userInfo").then(res =>{
|
||||
if (state.info.userId === res.data.id){
|
||||
ElMessage.warning("自己房源不能换住")
|
||||
return
|
||||
}
|
||||
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()
|
||||
})
|
||||
function handlePurchase() {
|
||||
if (state.info.userId === state.userInfo.id) {
|
||||
ElMessage.warning(" 不能购买自己的图书版权")
|
||||
return
|
||||
}
|
||||
buyDrawer.value = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 购买按钮提交
|
||||
*/
|
||||
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(() => {
|
||||
init()
|
||||
@ -384,7 +321,7 @@ onMounted(() => {
|
||||
.logo-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 24px;
|
||||
color: #1abc9c;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.blockchain-breadcrumb {
|
||||
@ -405,6 +342,7 @@ onMounted(() => {
|
||||
height: 400px;
|
||||
display: block;
|
||||
transition: all 0.3s;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.main-image:hover {
|
||||
@ -415,7 +353,7 @@ onMounted(() => {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background-color: rgba(26, 188, 156, 0.9);
|
||||
background-color: rgba(52, 152, 219, 0.9);
|
||||
color: white;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
@ -439,18 +377,43 @@ onMounted(() => {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.location-tag {
|
||||
background-color: rgba(52, 152, 219, 0.9);
|
||||
.language-tag {
|
||||
background-color: rgba(41, 128, 185, 0.9);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.type-tag {
|
||||
background-color: rgba(46, 204, 113, 0.9);
|
||||
.status-tag {
|
||||
background-color: rgba(39, 174, 96, 0.9);
|
||||
color: white;
|
||||
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 {
|
||||
padding: 0 15px;
|
||||
@ -460,10 +423,22 @@ onMounted(() => {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
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 {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
@ -478,7 +453,7 @@ onMounted(() => {
|
||||
.meta-label {
|
||||
color: #7f8c8d;
|
||||
margin-right: 10px;
|
||||
min-width: 80px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
@ -556,7 +531,7 @@ onMounted(() => {
|
||||
.detail-label {
|
||||
color: #7f8c8d;
|
||||
margin-right: 8px;
|
||||
min-width: 50px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
@ -570,7 +545,7 @@ onMounted(() => {
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 30px;
|
||||
border-left: 4px solid #1abc9c;
|
||||
border-left: 4px solid #3498db;
|
||||
}
|
||||
|
||||
.hash-title {
|
||||
@ -583,7 +558,7 @@ onMounted(() => {
|
||||
|
||||
.hash-title .el-icon {
|
||||
margin-right: 8px;
|
||||
color: #1abc9c;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.hash-value {
|
||||
@ -604,7 +579,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.view-transaction {
|
||||
color: #1abc9c !important;
|
||||
color: #3498db !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -621,7 +596,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.buy-button {
|
||||
background-color: #e74c3c;
|
||||
background-color: #3498db;
|
||||
border: none;
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
@ -631,32 +606,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.buy-button:hover {
|
||||
background-color: #c0392b;
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -689,6 +645,12 @@ onMounted(() => {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
background-color: #3498db;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.confirm-button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.confirm-button .el-icon {
|
||||
@ -704,7 +666,6 @@ onMounted(() => {
|
||||
flex-direction: column;
|
||||
}
|
||||
.buy-button,
|
||||
.cart-button,
|
||||
.favorite-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<el-col :span="24" class="search-container">
|
||||
<el-input
|
||||
v-model="state.page.title"
|
||||
placeholder="搜索房源信息"
|
||||
placeholder="搜索版权信息"
|
||||
class="search-input"
|
||||
clearable
|
||||
@input="init"
|
||||
@ -16,24 +16,6 @@
|
||||
</el-input>
|
||||
</el-col>
|
||||
</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-col :span="24">
|
||||
@ -51,9 +33,9 @@
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<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">
|
||||
<span class="current-price">{{ item.price }}元/平方</span>
|
||||
<span class="current-price">{{ item.publisher }}</span>
|
||||
<el-tag type="success" size="small">区块溯源</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,50 +66,23 @@ const state = reactive(<any>{
|
||||
page: {
|
||||
page: 1,
|
||||
title: '',
|
||||
limit:12
|
||||
limit:8
|
||||
},
|
||||
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
|
||||
}],
|
||||
getList: [],
|
||||
brands:[]
|
||||
})
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
function init() {
|
||||
frontRequest.get("/api/item/page",{
|
||||
frontRequest.get("/api/book/page",{
|
||||
params: 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
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 分页
|
||||
@ -136,7 +91,6 @@ const handleCurrentChange = (val: number) => {
|
||||
state.page.page = val
|
||||
init()
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
@ -162,25 +116,7 @@ onMounted(() => {
|
||||
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;
|
||||
@ -261,7 +197,6 @@ onMounted(() => {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
|
@ -49,7 +49,7 @@
|
||||
class="order-table"
|
||||
>
|
||||
<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 label="申请人房源图片" >
|
||||
<template #default="{ row }">
|
||||
|
@ -1,107 +1,304 @@
|
||||
<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-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 />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择状态">
|
||||
<el-select
|
||||
style="width: 120px"
|
||||
v-model="state.query.status"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
<el-tabs v-model="state.query.status" @tab-click="handleClick">
|
||||
<el-tab-pane v-for="status in state.status" :label="status.name" :name="status.status">
|
||||
<el-table
|
||||
v-loading="state.loading"
|
||||
:data="state.list"
|
||||
stripe
|
||||
border
|
||||
class="data-table"
|
||||
empty-text="暂无数据"
|
||||
>
|
||||
<el-option label="未审核" value="未审核" />
|
||||
<el-option label="已同意" value="已同意" />
|
||||
<el-option label="已拒绝 " value="已拒绝 " />
|
||||
<el-option label="已评价 " value="已评价 " />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="init()">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table-column label="封面图" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.image" fit="cover" class="book-image" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isbn" label="ISBN" align="center" />
|
||||
<el-table-column prop="title" label="书名" align="center" />
|
||||
<el-table-column prop="author" label="作者" align="center" />
|
||||
<el-table-column prop="publisher" label="出版社" align="center" />
|
||||
<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-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<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>
|
||||
<!-- 分页器 -->
|
||||
<el-pagination
|
||||
class="pagination"
|
||||
background
|
||||
layout="prev, pager, next, jumper, ->, total"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.total"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</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,
|
||||
list: [],
|
||||
// 搜索
|
||||
total: 0,
|
||||
query: {
|
||||
title: '',
|
||||
status: null,
|
||||
page: 1,
|
||||
limit: 5,
|
||||
},
|
||||
// 标签页相关
|
||||
activeTab: 'all',
|
||||
// 详情相关
|
||||
detailVisible: false,
|
||||
currentBook: {},
|
||||
// 凭证相关
|
||||
certificateVisible: false,
|
||||
currentCertificate: ''
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
function init() {
|
||||
state.loading = true
|
||||
frontRequest.get("/api/item/push",{params:state.query}).then(res =>{
|
||||
state.list = res.data
|
||||
frontRequest.get('/api/book/page', { params: state.query }).then((res) => {
|
||||
state.list = res.data.list
|
||||
state.total = res.data.total
|
||||
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()
|
||||
})
|
||||
|
||||
</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>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
|
@ -2,10 +2,10 @@
|
||||
<div class="item-trace-container">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="trace-header">
|
||||
<h1 class="title">换住源溯源系统</h1>
|
||||
<h1 class="title">版权源溯源系统</h1>
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="请输入换住区块编号"
|
||||
placeholder="请输入版权区块编号"
|
||||
class="search-input"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
@ -16,230 +16,151 @@
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20" >
|
||||
<el-col :span="12" v-if="!isEmpty(state.userA)">
|
||||
<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.userA.nickName }}</el-descriptions-item
|
||||
>
|
||||
<el-descriptions-item label="手机号" label-align="right" align="center">{{ state.userA.introduce }}</el-descriptions-item>
|
||||
<!-- 版权详情展示 -->
|
||||
<div v-if="state.bookData.hex" class="copyright-detail">
|
||||
<el-card shadow="hover" class="detail-card">
|
||||
<div class="info-header">
|
||||
<h2>图书版权信息</h2>
|
||||
<el-tag :type="getStatusTagType(state.bookData.status)" size="large">
|
||||
{{ state.bookData.status || '未知状态' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
</el-descriptions>
|
||||
|
||||
</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">
|
||||
<!-- 信息展示区 -->
|
||||
<el-divider />
|
||||
<div class="info-grid">
|
||||
<div class="info-section">
|
||||
<h1 class="property-title">{{ state.houseA.title}}</h1>
|
||||
|
||||
<div class="property-meta">
|
||||
<div class="meta-item">
|
||||
<span class="meta-label">房源地址:</span>
|
||||
<span class="meta-value">{{ state.houseA.city || '暂无' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="price-section">
|
||||
<span class="price-label">价格:</span>
|
||||
<span class="price-value">{{ state.houseA.price || '0' }}</span>
|
||||
<span class="price-unit">元/平方</span>
|
||||
<el-tag type="success" size="small" class="blockchain-tag">
|
||||
<span class="tag-icon">⛓</span>
|
||||
<span>链上价格</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<h3>图书基本信息</h3>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="封面">
|
||||
<el-image
|
||||
:src="state.bookData.image || 'https://via.placeholder.com/150x200?text=No+Cover'"
|
||||
style="width: 100px; height: 140px;"
|
||||
fit="cover"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="ISBN编号">{{ state.bookData.isbn || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="图书标题">{{ state.bookData.title || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="作者">{{ state.bookData.author || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出版社">{{ state.bookData.publisher || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出版日期">{{state.bookData.publishDate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版次">{{ state.bookData.edition || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="语言">{{ state.bookData.language || '中文' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="定价">{{ state.bookData.price ? `${state.bookData.price}元` : '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</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">
|
||||
<h1 class="property-title">{{ state.houseB.title}}</h1>
|
||||
|
||||
<div class="property-meta">
|
||||
<div class="meta-item">
|
||||
<span class="meta-label">房源地址:</span>
|
||||
<span class="meta-value">{{ state.houseB.city || '暂无' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="price-section">
|
||||
<span class="price-label">价格:</span>
|
||||
<span class="price-value">{{ state.houseB.price || '0' }}</span>
|
||||
<span class="price-unit">元/平方</span>
|
||||
<el-tag type="success" size="small" class="blockchain-tag">
|
||||
<span class="tag-icon">⛓</span>
|
||||
<span>链上价格</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<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.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>
|
||||
|
||||
<h3>版权信息</h3>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="版权区块编号">{{ state.bookData.hex || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版权持有人">{{ state.bookData.copyrightOwner || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版权起始年份">{{ state.bookData.copyrightStartYear || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版权到期年份">{{ state.bookData.copyrightEndYear || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="上链时间">{{state.bookData.createTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电子文件">
|
||||
<el-button
|
||||
v-if="state.bookData.file"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="downloadFile(state.bookData.file)"
|
||||
>
|
||||
下载文件
|
||||
</el-button>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</div>
|
||||
|
||||
</el-row>
|
||||
<el-divider />
|
||||
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else class="empty-state">
|
||||
<el-empty description="请输入版权区块编号查询详情" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ArrowRight, Connection, Search, SuccessFilled } from '@element-plus/icons-vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
|
||||
// 搜索查询
|
||||
const searchQuery = ref('')
|
||||
|
||||
function isEmpty(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
const state = reactive(<any>{
|
||||
dto:{},
|
||||
houseA:{},
|
||||
houseB:{},
|
||||
userA:{},
|
||||
userB:{},
|
||||
const state = reactive({
|
||||
bookData: {} as any
|
||||
})
|
||||
|
||||
// 搜索房源
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case '已通过':
|
||||
return 'success'
|
||||
case '待审核':
|
||||
return 'warning'
|
||||
case '已拒绝':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索版权信息
|
||||
const handleSearch = async () => {
|
||||
if (!searchQuery.value.trim()) {
|
||||
ElMessage.warning('请输入查询内容')
|
||||
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('查询成功')
|
||||
state.dto = response.data.dto
|
||||
state.houseA = response.data.houseA
|
||||
state.houseB = response.data.houseB
|
||||
state.userA = response.data.userA
|
||||
state.userB = response.data.userB
|
||||
})
|
||||
state.bookData = response.data || {}
|
||||
} catch (error) {
|
||||
ElMessage.error('查询失败,请检查区块编号是否正确')
|
||||
console.error('查询失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
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>
|
||||
|
||||
@ -265,11 +186,34 @@ const handleSearch = async () => {
|
||||
.search-input {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.copyright-detail {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.info-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.info-header h2 {
|
||||
margin-right: 15px;
|
||||
font-size: 22px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info-section h3 {
|
||||
color: #666;
|
||||
font-size: 18px;
|
||||
@ -278,363 +222,27 @@ const handleSearch = async () => {
|
||||
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 {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 30px;
|
||||
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;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.confirm-button .el-icon {
|
||||
margin-right: 8px;
|
||||
.empty-state {
|
||||
margin-top: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.property-details {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
.buy-button,
|
||||
.cart-button,
|
||||
.favorite-button {
|
||||
width: 100%;
|
||||
}
|
||||
:deep(.el-descriptions__body) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 120px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
|
@ -1,142 +1,397 @@
|
||||
<template>
|
||||
<div class="house-upload-container">
|
||||
<h2 class="upload-title">上传房屋信息</h2>
|
||||
<div class="copyright-upload-container">
|
||||
<h2 class="upload-title">图书版权信息登记</h2>
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
class="house-upload-form"
|
||||
class="copyright-upload-form"
|
||||
:rules="rules"
|
||||
>
|
||||
<!-- 基本信息部分 -->
|
||||
<div class="form-section">
|
||||
<h3 class="section-title">基本信息</h3>
|
||||
<h3 class="section-title">
|
||||
<el-icon><Document /></el-icon>
|
||||
基本信息
|
||||
</h3>
|
||||
<div class="form-grid">
|
||||
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]">
|
||||
<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-form-item label="图书标题" prop="title">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="state.formData.description"
|
||||
rows="5"
|
||||
placeholder="请输入房屋详细描述"
|
||||
class="form-textarea"
|
||||
v-model="formData.title"
|
||||
placeholder="请输入图书标题"
|
||||
clearable
|
||||
/>
|
||||
</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>
|
||||
</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">
|
||||
<el-button type="primary" @click="saveTransaction" class="submit-btn">保存并发布</el-button>
|
||||
<el-button @click="resetForm" class="reset-btn">重置</el-button>
|
||||
<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>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDecoration, getFace, getHouseType } from '~/utils/utils'
|
||||
import { reactive, ref } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive(<any>{
|
||||
formData: {}
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules, UploadProps } from 'element-plus'
|
||||
import {
|
||||
Document,
|
||||
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 = () => {
|
||||
formRef.value?.validate(async valid => {
|
||||
if (!valid) return
|
||||
state.formData.status = 0
|
||||
frontRequest.post(`/api/item`, state.formData).then(res => {
|
||||
// 处理成功逻辑
|
||||
ElMessage.success("上传成功")
|
||||
})
|
||||
// 表单引用
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
title: '',
|
||||
isbn: '',
|
||||
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 = () => {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
const handleImageUrl = (url: string) => {
|
||||
state.formData.image = url
|
||||
ElMessageBox.confirm('确定要重置表单吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
formRef.value?.resetFields()
|
||||
fileList.value = []
|
||||
formData.image = ''
|
||||
formData.file = ''
|
||||
ElMessage.success('表单已重置')
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.house-upload-container {
|
||||
max-width: 1000px;
|
||||
.copyright-upload-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 30px 20px;
|
||||
padding: 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
@ -147,22 +402,34 @@ const handleImageUrl = (url: string) => {
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.house-upload-form {
|
||||
.copyright-upload-form {
|
||||
.form-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background-color: #f9fafc;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
color: #409EFF;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
@ -171,74 +438,87 @@ const handleImageUrl = (url: string) => {
|
||||
gap: 20px;
|
||||
|
||||
: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 {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.image-uploader {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
.price-unit {
|
||||
margin-left: 8px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.file-upload-item {
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-upload-dragger) {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 30px;
|
||||
margin-top: 40px;
|
||||
|
||||
.submit-btn {
|
||||
padding: 12px 30px;
|
||||
padding: 12px 36px;
|
||||
font-size: 16px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
padding: 12px 30px;
|
||||
padding: 12px 36px;
|
||||
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 {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
"layout": "front",
|
||||
"requiresAuth": true
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
@ -7,8 +7,8 @@ export const getAdminList = () => {
|
||||
"icon": "HomeFilled" // 首页
|
||||
},
|
||||
{
|
||||
"path": "/admin/house",
|
||||
"name": "房源管理",
|
||||
"path": "/admin/book",
|
||||
"name": "版权管理",
|
||||
"icon": "Discount" // 电影管理
|
||||
},
|
||||
|
||||
@ -34,33 +34,23 @@ export const getFrontList = () => {
|
||||
},
|
||||
{
|
||||
"path": "/list",
|
||||
"name": "供应房源",
|
||||
"name": "版权展示",
|
||||
"icon": "House",
|
||||
},
|
||||
{
|
||||
"path": "/upload",
|
||||
"name": "发布版权",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/upload",
|
||||
"name": "发布房源",
|
||||
"path": "/push",
|
||||
"name": "版权管理",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
{
|
||||
"path": "/topic",
|
||||
"name": "房源溯源",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
{
|
||||
"path": "/order",
|
||||
"name": "我的换住",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
{
|
||||
"path": "/push",
|
||||
"name": "我的发布",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
{
|
||||
"path": "/collect",
|
||||
"name": "我的收藏",
|
||||
"name": "版权溯源",
|
||||
"icon": "Trophy"
|
||||
},
|
||||
]
|
||||
@ -86,12 +76,4 @@ export const getDecoration = () => {
|
||||
const 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