兔姬桑 пре 4 година
родитељ
комит
0f543ceab2

+ 39 - 0
app/Console/Commands/updateTicket.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\TicketReply;
+use App\Models\User;
+use Illuminate\Console\Command;
+use Log;
+
+class updateTicket extends Command {
+	protected $signature = 'updateTicket';
+	protected $description = '更新工单';
+
+	public function __construct() {
+		parent::__construct();
+	}
+
+	public function handle() {
+		Log::info('----------------------------【更新工单】开始----------------------------');
+		// 获取管理员
+		$adminList = User::query()->whereIsAdmin(1)->get();
+		foreach($adminList as $admin){
+			Log::info('----------------------------【更新管理员'.$admin->id.'回复工单】开始----------------------------');
+			// 获取该管理回复过的工单
+			$replyList = TicketReply::query()->whereUserId($admin->id)->get();
+			// 更新工单
+			foreach($replyList as $reply){
+				$ret = TicketReply::query()->whereId($reply->id)->update(['user_id' => 0, 'admin_id' => $admin->id]);
+				if($ret){
+					Log::info('--- 管理员:'.$admin->email.'回复子单ID:'.$reply->id.' ---');
+				}else{
+					Log::error('更新回复子单ID:【'.$reply->id.'】 失败!');
+				}
+			}
+			Log::info('----------------------------【更新管理员'.$admin->id.'回复工单】完成----------------------------');
+		}
+		Log::info('----------------------------【更新工单】结束----------------------------');
+	}
+}

+ 39 - 4
app/Http/Controllers/Admin/TicketController.php

@@ -9,6 +9,7 @@ use App\Mail\closeTicket;
 use App\Mail\replyTicket;
 use App\Models\Ticket;
 use App\Models\TicketReply;
+use App\Models\User;
 use Auth;
 use Illuminate\Http\Request;
 use Mail;
