Laravel 教程延伸 - 使用 textcomplete 标记用户 (支援中文)

实作了几次的 Laravel 教程后,该是导入一些功能的时候了
于是想练习一下标记用户的功能,网上搜寻了几个套件
发现 textcomplete 符合我的需求

请搭配第二本教程使用 《L02 Laravel 进阶课程 - 从零开始构建论坛系统》

准备

首先下载套件 : textcomplete
我们只需要用到

  • jquery.overlay.min.js
  • jquery.textcomplete.min.js
  • jquery.textcomplete.css
    这三个档案,我将套件放在 assets 的 textcomplete 目录内

动手做

赋予 textarea 一个 id : reply_box 以便操作

resources/views\topics_reply_box.blade.php
.
.
.
<div class="form-group">
    <textarea class="form-control" rows="3" placeholder="分享你的想法" name="content" id="reply_box"></textarea>
</div>
.
.
.

然后引入资源档案及操作

触发条件为 : 空一格后输入@及字触发搜寻

resources/views/topics/show.blade.php
.
.
.
@section('styles')
    <link rel=stylesheet type="text/css" href="{{asset('assets/textcomplete/jquery.textcomplete.css')}}">
@stop

@section('scripts')
    <script src="{{asset('assets/textcomplete/jquery.textcomplete.min.js')}}"></script>
    <script src="{{asset('assets/textcomplete/jquery.overlay.min.js')}}"></script>

    <script>
        $('#reply_box').textcomplete([
            {
                id: 'reply_box',
                match: /\B@([\u4e00-\u9fa5a-zA-Z0-9]+)$/,
                search: function (term, callback) {
                    $.ajax({
                        url: '{{route('users.json')}}',
                        type: 'post',
                        data: {
                            searchkey : term,
                            text : $('textarea[name="content"]').val(),
                        },
                        headers: {
                            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                        },
                        dataType: 'json',
                        success: function (r) {
                            callback($.map(r , function (v, i) {
                                return (v.name.toLowerCase().indexOf(term.toLowerCase()) >= 0 ) ? v : null;
                            }));
                        },
                        error: function (r) {
                        },
                    });

                },
                template: function (value) {
                    return '<img style="margin-right:0.7em" width="30" height="30" src="'+value.picture+'"></img>' + value.name;
                },
                replace: function (value, term) {
                    return '['+value.id+':'+value.name+'] ';
                },
                context : function(value) {
                    return value;
                },
                index: 1,
            },
        ], {
            onKeydown: function (e, commands) {
                if (e.ctrlKey && e.keyCode === 74) {
                    return commands.KEY_ENTER;
                }
            }
        }, {appendTo: 'body'}).overlay([
            {
                match: /(\[{1}[0-9]+:{1}[^\[\]:]+\]{1})/g,
                css: {
                    'background-color': '#d8dfea'
                }
            }
        ]);
    </script>
@stop

接着新增一个路由接收这个请求

web.php
.
.
.
Route::post('usersjson','UsersController@usersjson')->name('users.json');

轮到控制器了,新增一个方法来搜寻用户

app/Http/Controllers/UsersController.php
.
.
.
public function usersjson(Request $request)
    {
        $searchkey = $request->searchkey;
        $text = $request->text;
        if(trim($searchkey)) {
            $object_user = [];

            // 略过已被tag的id
            preg_match_all("/\[(.+?)\]/", $text, $match);
            $listedId = array_map(function($v){
                return substr($v, 0, strpos($v, ':'));
            }, $match[1]);

            // 略过本人
            $listedId[] = Auth::user()->id;

            $users_id = User::where('name', 'like', "%" . $searchkey . "%")->pluck('id')->toArray();
            $listIds = array_diff($users_id, $listedId);

            // 送给前端选单的用户清单
            if($listIds) {
                $users = User::whereIn('id', $listIds)->get(['id', 'name', 'avatar']);

                foreach ($users as $user) {
                    $object_user[] = [
                        'id' => $user['id'],
                        'name' => $user['name'],
                        'picture' => $user['avatar'],
                    ];
                }
            }
            return response()->json($object_user);
        }
    }

至此,在回复栏位应该可以出现相关条件下拉选单了。

您会发现标记后,在输入框内的格式为 : [id:name]
这是我设计用来辨识已被 Tag 的人员用的,如果各位有其他方式欢迎留言给我

优化

接着我们希望可以在回复清单上连结至被标记人名的个人页面
我们必须先对取出来的内容做处理

新增一个处理连结的函式

bootstrap/helpers.php
.
.
.
function make_mention_link($mention) {

    preg_match_all("/\[(.+?)\]/", $mention, $match);

    if($match[1]) {
        foreach ($match[1] as $k0 => $v0) {
            // 用户 id
            $mentionUserId =  substr($v0, 0, strpos($v0, ':'));  
            // 用户名稱
            $mentionUserName = substr($v0, strpos($v0, ':')+1, strlen($v0));   
            // 组出连结
            $replace = '<span class="mention"><a target="_blank" href="'.route('users.show', [$mentionUserId]).'">'.$mentionUserName.'</a></span>';
            $mention = str_replace($match[0][$k0], $replace, $mention);
        }
    }

    return $mention;
}
resources/views/topics/_reply_list.blade.php
.
.
.
                <div class="reply-content">
                    {!! make_mention_link($reply->content) !!}
                </div>
.
.
.

这样便可以连结至该人的个人页面。

下一个目标是送出评论后,可以通知被标记的人。

还有许多不足之处,如果有建议或指教,请您一定要留言给我让我学习,

谢谢大家了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 2

这样是不是好一点

User::where('name', 'like', "%" . $searchkey . "%")
    ->whereNotIn('id', $listedId)
    ->get(['id', 'name', 'avatar'])
    ->toArray()
6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!