15. 带你实现订单评价功能
评价表 SQL
CREATE TABLE `comment` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`score` double(10,1) DEFAULT NULL COMMENT '评分',
`content` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '评价内容',
`user_id` int DEFAULT NULL COMMENT '评价人',
`time` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '时间',
`order_id` int DEFAULT NULL COMMENT '订单ID',
`goods_id` int DEFAULT NULL COMMENT '商品ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单评价';
开发评价的后端接口
Comment.java
package com.example.entity;
public class Comment {
/**主键ID */
private Integer id;
/**评分 */
private Double score;
/**评价内容 */
private String content;
/**评价人 */
private Integer userId;
private String userName;
private String userAvatar;
/**时间 */
private String time;
/**订单ID */
private Integer orderId;
private String orderNo;
/**商品ID */
private Integer goodsId;
private String goodsName;
private String goodsImg;
public String getUserAvatar() {
return userAvatar;
}
public void setUserAvatar(String userAvatar) {
this.userAvatar = userAvatar;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getGoodsImg() {
return goodsImg;
}
public void setGoodsImg(String goodsImg) {
this.goodsImg = goodsImg;
}
}
CommentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.CommentMapper">
<select id="selectAll" resultType="com.example.entity.Comment">
select `comment`.*, user.name as userName, user.avatar as userAvatar, orders.order_no as orderNo, goods.name as goodsName, goods.img as goodsImg
from `comment`
left join user on `comment`.user_id = user.id
left join orders on `comment`.order_id = orders.id
left join goods on `comment`.goods_id = goods.id
<where>
<if test="content != null"> and `comment`.content like concat('%', #{content}, '%')</if>
<if test="userId != null"> and `comment`.user_id = #{userId}</if>
<if test="goodsId != null"> and `comment`.goods_id = #{goodsId}</if>
<if test="orderId != null"> and `comment`.order_id = #{orderId}</if>
<if test="goodsName != null"> and goods.name like concat('%', #{goodsName}, '%')</if>
</where>
order by `comment`.id desc
</select>
<select id="selectById" resultType="com.example.entity.Comment">
select * from `comment` where id = #{id}
</select>
<delete id="deleteById">
delete from `comment` where id = #{id}
</delete>
<insert id="insert" parameterType="com.example.entity.Comment" useGeneratedKeys="true">
insert into `comment`
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="score != null">score,</if>
<if test="content != null">content,</if>
<if test="userId != null">user_id,</if>
<if test="time != null">time,</if>
<if test="orderId != null">order_id,</if>
<if test="goodsId != null">goods_id,</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="score != null">#{score},</if>
<if test="content != null">#{content},</if>
<if test="userId != null">#{userId},</if>
<if test="time != null">#{time},</if>
<if test="orderId != null">#{orderId},</if>
<if test="goodsId != null">#{goodsId},</if>
</trim>
</insert>
<update id="updateById" parameterType="com.example.entity.Comment">
update `comment`
<set>
<if test="id != null">
id = #{id},
</if>
<if test="score != null">
score = #{score},
</if>
<if test="content != null">
content = #{content},
</if>
<if test="userId != null">
user_id = #{userId},
</if>
<if test="time != null">
time = #{time},
</if>
<if test="orderId != null">
order_id = #{orderId},
</if>
<if test="goodsId != null">
goods_id = #{goodsId},
</if>
</set>
where id = #{id}
</update>
</mapper>
开发前端页面
Comment.vue
<template>
<div>
<div class="card" style="margin-bottom: 5px;">
<el-input v-model="data.content" style="width: 300px; margin-right: 10px" placeholder="请输入评价内容查询"></el-input>
<el-input v-model="data.goodsName" style="width: 300px; margin-right: 10px" placeholder="请输入商品名称查询"></el-input>
<el-button type="primary" @click="load">查询</el-button>
<el-button type="info" style="margin: 0 10px" @click="reset">重置</el-button>
</div>
<div class="card" style="margin-bottom: 5px">
<el-table :data="data.tableData" stripe>
<el-table-column prop="goodsImg" label="商品图片">
<template #default="scope">
<el-image style="width: 50px; height: 50px; display: block; border-radius: 5px" :src="scope.row.goodsImg" :preview-src-list="[scope.row.goodsImg]" preview-teleported></el-image>
</template>
</el-table-column>
<el-table-column prop="goodsName" label="商品名称"></el-table-column>
<el-table-column prop="orderNo" label="订单编号"></el-table-column>
<el-table-column prop="score" label="评分" width="200">
<template #default="scope">
<el-rate disabled allow-half show-score v-model="scope.row.score" />
</template>
</el-table-column>
<el-table-column prop="content" label="评价内容" show-overflow-tooltip></el-table-column>
<el-table-column prop="userName" label="评价人"></el-table-column>
<el-table-column prop="time" label="时间"></el-table-column>
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="card">
<el-pagination @current-change="load" background layout="total, prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>
</div>
</template>
<script setup>
import request from "@/utils/request";
import {reactive, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
pageNum: 1,
pageSize: 10,
total: 0,
formVisible: false,
form: {},
tableData: [],
content: null,
goodsName: null,
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
]
}
})
// 分页查询
const load = () => {
request.get('/comment/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
content: data.content,
goodsName: data.goodsName,
}
}).then(res => {
data.tableData = res.data?.list
data.total = res.data?.total
})
}
load()
// 新增
const handleAdd = () => {
data.form = {}
data.formVisible = true
}
// 编辑
const handleEdit = (row) => {
data.form = JSON.parse(JSON.stringify(row))
data.formVisible = true
}
// 新增保存
const add = () => {
request.post('/comment/add', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 编辑保存
const update = () => {
request.put('/comment/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 弹窗保存
const save = () => {
formRef.value.validate(valid => {
if (valid) {
// data.form有id就是更新,没有就是新增
data.form.id ? update() : add()
}
})
}
// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/comment/delete/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 重置
const reset = () => {
data.content = null
data.goodsName = null
load()
}
</script>
UserComment.vue
<template>
<div class="front-container">
<div class="card" style="margin-bottom: 5px;">
<el-input v-model="data.content" style="width: 300px; margin-right: 10px" placeholder="请输入评价内容查询"></el-input>
<el-input v-model="data.goodsName" style="width: 300px; margin-right: 10px" placeholder="请输入商品名称查询"></el-input>
<el-button type="primary" @click="load">查询</el-button>
<el-button type="info" style="margin: 0 10px" @click="reset">重置</el-button>
</div>
<div class="card" style="margin-bottom: 5px">
<el-table :data="data.tableData" stripe>
<el-table-column prop="goodsImg" label="商品图片">
<template #default="scope">
<el-image style="width: 50px; height: 50px; display: block; border-radius: 5px" :src="scope.row.goodsImg" :preview-src-list="[scope.row.goodsImg]" preview-teleported></el-image>
</template>
</el-table-column>
<el-table-column prop="goodsName" label="商品名称">
<template #default="scope">
<a :href="'/front/goodsDetail?id=' + scope.row.goodsId">{{ scope.row.goodsName }}</a>
</template>
</el-table-column>
<el-table-column prop="orderNo" label="订单编号"></el-table-column>
<el-table-column prop="score" label="评分" width="200">
<template #default="scope">
<el-rate disabled allow-half show-score v-model="scope.row.score" />
</template>
</el-table-column>
<el-table-column prop="content" label="评价内容" width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="time" label="时间"></el-table-column>
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-button type="primary" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="card">
<el-pagination @current-change="load" background layout="total, prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>
<el-dialog title="评价信息" width="30%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form ref="formRef" :model="data.form" :rules="data.rules" label-width="80px" style="padding-right: 30px; padding-top: 20px">
<el-form-item label="评分" prop="score">
<el-rate show-score allow-half v-model="data.form.score"></el-rate>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input type="textarea" :rows="3" v-model="data.form.content" autocomplete="off" placeholder="请输入评价内容" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import request from "@/utils/request";
import {reactive, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
pageNum: 1,
pageSize: 10,
total: 0,
formVisible: false,
form: {},
tableData: [],
content: null,
goodsName: null,
rules: {
score: [
{ required: true, message: '请输入评分', trigger: 'change' },
],
content: [
{ required: true, message: '请输入内容', trigger: 'blur' },
],
}
})
// 分页查询
const load = () => {
request.get('/comment/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
content: data.content,
goodsName: data.goodsName,
userId: data.user.id,
}
}).then(res => {
data.tableData = res.data?.list
data.total = res.data?.total
})
}
load()
// 新增
const handleAdd = () => {
data.form = {}
data.formVisible = true
}
// 编辑
const handleEdit = (row) => {
data.form = JSON.parse(JSON.stringify(row))
data.formVisible = true
}
// 新增保存
const add = () => {
request.post('/comment/add', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 编辑保存
const update = () => {
request.put('/comment/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 弹窗保存
const save = () => {
formRef.value.validate(valid => {
if (valid) {
// data.form有id就是更新,没有就是新增
data.form.id ? update() : add()
}
})
}
// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/comment/delete/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 重置
const reset = () => {
data.content = null
data.goodsName = null
load()
}
</script>
UserOrders.vue
<template>
<div class="front-container" style="width: 80%">
<div style="margin-bottom: 10px;">
<el-input clearable @clear="load" v-model="data.orderNo" style="width: 400px; height: 40px; margin-right: 10px" placeholder="请输入订单编号查询"></el-input>
<el-input clearable @clear="load" v-model="data.goodsName" style="width: 400px; height: 40px; margin-right: 10px" placeholder="请输入商品名称查询"></el-input>
<el-button style="height: 40px" type="primary" @click="load">查 询</el-button>
</div>
<div class="card">
<el-table :data="data.tableData" stripe :cell-style="{'backgroundColor': '#e8efff'}" default-expand-all>
<el-table-column type="expand">
<template #default="props">
<div style="padding: 10px">
<el-table :data="props.row.orderDetailList" border>
<el-table-column label="商品图片" prop="goodsImg" width="100">
<template #default="scope">
<img :src="scope.row.goodsImg" alt="" style="width: 50px; height: 50px">
</template>
</el-table-column>
<el-table-column label="商品名称" prop="goodsName" show-overflow-tooltip></el-table-column>
<el-table-column label="商品单价" prop="goodsPrice" width="100"></el-table-column>
<el-table-column label="数量" prop="num" width="100">
<template #default="scope">
X {{ scope.row.num }}
</template>
</el-table-column>
<el-table-column label="小计" width="150">
<template #default="scope">
<b style="color: red">{{ (scope.row.goodsPrice * scope.row.num).toFixed(2) }} 元</b>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="scope">
<el-button @click="handleAddComment(scope.row)" type="success" v-if="props.row.status === '已完成'">评价</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column prop="orderNo" label="订单编号" width="240">
<template #default="scope">
<b style="color: #333">{{ scope.row.orderNo }}</b>
</template>
</el-table-column>
<el-table-column prop="total" label="总价格">
<template #default="scope">
<b style="color: red">{{ scope.row.total }}元</b>
</template>
</el-table-column>
<el-table-column prop="deliverType" label="配送类型"></el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag type="danger" v-if="scope.row.status === '已取消'">已取消</el-tag>
<el-tag type="warning" v-if="scope.row.status === '待接单'">待接单</el-tag>
<el-tag type="primary" v-if="scope.row.status === '已配送'">已配送</el-tag>
<el-tag type="primary" v-if="scope.row.status === '已出货'">已出货</el-tag>
<el-tag type="success" v-if="scope.row.status === '已完成'">已完成</el-tag>
</template>
</el-table-column>
<el-table-column prop="time" label="下单时间"></el-table-column>
<el-table-column prop="address" label="地址" width="300"></el-table-column>
<el-table-column prop="deliver" label="配送信息" width="300"></el-table-column>
<el-table-column label="订单操作" align="center" width="120">
<template #default="scope">
<el-button @click="cancel(scope.row)" type="danger" v-if="scope.row.status === '待接单'">取消</el-button>
<el-button @click="done(scope.row)" type="primary" v-if="scope.row.status === '已出货' || scope.row.status === '已配送'">确认收货</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 20px">
<el-pagination @current-change="load" layout="total, prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>
</div>
<el-dialog title="评价信息" width="30%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form ref="formRef" :model="data.form" :rules="data.rules" label-width="80px" style="padding-right: 30px; padding-top: 20px">
<el-form-item label="评分" prop="score">
<el-rate show-score allow-half v-model="data.form.score"></el-rate>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input type="textarea" :rows="3" v-model="data.form.content" autocomplete="off" placeholder="请输入评价内容" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import request from "@/utils/request";
import {reactive, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
pageNum: 1,
pageSize: 3,
total: 0,
formVisible: false,
form: {},
tableData: [],
orderNo: null,
goodsName: null,
rules: {
score: [
{ required: true, message: '请输入评分', trigger: 'change' },
],
content: [
{ required: true, message: '请输入内容', trigger: 'blur' },
],
}
})
// 分页查询
const load = () => {
request.get('/orders/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
orderNo: data.orderNo,
goodsName: data.goodsName,
userId: data.user.id
}
}).then(res => {
data.tableData = res.data?.list
data.total = res.data?.total
})
}
load()
const cancel = (row) => {
ElMessageBox.confirm('您确认取消订单吗?', '二次确认', { type: 'warning' }).then(res => {
data.form = row
data.form.status = '已取消'
updateOrder()
}).catch(err => {})
}
const done = (row) => {
ElMessageBox.confirm('您确认订单货物已经收到了吗?', '二次确认', { type: 'warning' }).then(res => {
data.form = row
data.form.status = '已完成'
updateOrder()
}).catch(err => {})
}
// 编辑保存
const updateOrder = () => {
request.put('/orders/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}
// 新增
const handleAddComment = (row) => {
request.get('/comment/selectAll', {
params: {
orderId: row.orderId,
goodsId: row.goodsId
}
}).then(res => {
// 返回的是数组
data.form = res.data?.length > 0 ? res.data[0] : { orderId: row.orderId, goodsId: row.goodsId, userId: data.user.id }
data.formVisible = true
})
}
// 新增保存
const addComment = () => {
request.post('/comment/add', data.form).then(res => {
if (res.code === '200') {
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 编辑保存
const updateComment = () => {
request.put('/comment/update', data.form).then(res => {
if (res.code === '200') {
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 弹窗保存
const save = () => {
formRef.value.validate(valid => {
if (valid) {
// data.form有id就是更新,没有就是新增
data.form.id ? updateComment() : addComment()
}
})
}
// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/orders/delete/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 重置
const reset = () => {
data.orderNo = null
data.goodsName = null
load()
}
</script>
<style scoped>
.el-tag {
font-weight: bold;
}
.el-tag--warning {
color: orange;
background-color: #fff2de;
}
</style>
GoodsDetail.vue
<template>
<div class="front-container" style="width: 50%">
<div class="card" style="padding: 20px; display: flex; grid-gap: 20px; margin-bottom: 10px">
<img :src="data.goods.img" alt="" style="width: 300px; height: 300px">
<div style="flex: 1">
<div style="display: flex; align-items: flex-start; grid-gap: 20px; margin-bottom: 10px">
<div style="font-size: 22px; font-weight: bold; line-height: 25px; flex: 1">
<el-tag style="margin-right: 5px; float: left; background-color: red; color: white" type="danger" v-if="data.goods.recommend === '是'">推荐</el-tag>
{{ data.goods.name }}
</div>
<div style="width: 60px; cursor: pointer; color: #666" @click="addCollect" v-if="!data.userCollect?.id">
<el-icon style="position: relative; top: 3px" size="18"><Star /></el-icon>收藏
</div>
<div style="width: 100px; cursor: pointer; color: orange" @click="removeCollect" v-if="data.userCollect?.id">
<el-icon style="position: relative; top: 3px" size="18"><StarFilled /></el-icon>取消收藏
</div>
</div>
<div style="margin-bottom: 20px">
<span style="color: red; font-size: 18px">¥</span><b style="color: red; font-size: 30px">{{ data.goods.price }}</b>
<span style="color: #666; margin-left: 20px">累计销量 {{ data.goods.saleCount }}</span>
<span style="color: #666; margin-left: 20px">剩余库存 {{ data.goods.store }}</span>
</div>
<div style="margin-bottom: 20px; padding: 10px; border-radius: 5px; background-color: #e8e4e4; line-height: 25px; text-align: justify">{{ data.goods.description }}</div>
<div>
<el-input-number style="width: 150px; height: 40px" :min="1" v-model="data.num"></el-input-number>
<el-button @click="addCart" style="height: 40px; margin-left: 5px" type="danger">加入购物车</el-button>
<el-button @click="handleAddOrder" style="height: 40px; margin-left: 5px" type="danger">立即购买</el-button>
</div>
<div style="margin-top: 10px; color: #666">校园小卖部销售并发货的商品,由小卖部提供发票和相应的售后服务。请您放心购买!</div>
</div>
</div>
<div class="card" style="padding: 20px; margin-bottom: 50px">
<div style="font-size: 20px; padding-bottom: 10px; border-bottom: 1px solid #ddd">
<span @click="changeTab('商品详情')" style="cursor: pointer" :class="{'current-active': data.current === '商品详情' }">商品详情</span>
<span @click="changeTab('商品评价')" :class="{'current-active': data.current === '商品评价' }" style="cursor: pointer; margin-left: 20px">商品评价</span>
</div>
<div v-if="data.current === '商品详情'" style="padding: 10px" v-html="data.goods.content"></div>
<div v-if="data.current === '商品评价'" style="min-height: 700px">
<div v-if="data.commentList.length === 0" style="padding: 50px; text-align: center; color: #666">暂无评价...</div>
<div v-if="data.commentList.length > 0" style="padding: 20px;">
<!-- 显示评论列表-->
<div v-for="(item, index) in data.commentList" :key="item.id" style="display: flex; grid-gap: 10px; padding: 10px 0;
border-bottom: 1px solid #ddd" :style="{ 'borderWidth': index === data.commentList.length - 1 ? 0 : '1px' }">
<img :src="item.userAvatar" alt="" style="width: 50px; height: 50px; border-radius: 50%">
<div style="flex: 1">
<div><span>{{ item.userName }}</span>
<span style="color: #666; font-size: 13px; margin-left: 10px">{{ item.time }}</span>
</div>
<div style="margin-bottom: 5px"> <el-rate v-model="item.score" show-score allow-half disabled></el-rate></div>
<div>{{ item.content }}</div>
</div>
</div>
<div style="margin-top: 20px">
<el-pagination @current-change="loadComment" layout="total, prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>
</div>
</div>
</div>
<el-dialog title="下单信息" width="30%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form ref="formRef" :model="data.form" :rules="data.rules" label-width="100px" style="padding-right: 30px">
<el-form-item label="配送类型" prop="deliverType">
<el-radio-group v-model="data.form.deliverType">
<el-radio-button value="自提" label="自提"></el-radio-button>
<el-radio-button value="外送" label="外送"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="收货地址" prop="address" v-if="data.form.deliverType === '外送'">
<el-input v-model="data.form.address" type="textarea" :rows="3" placeholder="请输入外送的接收地址,包括联系人、联系电话、地址信息"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="addOrder">确 认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import router from "@/router";
import request from "@/utils/request";
import {ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
id: router.currentRoute.value.query.id,
goods: {},
num: 1,
current: '商品详情',
commentList: [],
pageNum: 1,
pageSize: 5,
total: 0,
userCollect: {},
form: {},
formVisible: false,
rules: {
deliverType: [
{ required: true, message: '请选择配送类型', trigger: 'change' }
],
address: [
{ required: true, message: '请输入配送地址', trigger: 'blur' }
]
}
})
const loadComment = () => {
request.get('/comment/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
goodsId: data.id
}
}).then(res => {
data.commentList = res.data?.list
data.total = res.data?.total
})
}
loadComment()
const handleAddOrder = () => {
data.form = {}
data.formVisible = true
}
const addOrder = () => {
formRef.value.validate(valid => {
if (valid) {
data.form.userId = data.user.id
data.form.cartList = [ {goodsId: data.id, num: data.num } ]
request.post('/orders/add', data.form).then(res => {
if (res.code === '200') {
ElMessage.success('下单成功')
load()
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
})
}
const addCart = () => {
request.post('/cart/add', { goodsId: data.id, num: data.num, userId: data.user.id }).then(res => {
if (res.code === '200') {
ElMessage.success('加入购物车成功')
} else {
ElMessage.error(res.msg)
}
})
}
// 当前的商品是否被当前登录的用户收藏过
const loadCollect = () => {
request.get('/collect/selectAll', {
params:{
goodsId: data.id,
userId: data.user.id
}
}).then(res => {
if (res.data?.length > 0) { // 查询到数据了 表示用户收藏过了
data.userCollect = res.data[0]
} else {
data.userCollect = {}
}
})
}
loadCollect()
// 取消收藏
const removeCollect = () => {
request.delete('/collect/delete/' + data.userCollect.id).then(res => {
if (res.code === '200') {
ElMessage.success('操作成功')
loadCollect()
} else {
ElMessage.error(res.msg)
}
})
}
const addCollect = () => {
request.post('/collect/add', { goodsId: data.id, userId: data.user.id }).then(res => {
if (res.code === '200') {
ElMessage.success('操作成功')
loadCollect()
} else {
ElMessage.error(res.msg)
}
})
}
const changeTab = (tabName) => {
data.current = tabName
}
const load = () => {
request.get('/goods/selectById/' + data.id).then(res => {
data.goods = res.data
})
}
load()
</script>
<style>
.current-active {
color: red;
border-bottom: 2px solid red;
padding-bottom: 10px
}
</style>