@@ -32,7 +33,7 @@ class TicketController extends Controller {
 	public function ticketList(Request $request) {
 		$email = $request->input('email');
 
-		$query = Ticket::query();
+		$query = Ticket::query()->whereIn('admin_id', [0, Auth::id()]);
 
 		if(isset($email)){
 			$query->whereHas('user', function($q) use ($email) {
@@ -45,6 +46,40 @@ class TicketController extends Controller {
 		return Response::view('admin.ticket.ticketList', $view);
 	}
 
+	// 创建工单
+	public function createTicket(Request $request) {
+		$id = $request->input('id');
+		$email = $request->input('email');
+		$title = $request->input('title');
+		$content = $request->input('content');
+
+		$user = User::query()->find($id)?: User::query()->whereEmail($email)->first();
+
+		if(!$user){
+			return Response::json(['status' => 'fail', 'message' => '用户不存在']);
+		}elseif($user == Auth::user()){
+			return Response::json(['status' => 'fail', 'message' => '不能对自己发起工单']);
+		}
+
+		if(empty($title) || empty($content)){
+			return Response::json(['status' => 'fail', 'message' => '请输入标题和内容']);
+		}
+
+		$obj = new Ticket();
+		$obj->user_id = $user->id;
+		$obj->admin_id = Auth::id();
+		$obj->title = $title;
+		$obj->content = $content;
+		$obj->status = 0;
+		$obj->save();
+
+		if($obj->id){
+			return Response::json(['status' => 'success', 'message' => '工单创建成功']);
+		}else{
+			return Response::json(['status' => 'fail', 'message' => '工单创建失败']);
+		}
+	}
+
 	// 回复工单
 	public function replyTicket(Request $request) {
 		$id = $request->input('id');
@@ -56,7 +91,7 @@ class TicketController extends Controller {
 
 			$obj = new TicketReply();
 			$obj->ticket_id = $id;
-			$obj->user_id = Auth::id();
+			$obj->admin_id = Auth::id();
 			$obj->content = $content;
 			$obj->save();
 
@@ -89,8 +124,8 @@ class TicketController extends Controller {
 				return Response::json(['status' => 'fail', 'data' => '', 'message' => '回复失败']);
 			}
 		}else{
-			$view['ticket'] = Ticket::query()->whereId($id)->with('user')->first();
-			$view['replyList'] = TicketReply::query()->whereTicketId($id)->with('user')->orderBy('id')->get();
+			$view['ticket'] = Ticket::query()->whereId($id)->first();
+			$view['replyList'] = TicketReply::query()->whereTicketId($id)->orderBy('id')->get();
 
 			return Response::view('admin.ticket.replyTicket', $view);
 		}

+ 6 - 0
app/Models/Ticket.php

@@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model;
  *
  * @property int                             $id
  * @property int                             $user_id    用户ID
+ * @property int|null                        $admin_id   管理员ID
  * @property string                          $title      标题
  * @property string                          $content    内容
  * @property int                             $status     状态:0-待处理、1-已处理未关闭、2-已关闭
@@ -22,6 +23,7 @@ use Illuminate\Database\Eloquent\Model;
  * @method static Builder|Ticket newQuery()
  * @method static Builder|Ticket query()
  * @method static Builder|Ticket uid()
+ * @method static Builder|Ticket whereAdminId($value)
  * @method static Builder|Ticket whereContent($value)
  * @method static Builder|Ticket whereCreatedAt($value)
  * @method static Builder|Ticket whereId($value)
@@ -43,6 +45,10 @@ class Ticket extends Model {
 		return $this->hasOne(User::class, 'id', 'user_id');
 	}
 
+	function admin() {
+		return $this->hasOne(User::class, 'id', 'admin_id');
+	}
+
 	function getStatusLabelAttribute() {
 		switch($this->attributes['status']){
 			case 0:

+ 7 - 1
app/Models/TicketReply.php

@@ -10,7 +10,8 @@ use Illuminate\Database\Eloquent\Model;
  *
  * @property int                             $id
  * @property int                             $ticket_id  工单ID
- * @property int                             $user_id    回复用户的ID
+ * @property int                             $user_id    回复用户ID
+ * @property int                             $admin_id   管理员ID
  * @property string                          $content    回复内容
  * @property \Illuminate\Support\Carbon|null $created_at 创建时间
  * @property \Illuminate\Support\Carbon|null $updated_at 最后更新时间
@@ -18,6 +19,7 @@ use Illuminate\Database\Eloquent\Model;
  * @method static Builder|TicketReply newModelQuery()
  * @method static Builder|TicketReply newQuery()
  * @method static Builder|TicketReply query()
+ * @method static Builder|TicketReply whereAdminId($value)
  * @method static Builder|TicketReply whereContent($value)
  * @method static Builder|TicketReply whereCreatedAt($value)
  * @method static Builder|TicketReply whereId($value)
@@ -33,4 +35,8 @@ class TicketReply extends Model {
 	function user() {
 		return $this->hasOne(User::class, 'id', 'user_id');
 	}
+
+	function admin() {
+		return $this->hasOne(User::class, 'id', 'admin_id');
+	}
 }

+ 2 - 0
app/Models/User.php

@@ -25,6 +25,7 @@ use Illuminate\Notifications\Notifiable;
  * @property int                                                                                                            $enable          代理状态
  * @property string                                                                                                         $method          加密方式
  * @property string                                                                                                         $protocol        协议
+ * @property string|null                                                                                                    $protocol_param  协议参数
  * @property string                                                                                                         $obfs            混淆
  * @property int                                                                                                            $speed_limit     用户限速,为0表示不限速,单位Byte
  * @property string|null                                                                                                    $wechat          微信
@@ -75,6 +76,7 @@ use Illuminate\Notifications\Notifiable;
  * @method static Builder|User wherePassword($value)
  * @method static Builder|User wherePort($value)
  * @method static Builder|User whereProtocol($value)
+ * @method static Builder|User whereProtocolParam($value)
  * @method static Builder|User whereQq($value)
  * @method static Builder|User whereReferralUid($value)
  * @method static Builder|User whereRegIp($value)

+ 9 - 6
readme.md

@@ -1,12 +1,15 @@
 # ProxyPanel
-Support but not limited to: Shadowsocks,ShadowsocksR,ShadowsocksRR,V2Ray,VNET
+[简体中文](https://github.com/ZBrettonYe/ProxyPanel/wiki)
 
-- [Demo](https://demo.proxypanel.ml/admin/userList) 
+Support but not limited to: Shadowsocks,ShadowsocksR,ShadowsocksRR,V2Ray,Trojan,VNET
+
+- [Demo](https://demo.proxypanel.ml/) 
     - Account: test@test.com
     - Password: 123456
-- [Issues](https://github.com/ZBrettonYe/OtakuPanel_SSR/issues)
-- [WIKI](https://github.com/ZBrettonYe/OtakuPanel_SSR/wiki)
-- [Update Log](https://github.com/ZBrettonYe/OtakuPanel_SSR/wiki/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97)
+- [Issues](https://github.com/ZBrettonYe/ProxyPanel/issues)
+- [WIKI](https://github.com/ZBrettonYe/ProxyPanel/wiki)
+- [Update Log](https://github.com/ZBrettonYe/ProxyPanel/wiki/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97)
+- [Upcoming](https://github.com/ZBrettonYe/ProxyPanel/projects/2)
 - [Telegram](https://t.me/joinchat/GUrO5hZsT3FOd79HAa9pcA)
 
 ## Requirements
@@ -39,4 +42,4 @@ Support but not limited to: Shadowsocks,ShadowsocksR,ShadowsocksRR,V2Ray,VNET
 
 ## License
 
-ProxyPanel also is open-sourced software licensed under the MIT license.
+ProxyPanel is open-sourced software licensed under the GPL-3.0 license.

+ 1 - 1
resources/views/admin/config/system.blade.php

@@ -719,7 +719,7 @@
 												<option value="1">首购返利</option>
 												<option value="2">循环返利</option>
 											</select>
-											<span class="text-help offset-md-3"> 启用后登录、注册需要输入验证码 </span>
+											<span class="text-help offset-md-3"> 切换模式后旧数据不变,新的返利按新的模式计算 </span>
 										</div>
 									</div>
 									<div class="form-group col-lg-6">

+ 2 - 2
resources/views/admin/layouts.blade.php

@@ -136,7 +136,7 @@
 					</li>
 				</ul>
 			</li>
-			<li class="site-menu-item has-sub {{in_array(Request::path(), ['ticket', 'ticket/create', 'ticket/reply', 'admin/articleList', 'admin/addArticle', 'admin/editArticle', 'marketing/push', 'marketing/email']) ? 'active open' : ''}}">
+			<li class="site-menu-item has-sub {{in_array(Request::path(), ['ticket', 'ticket/reply', 'admin/articleList', 'admin/addArticle', 'admin/editArticle', 'marketing/push', 'marketing/email']) ? 'active open' : ''}}">
 				<a href="javascript:void(0)">
 					<i class="site-menu-icon wb-chat-working" aria-hidden="true"></i>
 					<span class="site-menu-title">客服系统</span>
@@ -147,7 +147,7 @@
 					@endif
 				</a>
 				<ul class="site-menu-sub">
-					<li class="site-menu-item {{in_array(Request::path(), ['ticket', 'ticket/create', 'ticket/reply']) ? 'active open' : ''}}">
+					<li class="site-menu-item {{in_array(Request::path(), ['ticket', 'ticket/reply']) ? 'active open' : ''}}">
 						<a href="/ticket">
 							<span class="site-menu-title">服务工单</span>
 							@if(\App\Models\Ticket::query()->whereStatus(0)->count() > 0 )

+ 0 - 77
resources/views/admin/ticket/addTicket.blade.php

@@ -1,77 +0,0 @@
-@extends('admin.layouts')
-@section('content')
-	<div class="page-content container">
-		@if($errors->any())
-			<div class="alert alert-danger" role="alert">
-				<button class="close" data-dismiss="alert" aria-label="Close"><span
-							aria-hidden="true">&times;</span><span class="sr-only">{{trans('home.close')}}</span>
-				</button>
-				{{$errors->first()}}
-			</div>
-		@endif
-		<div class="panel">
-			<div class="panel-heading p-20">
-				<h1 class="panel-title cyan-600"><i class="icon wb-add"></i>发起工单</h1>
-			</div>
-			<div class="panel-body">
-				<form action="/ticket/add" method="post" enctype="multipart/form-data" class="form-horizontal"
-						onsubmit="return Submit()">
-					<div class="form-body">
-						<div class="form-group">
-							<label class="control-label col-md-2">用户名</label>
-							<div class="col-md-8">
-								<input type="text" class="form-control" name="email" value="{{Request::get('email')}}"
-										id="email" autocomplete="off" autofocus required/>
-							</div>
-						</div>
-						<div class="form-group">
-							<label class="control-label col-md-2">标题</label>
-							<div class="col-md-8">
-								<input type="text" class="form-control" name="title" id="title" autocomplete="off"
-										required/>
-							</div>
-						</div>
-						<div class="form-group">
-							<label class="control-label col-md-2">内容</label>
-							<div class="col-md-8">
-                                <textarea class="form-control" rows="5" name="content" id="content"
-		                                placeholder="{{trans('home.ticket_table_content')}}"></textarea>
-							</div>
-						</div>
-					</div>
-					<div class="form-actions">
-						<div class="row">
-							<div class="col-md-offset-2 col-md-10">
-								<button type="submit" class="btn btn-success">提 交</button>
-							</div>
-						</div>
-					</div>
-				</form>
-			</div>
-		</div>
-	</div>
-@endsection
-@section('script')
-	<script type="text/javascript">
-		// ajax同步提交
-		function Submit() {
-			$.ajax({
-				type: "POST",
-				url: "/ticket/add",
-				async: false,
-				data: {
-					_token: '{{csrf_token()}}',
-					email: $('#email').val(),
-					title: $('#title').val(),
-					content: $('#content').val()
-				},
-				dataType: 'json',
-				success: function (ret) {
-					swal.fire({title: ret.message, type: 'success', timer: 1000})
-						.then(() => window.location.href = '/ticket');
-				}
-			});
-			return false;
-		}
-	</script>
-@endsection

+ 4 - 57
resources/views/admin/ticket/replyTicket.blade.php

@@ -17,58 +17,9 @@
 			<div class="panel-body">
 				<div class="chat-box">
 					<div class="chats">
-						<div class="chat chat-left">
-							<div class="chat-avatar">
-								<p class="avatar">
-									@if($ticket->user->qq)
-										<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->qq}}&s=640" alt="客户">
-									@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
-										<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->email}}&s=640" alt="客户">
-									@else
-										<img src="/assets/images/avatar.svg" alt="客户">
-									@endif
-								</p>
-							</div>
-							<div class="chat-body">
-								<div class="chat-content">
-									<p>
-										{!! $ticket->content !!}
-									</p>
-									<time class="chat-time">{{$ticket->created_at}}</time>
-								</div>
-							</div>
-						</div>
+						@component('components.chatUnit',['ticket'=>$ticket])@endcomponent
 						@foreach ($replyList as $reply)
-							<div class="chat @if (!$reply->user->is_admin) chat-left @endif">
-								<div class="chat-avatar">
-									@if ($reply->user->is_admin)
-										<p class="avatar">
-											<img src="/assets/images/logo64.png"
-													alt="{{trans('home.ticket_reply_master')}}"/>
-										</p>
-									@else
-										<p class="avatar">
-											@if($ticket->user->qq)
-												<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->qq}}&s=640"
-														alt="客户">
-											@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
-												<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->email}}&s=640"
-														alt="客户">
-											@else
-												<img src="/assets/images/avatar.svg" alt="客户">
-											@endif
-										</p>
-									@endif
-								</div>
-								<div class="chat-body">
-									<div class="chat-content">
-										<p>
-											{!! $reply->content!!}
-										</p>
-										<time class="chat-time">{{$reply->created_at}}</time>
-									</div>
-								</div>
-							</div>
+							@component('components.chatUnit',['ticket'=>$reply])@endcomponent
 						@endforeach
 					</div>
 				</div>
@@ -77,11 +28,9 @@
 				<div class="panel-footer pb-30">
 					<form>
 						<div class="input-group">
-							<input type="text" class="form-control" id="editor"
-									placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
+							<input type="text" class="form-control" id="editor" placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
 							<span class="input-group-btn">
-								<button type="button" class="btn btn-primary"
-										onclick="replyTicket()"> {{trans('home.ticket_reply')}}</button>
+								<button type="button" class="btn btn-primary" onclick="replyTicket()"> {{trans('home.ticket_reply')}}</button>
 							</span>
 						</div>
 					</form>
@@ -105,7 +54,6 @@
 			swal.fire({
 				title: '确定关闭工单?',
 				type: 'question',
-				allowEnterKey: false,
 				showCancelButton: true,
 				cancelButtonText: '{{trans('home.ticket_close')}}',
 				confirmButtonText: '{{trans('home.ticket_confirm')}}',
@@ -122,7 +70,6 @@
 								title: ret.message,
 								type: 'success',
 								timer: 1000,
-								showConfirmButton: false
 							}).then(() => window.location.href = '/ticket')
 						},
 						error: function () {

+ 102 - 8
resources/views/admin/ticket/ticketList.blade.php

@@ -8,17 +8,17 @@
 			<div class="panel-heading">
 				<h3 class="panel-title">工单列表</h3>
 				<div class="panel-actions">
-					<a href="/ticket/create" class="btn btn-primary btn-animate btn-animate-side">
-						<span><i class="icon wb-plus" aria-hidden="true"></i> {{trans('home.ticket_table_new_button')}}
+					<button class="btn btn-primary btn-animate btn-animate-side" data-toggle="modal" data-target="#add_ticket_modal">
+						<span>
+							<i class="icon wb-plus" aria-hidden="true"></i> {{trans('home.ticket_table_new_button')}}
 						</span>
-					</a>
+					</button>
 				</div>
 			</div>
 			<div class="panel-body">
 				<div class="form-row">
 					<div class="form-group col-lg-3 col-sm-6">
-						<input type="text" class="form-control" name="email" id="email"
-								value="{{Request::get('email')}}" placeholder="用户名" autocomplete="off"/>
+						<input type="text" class="form-control" name="email" id="email" value="{{Request::get('email')}}" placeholder="用户名" autocomplete="off"/>
 					</div>
 					<div class="form-group col-lg-2 col-sm-6 btn-group">
 						<button class="btn btn-primary" onclick="Search()">搜 索</button>
@@ -32,6 +32,7 @@
 						<th> 用户名</th>
 						<th> 标题</th>
 						<th> 状态</th>
+						<th> 操作</th>
 					</tr>
 					</thead>
 					<tbody>
@@ -42,17 +43,27 @@
 								@if(!$ticket->user)
 									【账号已删除】
 								@else
-									<a href="/admin/userList?id={{$ticket->user->id}}"
-											target="_blank">{{$ticket->user->email}}</a>
+									<a href="/admin/userList?id={{$ticket->user->id}}" target="_blank">{{$ticket->user->email}}</a>
 								@endif
 							</td>
 
 							<td>
-								<a href="/ticket/reply?id={{$ticket->id}}" target="_blank">{{$ticket->title}}</a>
+								{{$ticket->title}}
 							</td>
 							<td>
 								{!!$ticket->status_label!!}
 							</td>
+							<td>
+								<a href="/ticket/reply?id={{$ticket->id}}" class="btn btn-animate btn-animate-vertical btn-outline-info">
+									<span>
+										@if($ticket->status == 2)
+											<i class="icon wb-eye" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_table_view')}}
+										@else
+											<i class="icon wb-check" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_open')}}
+										@endif
+									</span>
+								</a>
+							</td>
 						</tr>
 					@endforeach
 					</tbody>
@@ -72,6 +83,41 @@
 			</div>
 		</div>
 	</div>
+
+	<div id="add_ticket_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-keyboard="false">
+		<div class="modal-dialog modal-simple modal-center modal-lg">
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal" aria-label="Close">
+						<span aria-hidden="true">×</span>
+					</button>
+					<h4 class="modal-title"> {{trans('home.ticket_table_new_button')}} </h4>
+				</div>
+				<div class="modal-body">
+					<div class="form-group row">
+						<label for="domain" class="col-2 col-form-label">域名</label>
+						<div class="input-group col-10">
+							<input type="number" class="form-control col-md-4" name="user_id" id="user_id" placeholder="用户ID"/>
+							<div class="input-group-prepend">
+								<span class="input-group-text">或</span>
+							</div>
+							<input type="email" class="form-control col-md-8" name="user_email" id="user_email" placeholder="用户邮箱"/>
+						</div>
+					</div>
+					<div class="form-group">
+						<input type="text" class="form-control" name="title" id="title" placeholder="标题">
+					</div>
+					<div class="form-group">
+						<textarea type="text" class="form-control" rows="5" name="content" id="content" placeholder="内容"></textarea>
+					</div>
+				</div>
+				<div class="modal-footer">
+					<button type="button" data-dismiss="modal" class="btn btn-danger"> {{trans('home.ticket_cancel')}} </button>
+					<button type="button" data-dismiss="modal" class="btn btn-success" onclick="createTicket()"> {{trans('home.ticket_confirm')}} </button>
+				</div>
+			</div>
+		</div>
+	</div>
 @endsection
 @section('script')
 	<script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
@@ -90,5 +136,53 @@
 		function Search() {
 			window.location.href = '/ticket?email=' + $("#email").val();
 		}
+
+		// 发起工单
+		function createTicket() {
+			const id = $('#user_id').val();
+			const email = $('#user_email').val();
+			const title = $('#title').val();
+			const content = $('#content').val();
+
+			if (id.trim() === '' && email.trim() === '') {
+				swal.fire({title: '请填入目标用户信息!', type: 'warning'});
+				return false;
+			}
+
+			if (title.trim() === '') {
+				swal.fire({title: '您未填写工单标题!', type: 'warning'});
+				return false;
+			}
+
+			if (content.trim() === '') {
+				swal.fire({title: '您未填写工单内容!', type: 'warning'});
+				return false;
+			}
+
+			swal.fire({
+				title: '确定提交工单?',
+				type: 'question',
+				showCancelButton: true,
+				cancelButtonText: '{{trans('home.ticket_close')}}',
+				confirmButtonText: '{{trans('home.ticket_confirm')}}',
+			}).then((result) => {
+				if (result.value) {
+					$.post("/ticket/create", {
+						_token: '{{csrf_token()}}',
+						id: id,
+						email: email,
+						title: title,
+						content: content
+					}, function (ret) {
+						if (ret.status === 'success') {
+							swal.fire({title: ret.message, type: 'success', timer: 1000, showConfirmButton: false})
+								.then(() => window.location.reload())
+						} else {
+							swal.fire({title: ret.message, type: "error"}).then(() => window.location.reload())
+						}
+					});
+				}
+			})
+		}
 	</script>
 @endsection

+ 7 - 0
resources/views/components/avatar.blade.php

@@ -0,0 +1,7 @@
+@if($user->qq)
+	<img src="http://q1.qlogo.cn/g?b=qq&nk={{$user->qq}}&s=640" alt="头像">
+@elseif(strpos(strtolower($user->email),"@qq.com") !== false)
+	<img src="http://q1.qlogo.cn/g?b=qq&nk={{$user->email}}&s=640" alt="头像">
+@else
+	<img src="/assets/images/avatar.svg" alt="头像">
+@endif

+ 15 - 0
resources/views/components/chatUnit.blade.php

@@ -0,0 +1,15 @@
+<div class="chat @if(($ticket->admin && !Auth::getUser()->is_admin) ||(!$ticket->admin && Auth::getUser()->is_admin)) chat-left @endif">
+	<div class="chat-avatar">
+		<p class="avatar" data-toggle="tooltip" href="#" data-placement="right" title="" data-original-title="{{($ticket->admin?:$ticket->user)->email}}">
+			@component('components.avatar',['user' => $ticket->admin?:$ticket->user])@endcomponent
+		</p>
+	</div>
+	<div class="chat-body">
+		<div class="chat-content">
+			<p>
+				{!! $ticket->content !!}
+			</p>
+			<time class="chat-time">{{$ticket->created_at}}</time>
+		</div>
+	</div>
+</div>

+ 0 - 7
resources/views/user/components/avatar.blade.php

@@ -1,7 +0,0 @@
-@if(Auth::getUser()->qq)
-	<img src="http://q1.qlogo.cn/g?b=qq&nk={{Auth::getUser()->qq}}&s=640" alt="{{trans('home.ticket_reply_me')}}">
-@elseif(strpos(strtolower(Auth::getUser()->email),"@qq.com") !== FALSE)
-	<img src="http://q1.qlogo.cn/g?b=qq&nk={{Auth::getUser()->email}}&s=640" alt="{{trans('home.ticket_reply_me')}}">
-@else
-	<img src="/assets/images/avatar.svg" alt="{{trans('home.ticket_reply_me')}}">
-@endif

+ 1 - 1
resources/views/user/layouts.blade.php

@@ -112,7 +112,7 @@
 					<a href="#" aria-expanded="false" class="nav-link navbar-avatar" data-animation="scale-up"
 							data-toggle="dropdown" role="button">
 						<span class="avatar avatar-online">
-							@include('user.components.avatar')
+							@component('components.avatar',['user' => Auth::getUser()])@endcomponent
 							<i></i>
 						</span>
 					</a>

+ 21 - 41
resources/views/user/profile.blade.php

@@ -6,17 +6,15 @@
 				<div class="card">
 					<div class="card-header white bg-cyan-600 p-30 clearfix">
 						<span class="avatar avatar-100 float-left mr-20">
-							@include('user.components.avatar')
+							@component('components.avatar',['user' => Auth::getUser()])@endcomponent
 						</span>
 						<div class="float-left">
 							<div class="font-size-20 mb-15">{{Auth::getUser()->username}}</div>
 							<p class="mb-5 text-nowrap"><i class="icon bd-webchat mr-10" aria-hidden="true"></i>
-								<span class="text-break">微信: @if(Auth::getUser()->wechat) {{Auth::getUser()->wechat}} @else
-								                         未添加 @endif</span>
+								<span class="text-break">微信: @if(Auth::getUser()->wechat) {{Auth::getUser()->wechat}} @else未添加 @endif</span>
 							</p>
 							<p class="mb-5 text-nowrap"><i class="icon bd-qq mr-10" aria-hidden="true"></i>
-								<span class="text-break">QQ: @if(Auth::getUser()->qq) {{Auth::getUser()->qq}} @else
-								                         未添加 @endif</span>
+								<span class="text-break">QQ: @if(Auth::getUser()->qq) {{Auth::getUser()->qq}} @else未添加 @endif</span>
 							</p>
 						</div>
 					</div>
@@ -27,32 +25,27 @@
 					@if (Session::has('successMsg'))
 						<div class="alert alert-success alert-dismissable">
 							<button class="close" data-dismiss="alert" aria-label="Close">
-								<span aria-hidden="true">&times;</span><span
-										class="sr-only">Close</span></button>
+								<span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
 							{{Session::get('successMsg')}}
 						</div>
 					@endif
 					@if($errors->any())
 						<div class="alert alert-danger alert-dismissable">
 							<button class="close" data-dismiss="alert" aria-label="Close">
-								<span aria-hidden="true">&times;</span><span
-										class="sr-only">Close</span></button>
+								<span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
 							<strong>{{trans('home.error')}}:</strong> {{$errors->first()}}
 						</div>
 					@endif
 					<div class="panel-body nav-tabs-animate nav-tabs-horizontal" data-plugin="tabs">
 						<ul class="nav nav-tabs nav-tabs-line" role="tablist">
 							<li class="nav-item" role="presentation">
-								<a class="active nav-link" data-toggle="tab" href="#tab_1" aria-controls="tab_1"
-										role="tab">{{trans('home.password')}}</a>
+								<a class="active nav-link" data-toggle="tab" href="#tab_1" aria-controls="tab_1" role="tab">{{trans('home.password')}}</a>
 							</li>
 							<li class="nav-item" role="presentation">
-								<a class="nav-link" data-toggle="tab" href="#tab_2" aria-controls="tab_2"
-										role="tab">{{trans('home.contact')}}</a>
+								<a class="nav-link" data-toggle="tab" href="#tab_2" aria-controls="tab_2" role="tab">{{trans('home.contact')}}</a>
 							</li>
 							<li class="nav-item" role="presentation">
-								<a class="nav-link" data-toggle="tab" href="#tab_3" aria-controls="tab_3"
-										role="tab">{{trans('home.ssr_setting')}}</a>
+								<a class="nav-link" data-toggle="tab" href="#tab_3" aria-controls="tab_3" role="tab">{{trans('home.ssr_setting')}}</a>
 							</li>
 						</ul>
 						<div class="tab-content py-10">
@@ -60,17 +53,13 @@
 								<form action="/profile" method="post" enctype="multipart/form-data"
 										class="form-horizontal" autocomplete="off">
 									<div class="form-group row">
-										<label for="old_password"
-												class="col-md-2 col-form-label">{{trans('home.current_password')}}</label>
-										<input type="password" class="form-control col-md-5 round" name="old_password"
-												id="old_password" autofocus required/>
+										<label for="old_password" class="col-md-2 col-form-label">{{trans('home.current_password')}}</label>
+										<input type="password" class="form-control col-md-5 round" name="old_password" id="old_password" autofocus required/>
 										{{csrf_field()}}
 									</div>
 									<div class="form-group row">
-										<label for="new_password"
-												class="col-md-2  col-form-label">{{trans('home.new_password')}}</label>
-										<input type="password" class="form-control col-md-5 round" name="new_password"
-												id="new_password" required/>
+										<label for="new_password" class="col-md-2  col-form-label">{{trans('home.new_password')}}</label>
+										<input type="password" class="form-control col-md-5 round" name="new_password" id="new_password" required/>
 									</div>
 									<div class="form-actions">
 										<button type="submit" class="btn btn-info">{{trans('home.submit')}}</button>
@@ -78,25 +67,19 @@
 								</form>
 							</div>
 							<div class="tab-pane animation-slide-left" id="tab_2" role="tabpanel">
-								<form action="/profile" method="post" enctype="multipart/form-data"
-										class="form-horizontal">
+								<form action="/profile" method="post" enctype="multipart/form-data" class="form-horizontal">
 									<div class="form-group row">
-										<label for="username"
-												class="col-md-2 col-form-label">{{trans('auth.username')}}</label>
-										<input type="text" class="form-control col-md-5 round" name="username"
-												id="username" value="{{Auth::getUser()->username}}"/>
+										<label for="username" class="col-md-2 col-form-label">{{trans('auth.username')}}</label>
+										<input type="text" class="form-control col-md-5 round" name="username" id="username" value="{{Auth::getUser()->username}}"/>
 									</div>
 									<div class="form-group row">
-										<label for="wechat"
-												class="col-md-2 col-form-label">{{trans('home.wechat')}}</label>
-										<input type="text" class="form-control col-md-5 round" name="wechat" id="wechat"
-												value="{{Auth::getUser()->wechat}}"/>
+										<label for="wechat" class="col-md-2 col-form-label">{{trans('home.wechat')}}</label>
+										<input type="text" class="form-control col-md-5 round" name="wechat" id="wechat" value="{{Auth::getUser()->wechat}}"/>
 										{{csrf_field()}}
 									</div>
 									<div class="form-group row">
 										<label for="qq" class="col-md-2 col-form-label">QQ</label>
-										<input type="number" class="form-control col-md-5 round" name="qq" id="qq"
-												value="{{Auth::getUser()->qq}}"/>
+										<input type="number" class="form-control col-md-5 round" name="qq" id="qq" value="{{Auth::getUser()->qq}}"/>
 									</div>
 									<div class="form-actions">
 										<button type="submit" class="btn btn-info">{{trans('home.submit')}}</button>
@@ -104,13 +87,10 @@
 								</form>
 							</div>
 							<div class="tab-pane animation-slide-left" id="tab_3" role="tabpanel">
-								<form action="/profile" method="post" enctype="multipart/form-data"
-										class="form-horizontal">
+								<form action="/profile" method="post" enctype="multipart/form-data" class="form-horizontal">
 									<div class="form-group row">
-										<label for="passwd"
-												class="col-md-2 col-form-label"> {{trans('home.connection_password')}} </label>
-										<input type="text" class="form-control col-md-5 round" name="passwd" id="passwd"
-												value="{{Auth::getUser()->passwd}}" required/>
+										<label for="passwd" class="col-md-2 col-form-label"> {{trans('home.connection_password')}} </label>
+										<input type="text" class="form-control col-md-5 round" name="passwd" id="passwd" value="{{Auth::getUser()->passwd}}" required/>
 										{{csrf_field()}}
 									</div>
 									<div class="form-actions">

+ 9 - 49
resources/views/user/replyTicket.blade.php

@@ -6,7 +6,7 @@
 	<div class="page-content">
 		<div class="panel panel-bordered">
 			<div class="panel-heading">
-				<h1 class="panel-title cyan-600"><i class="icon wb-help-circle"></i>{{$ticket->title}}</h1>
+				<h1 class="panel-title cyan-600"><i class="icon wb-help-circle"></i> {{$ticket->title}} </h1>
 				@if($ticket->status != 2)
 					<div class="panel-actions">
 						<button class="btn btn-danger" onclick="closeTicket()"> {{trans('home.ticket_close')}} </button>
@@ -16,47 +16,9 @@
 			<div class="panel-body">
 				<div class="chat-box">
 					<div class="chats">
-						<div class="chat">
-							<div class="chat-avatar">
-								<p class="avatar" data-toggle="tooltip" data-placement="right"
-										title="{{trans('home.ticket_reply_me')}}">
-									@include('user.components.avatar')
-								</p>
-							</div>
-							<div class="chat-body">
-								<div class="chat-content">
-									<p>
-										{!! $ticket->content !!}
-									</p>
-									<time class="chat-time">{{$ticket->created_at}}</time>
-								</div>
-							</div>
-						</div>
+						@component('components.chatUnit',['ticket'=>$ticket])@endcomponent
 						@foreach ($replyList as $reply)
-							<div class="chat @if($reply->user->is_admin) chat-left @endif">
-								<div class="chat-avatar">
-									@if ($reply->user->is_admin)
-										<p class="avatar" data-toggle="tooltip" data-placement="left"
-												title="{{trans('home.ticket_reply_master')}}">
-											<img src="/assets/images/logo64.png"
-													alt="{{trans('home.ticket_reply_master')}}"/>
-										</p>
-									@else
-										<p class="avatar" data-toggle="tooltip" data-placement="left"
-												title="{{trans('home.ticket_reply_me')}}">
-											@include('user.components.avatar')
-										</p>
-									@endif
-								</div>
-								<div class="chat-body">
-									<div class="chat-content">
-										<p>
-											{!! $reply->content!!}
-										</p>
-										<time class="chat-time">{{$reply->created_at}}</time>
-									</div>
-								</div>
-							</div>
+							@component('components.chatUnit',['ticket'=>$reply])@endcomponent
 						@endforeach
 					</div>
 				</div>
@@ -65,11 +27,9 @@
 				<div class="panel-footer pb-30">
 					<form>
 						<div class="input-group">
-							<input type="text" class="form-control" id="editor"
-									placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
+							<input type="text" class="form-control" id="editor" placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
 							<span class="input-group-btn">
-								<button type="button" class="btn btn-primary"
-										onclick="replyTicket()"> {{trans('home.ticket_reply')}}</button>
+								<button type="button" class="btn btn-primary" onclick="replyTicket()"> {{trans('home.ticket_reply')}}</button>
 							</span>
 						</div>
 					</form>
@@ -93,7 +53,7 @@
 			swal.fire({
 				title: '{{trans('home.ticket_close_title')}}',
 				text: '{{trans('home.ticket_close_content')}}',
-				type: 'warning',
+				type: 'question',
 				showCancelButton: true,
 				cancelButtonText: '{{trans('home.ticket_close')}}',
 				confirmButtonText: '{{trans('home.ticket_confirm')}}',
@@ -115,7 +75,7 @@
 						error: function () {
 							swal.fire("未知错误!请通知客服!")
 						}
-					});
+					})
 				}
 			})
 		}
@@ -125,7 +85,7 @@
 			const content = document.getElementById('editor').value;
 
 			if (content.trim() === '') {
-				swal.fire({title: '您未填写工单内容!', type: 'warning'});
+				swal.fire({title: '您未填写工单内容!', type: 'warning', timer: 1500});
 				return false;
 			}
 			swal.fire({
@@ -146,7 +106,7 @@
 							swal.fire({
 								title: ret.message,
 								type: 'success',
-								timer: 800,
+								timer: 1000,
 								showConfirmButton: false
 							}).then(() => window.location.reload())
 						} else {

+ 19 - 29
resources/views/user/ticketList.blade.php

@@ -8,13 +8,13 @@
 			<div class="col-lg-8 order-lg-1 order-2">
 				<div class="panel panel-bordered">
 					<div class="panel-heading p-20">
-						<h1 class="panel-title cyan-600"><i
-									class="icon wb-user-circle"></i>{{trans('home.ticket_title')}}
+						<h1 class="panel-title cyan-600">
+							<i class="icon wb-user-circle"></i>{{trans('home.ticket_title')}}
 						</h1>
 						<div class="panel-actions">
-							<button class="btn btn-primary btn-animate btn-animate-side" data-toggle="modal"
-									data-target="#add_ticket_modal">
-								<span><i class="icon wb-plus" aria-hidden="true"></i> {{trans('home.ticket_table_new_button')}}
+							<button class="btn btn-primary btn-animate btn-animate-side" data-toggle="modal" data-target="#add_ticket_modal">
+								<span>
+									<i class="icon wb-plus" aria-hidden="true"></i> {{trans('home.ticket_table_new_button')}}
 								</span>
 							</button>
 						</div>
@@ -35,23 +35,17 @@
 									<tr>
 										<td>{{$ticket->id}}</td>
 										<td>{{$ticket->title}}</td>
+										<td>{!!$ticket->status_label!!}</td>
 										<td>
-											{!!$ticket->status_label!!}
-										</td>
-										<td>
-											@if($ticket->status == 2)
-												<a href="/replyTicket?id={{$ticket->id}}"
-														class="btn btn-animate btn-animate-vertical btn-outline-info">
-													<span><i class="icon wb-eye" aria-hidden="true"
-																style="left: 40%"> </i> {{trans('home.ticket_table_view')}}
-													</span></a>
-											@else
-												<a href="/replyTicket?id={{$ticket->id}}"
-														class="btn btn-animate btn-animate-vertical btn-outline-success">
-													<span><i class="icon wb-check" aria-hidden="true"
-																style="left: 40%"></i> {{trans('home.ticket_open')}}
-													</span></a>
-											@endif
+											<a href="/replyTicket?id={{$ticket->id}}" class="btn btn-animate btn-animate-vertical btn-outline-info">
+												<span>
+													@if($ticket->status == 2)
+														<i class="icon wb-eye" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_table_view')}}
+													@else
+														<i class="icon wb-check" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_open')}}
+													@endif
+												</span>
+											</a>
 										</td>
 									</tr>
 								@endforeach
@@ -119,20 +113,16 @@
 				<div class="modal-body">
 					<div class="row">
 						<div class="col-xl-12 form-group">
-							<input type="text" class="form-control" name="title" id="title"
-									placeholder="{{trans('home.ticket_table_new_title')}}">
+							<input type="text" class="form-control" name="title" id="title" placeholder="{{trans('home.ticket_table_new_title')}}">
 						</div>
 						<div class="col-xl-12 form-group">
-                            <textarea class="form-control" rows="5" name="content" id="content"
-		                            placeholder="{{trans('home.ticket_table_content')}}"></textarea>
+							<textarea class="form-control" rows="5" name="content" id="content" placeholder="{{trans('home.ticket_table_content')}}"></textarea>
 						</div>
 					</div>
 				</div>
 				<div class="modal-footer">
-					<button type="button" data-dismiss="modal"
-							class="btn btn-danger"> {{trans('home.ticket_cancel')}} </button>
-					<button type="button" data-dismiss="modal" class="btn btn-success"
-							onclick="createTicket()"> {{trans('home.ticket_confirm')}} </button>
+					<button type="button" data-dismiss="modal" class="btn btn-danger"> {{trans('home.ticket_cancel')}} </button>
+					<button type="button" data-dismiss="modal" class="btn btn-success" onclick="createTicket()"> {{trans('home.ticket_confirm')}} </button>
 				</div>
 			</div>
 		</div>

+ 1 - 2
routes/web.php

@@ -105,8 +105,7 @@ Route::group(['middleware' => ['isForbidden', 'isAdminLogin', 'isAdmin']], funct
 	Route::group(['namespace' => 'Admin'], function() {
 		Route::group(['prefix' => 'ticket'], function() {
 			Route::get('/', 'TicketController@ticketList'); // 工单列表
-			// Todo: 创建工单
-			// Route::post('create', 'TicketController@createTicket'); // 创建工单
+			Route::post('create', 'TicketController@createTicket'); // 创建工单
 			Route::post('close', 'TicketController@closeTicket'); // 关闭工单
 			Route::any('reply', 'TicketController@replyTicket'); // 回复工单
 		});

+ 4 - 2
sql/db.sql

@@ -675,6 +675,7 @@ CREATE TABLE `ticket`
 (
     `id`         INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
     `user_id`    INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '用户ID',
+    `admin_id`   INT(10) UNSIGNED          DEFAULT '0' COMMENT '管理员ID',
     `title`      VARCHAR(255)     NOT NULL DEFAULT '' COMMENT '标题',
     `content`    TEXT             NOT NULL COMMENT '内容',
     `status`     TINYINT(1)       NOT NULL DEFAULT '0' COMMENT '状态:0-待处理、1-已处理未关闭、2-已关闭',
@@ -691,7 +692,8 @@ CREATE TABLE `ticket_reply`
 (
     `id`         INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
     `ticket_id`  INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '工单ID',
-    `user_id`    INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '回复用户的ID',
+    `user_id`    INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '回复用户ID',
+    `admin_id`   INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '管理员ID',
     `content`    TEXT             NOT NULL COMMENT '回复内容',
     `created_at` DATETIME                  DEFAULT NULL COMMENT '创建时间',
     `updated_at` DATETIME                  DEFAULT NULL COMMENT '最后更新时间',
@@ -1300,7 +1302,7 @@ VALUES (1, '1', '360',
         '(.*\.||)(youtube|googlevideo|hulu|netflix|nflxvideo|akamai|nflximg|hbo|mtv|bbc|tvb)\.(org|club|com|net|tv)',
         '2019-11-19 15:04:11', '2019-11-19 15:04:11'),
        (17, '1', '测速类', '(.*\.||)(fast|speedtest)\.(org|com|net|cn)', '2019-11-19 15:04:11', '2019-11-19 15:04:11'),
-       (17, '1', '涉洗钱', '(.*\.||)(metatrader4|metatrader5|mql5)\.(org|com|net)', '2020-7-9 14:25:11', '2020-7-9 14:25:11');
+       (18, '1', '涉洗钱', '(.*\.||)(metatrader4|metatrader5|mql5)\.(org|com|net)', '2020-7-9 14:25:11', '2020-7-9 14:25:11');
 
 -- ----------------------------
 -- Table structure for rule_group

+ 7 - 0
sql/mod/20200711.sql

@@ -0,0 +1,7 @@
+ALTER TABLE `ticket`
+    ADD `admin_id` INT(10) UNSIGNED DEFAULT '0' COMMENT '管理员ID' AFTER `user_id`;
+
+ALTER TABLE `ticket_reply`
+    ADD `admin_id` INT(10) UNSIGNED DEFAULT '0' COMMENT '管理员ID' AFTER `user_id`;
+
+--升级sql后 运行 php artisan updateTicket 更新工单