目录
回复操作
1.创建回复表
执行命令。
php think migrate:create Reply
打开database\migrations\…_reply.php,编写change()方法。
public function change()
{
$table = $this->table(
'reply',
['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci']
);
$table->addColumn(
'topic_id',
'integer',
['null' => false, 'default' => 0, 'comment' => '分类id']
)
->addColumn(
'user_id',
'integer',
['null' => false, 'default' => 0, 'comment' => '用户id']
)
->addColumn(
'content',
'text',
['null' => false, 'comment' => '回复内容']
)
->addColumn(
'is_show',
'boolean',
['null' => false, 'default' => 0, 'comment' => '是否显示']
)
->addTimestamps()
->create();
}
执行迁移。
php think migrate:run
2.发表回复
编写src\components\topic\Reply.vue。
<template>
<div class="reply">
<div class="add-reply mt-4">
<div class="row mb-1">
<div class="col-md-8">
<textarea ref="reply" maxlength="10000" v-model="reply.content"></textarea>
</div>
</div>
<input type="button" class="btn btn-primary" @click="sendReply" value="发布评论" />
</div>
</div>
</template>
<script>
export default {
data () {
return {
topic_id: this.$route.params.id,
reply: {
content: ''
},
}
},
methods: {
sendReply () {
// 发布评论
}
}
}
</script>
<style scoped>
.add-reply textarea {
width: 100%;
height: 80px;
border-radius: 3px;
padding: 5px;
font-size: 14px;
border: 1px solid #ccc;
}
.add-reply button {
margin-top: 10px;
}
</style>
页面效果如下。
编写sendReply()方法。
sendReply () {
var data = { topic_id: this.topic_id, content: this.reply.content }
this.$http.post('reply/save', data).then(res => {
if (res.data.code === 0) {
this.$toastr.e(res.data.msg)
} else if (res.data.code === 1) {
this.$toastr.s(res.data.msg)
}
}).catch(() => {
this.$toastr.e('服务器异常。')
})
},
打开route\route.php,编写服务器端路由。
Route::post('reply/save', 'api/Reply/save');
创建application\api\controller\Reply.php。
<?php
namespace app\api\controller;
class Reply extends Common
{
protected $checkActive = ['save', 'del'];
public function save()
{
$id = $this->request->post('id/d', 0);
$data = [
'topic_id' => $this->request->post('topic_id/d', 0),
'content' => $this->request->post('content/s', '')
];
}
}
创建application\api\validate\Reply.php。
<?php
namespace app\api\validate;
use think\Validate;
class Reply extends Validate
{
protected $rule = [
'content' => 'checkText:65535'
];
protected $message = [
];
protected function checkText($value, $rule)
{
return strlen($value) <= $rule ? true : '内容最多65535个字节';
}
}
创建application\api\model\Reply.php。
<?php
namespace app\api\model;
use think\Model;
class Reply extends Model
{
}
导入命名空间。
use app\api\validate\Reply as ReplyValidate;
use app\api\model\Reply as ReplyModel;
打开application\api\controller\Reply.php,继续编写save()方法。
public function save()
{
……(原有代码)
$validate = new ReplyValidate();
if (!$validate->check($data)) {
$this->error('操作失败,' . $validate->getError() . '。');
}
if ($id) {
if (!$reply = ReplyModel::get($id)) {
$this->error('修改失败,记录不存在。');
}
if ($this->user->role !== 'admin' && $this->user->id !== $reply->user_id) {
$this->error('修改失败,您没有权限修改此内容。');
}
$reply->save($data);
$this->success('修改成功。', null, ['id' => $id]);
} else {
$data['is_show'] = true;
$data['user_id'] = $this->user->id;
$reply = ReplyModel::create($data);
$this->success('添加成功。', null, ['id' => $reply->id]);
}
}
测试程序。
3.回复列表
修改src\components\topic\Reply.vue,在页面打开后加载回复列表。
<script>
import Pagination from '../../components/Pagination'
export default {
data () {
return {
……(原有代码)
replies: [],
page: {
current: parseInt(this.$route.params.page) || 1,
total: 0,
size: 7
}
}
},
created () {
this.getReply()
},
methods: {
getReply () {
var params = { topic_id: this.topic_id, page: this.page.current, size: this.page.size }
this.$http.get('reply/index', { params: params }).then(res => {
if (res.data.code === 1) {
this.replies = res.data.data.data
this.page.current = res.data.data.page
this.page.total = res.data.data.total
}
}).catch(() => {
this.$toastr.e('加载失败,服务器异常。')
})
},
pageChange (page) {
this.page.current = page
this.getReply()
},
……(原有代码)
},
components: { Pagination }
}
打开src\components\topic\Reply.vue,在页面中输出。
<div class="reply">
<ul class="list-group list-group-flush mb-4">
<li class="list-group-item bg-light" v-for="(reply,index) in replies" :key="index">
<div class="row">
<div clas="col">
<img :src="reply.user.img_url" class="rounded-circle reply-img">
</div>
<div clas="col">
<div class="row">
<span>
<strong>{{ reply.user.name }}</strong>
<span class="small text-muted">
<span class="d-none d-md-inline"> 创建于 {{ reply.create_time }} </span>
<span>更新于 {{ reply.update_time }}</span>
</span>
</span>
</div>
<div class="row card-text">{{ reply.content }}</div>
</div>
</div>
</li>
</ul>
<Pagination :current="page.current" :total="page.total" :size="page.size" @change="pageChange"></Pagination>
……(原有代码)
</div>
添加样式。
<style scoped>
……(原有代码)
.reply {
font-size: 14px;
margin-top: 20px;
margin-bottom: 20px;
}
.reply span {
color: #385f8a;
}
.reply-img {
width: 44px;
height: 44px;
margin-right: 25px;
}
</style>
打开route\route.php,添加服务器端路由。
Route::get('reply/index', 'api/Reply/index');
打开application\api\model\Reply.php,编写user()方法。
public function user()
{
return $this->belongsTo('User', 'user_id');
}
打开application\api\controller\Reply.php,编写index()方法。
public function index()
{
$topic_id = $this->request->get('topic_id/d', 0);
$page = max($this->request->get('page/d', 1), 1);
$size = max(min($this->request->get('size/d', 10), 50), 1);
$where = ['is_show' => 1, 'topic_id' => $topic_id];
$total = ReplyModel::where($where)->count();
$data = ReplyModel::with(['user' => function ($query) {
$query->field('id,name,img_url');
}])->where($where)->order('id', 'desc')->limit(($page - 1) * $size, $size)->select();
$data = array_map(function ($v) {
$v['user']['img_url'] = $this->avatarUrl($v['user']['img_url']);
return $v;
}, $data->toArray());
$this->success('', null, [
'data' => $data,
'total' => $total,
'page' => $page
]);
}
在发表回复成功后,更新列表,并重置表单。
sendReply () {
var data = { topic_id: this.topic_id, content: this.reply.content }
this.$http.post('reply/save', data).then(res => {
if (res.data.code === 0) {
this.$toastr.e(res.data.msg)
} else if (res.data.code === 1) {
this.$toastr.s(res.data.msg)
this.resetReply()
this.getReply()
}
}).catch(() => {
this.$toastr.e('服务器异常。')
})
},
resetReply () {
this.reply.content = ''
},
页面效果如下。
4.修改回复
打开src\components\topic\Reply.vue。
<script>
import { mapState } from 'vuex'
import Pagination from '../../components/Pagination'
export default {
data () {
……(原有代码)
},
computed: {
...mapState(['user'])
},
……(原有代码)
}
</script>
在回复的旁边放上“编辑”按钮。
<span>
<strong>{{ reply.user.name }}</strong>
……(原有代码)
<span v-if="user.role === 'admin' || user.id == reply.user_id">
<button class="btn btn-link opt" @click="editReply(reply)">编辑</button>
</span>
</span>
在data中给reply增加一个id,表示当前编辑的回复的id。
reply: {
id: 0,
content: ''
},
在methods中增加editReply()方法。
methods: {
……(原有代码)
editReply (reply) {
this.reply.id = reply.id
this.reply.content = reply.content
this.$refs.reply.focus()
},
}
当设置了回复的id以后,将“发布评论”按钮改为“修改评论”,并提供一个“取消”按钮。
<div class="add-reply mt-4">
……(原有代码)
<input type="button" class="btn btn-primary" @click="sendReply" :value="reply.id ? '修改评论' : '发布评论'" />
<input type="button" v-if="reply.id" class="btn btn-default ml-1" @click="resetReply" value="取消" />
</div>
修改sendReply()方法,将id放入data中发送。
sendReply () {
var data = { topic_id: this.topic_id, content: this.reply.content, id: this.reply.id }
……(原有代码)
},
修改resetReply()方法,将id设为0。
resetReply () {
this.reply.id = 0
this.reply.content = ''
},
添加样式。
<style scoped>
……(原有代码)
.opt {
color: #adadad;
font-size: 12px;
}
.opt:hover {
text-decoration: none;
color: #666;
}
</style>
页面效果如下。
单击“编辑”后,效果如下。
5.删除回复
打开src\components\topic\Reply.vue,添加“删除”按钮。
<span v-if="user.role === 'admin' || user.id == reply.user_id">
……(原有代码)
<button class="btn btn-link opt" @click="delReply(index, reply.id)">删除</button>
</span>
在methods中编写delReply()方法。
methods: {
……(原有代码)
delReply (index, id) {
this.replies.splice(index, 1)
this.$http.post('reply/del', { id: id }).then(res => {
if (res.data.code === 0) {
this.$toastr.e(res.data.msg)
} else if (res.data.code === 1) {
this.$toastr.s(res.data.msg)
}
})
}
}
打开route\route.php,编写服务器端路由。
Route::post('reply/del', 'api/Reply/del');
打开application\api\controller\Reply.php,编写del()方法。
public function del()
{
$id = $this->request->post('id/d', 0);
$reply = ReplyModel::where([
'is_show' => true
])->get($id);
if (!$reply) {
$this->error('删除失败,记录不存在。');
}
if ($this->user->role !== 'admin' && $reply->user_id !== $this->user->id) {
$this->error('删除失败,您没有权限执行此操作。');
}
$reply->is_show = false;
$reply->save();
$this->success('删除成功。', null, []);
}
测试程序。
6.删除主题时隐藏关联的回复
打开application\api\controller\Topic.php,先导入命名空间。
use app\api\model\Reply as ReplyModel;
找到del()方法,进行修改。
public function del()
{
……(原有代码)
ReplyModel::where(['topic_id' => $id, 'is_show' => true])->update(['is_show' => false]);
$this->success('删除成功。', null, []);
}
测试程序。
大功告成啦、、嘿嘿~