diff --git a/views/admin/ticket_index.php b/views/admin/ticket_index.php
new file mode 100644
index 0000000..a7f8b81
--- /dev/null
+++ b/views/admin/ticket_index.php
@@ -0,0 +1,127 @@
+title = '工单支持管理';
+$this->params['breadcrumbs'][] = $this->title;
+FontAwesomeAsset::register($this);
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ [
+ 'attribute' => 'id',
+ 'label' => '工单ID',
+ ],
+ [
+ 'attribute' => 'user_id',
+ 'label' => '用户',
+ 'format' => 'html',
+ 'value' => function (Tickets $model) {
+ return $model->user->username . '
(ID: ' . $model->user->id . ')';
+ }
+ ],
+ [
+ 'attribute' => 'title',
+ 'label' => '标题',
+ 'format' => 'raw', // 使用 raw 格式,这样 Yii2 不会对 value 的返回值进行 HTML 编码
+ 'value' => function (Tickets $model) {
+ return Html::a($model->title, ['ticket-view', 'id' => $model->id]);
+ },
+ ],
+ [
+ 'attribute' => 'created_at',
+ 'label' => '创建时间',
+ 'filter' => false
+ ],
+ [
+ 'attribute' => 'updated_at',
+ 'label' => '最近更新时间',
+ 'value' => function (Tickets $model) {
+ return Yii::$app->formatter->asRelativeTime(new DateTime($model->updated_at, new DateTimeZone('GMT+8')));
+ },
+ 'filter' => false
+ ],
+ [
+ 'attribute' => 'status',
+ 'label' => '状态',
+ 'format' => 'raw',
+ 'value' => function (Tickets $model) {
+ return match ($model->status) {
+ Tickets::STATUS_OPEN => '工单已开启',
+ Tickets::STATUS_ADMIN_REPLY => '管理员已回复',
+ Tickets::STATUS_USER_REPLY => '用户已回复',
+ Tickets::STATUS_CLOSED => '工单已关闭',
+ default => '未知状态',
+ };
+ },
+ 'filter' => [
+ Tickets::STATUS_OPEN => '工单已开启',
+ Tickets::STATUS_ADMIN_REPLY => '管理员已回复',
+ Tickets::STATUS_USER_REPLY => '用户已回复',
+ Tickets::STATUS_CLOSED => '工单已关闭',
+ ]
+ ],
+ [
+ 'class' => ActionColumn::class,
+ 'template' => '{view} {delete}',
+ 'buttons' => [
+ 'view' => function ($url, $model, $key) {
+ return Html::a('', ['ticket-view', 'id' => $model->id], [
+ 'title' => '查看工单',
+ 'data-pjax' => '0',
+ ]);
+ },
+ 'delete' => function ($url, $model, $key) {
+ if ($model->status !== Tickets::STATUS_CLOSED) {
+ return Html::a('', ['ticket-delete', 'id' => $model->id, 'from' => 'index'], [
+ 'title' => '关闭工单',
+ 'data-pjax' => '0',
+ 'data-confirm' => '你确定要关闭这个工单吗?',
+ 'data-method' => 'post',
+ ]);
+ } else {
+ return Html::tag('i', '', [
+ 'class' => 'fa-solid fa-xmark',
+ 'style' => 'color: gray;',
+ 'title' => '工单已关闭',
+ ]);
+ }
+ },
+ ],
+ 'urlCreator' => function ($action, Tickets $model, $key, $index, $column) {
+ return Url::toRoute([$action, 'id' => $model->id]);
+ },
+ ],
+ ],
+ ]); ?>
+
+
+
+
diff --git a/views/admin/ticket_view.php b/views/admin/ticket_view.php
new file mode 100644
index 0000000..22c2c5e
--- /dev/null
+++ b/views/admin/ticket_view.php
@@ -0,0 +1,170 @@
+title = '工单: ' . $model->title;
+$this->params['breadcrumbs'][] = ['label' => '工单支持管理', 'url' => ['ticket-manage']];
+$this->params['breadcrumbs'][] = '工单ID ' . $model->id;
+YiiAsset::register($this);
+QuillAsset::register($this);
+FontAwesomeAsset::register($this);
+$this->registerCssFile('@web/css/tickets.css');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'id',
+ [
+ 'attribute' => 'status',
+ 'label' => '状态',
+ 'format' => 'raw',
+ 'value' => function (Tickets $model) {
+ return match ($model->status) {
+ Tickets::STATUS_OPEN => '
工单已开启',
+ Tickets::STATUS_ADMIN_REPLY => '
管理员已回复',
+ Tickets::STATUS_USER_REPLY => '
用户已回复',
+ Tickets::STATUS_CLOSED => '
工单已关闭',
+ default => '
未知状态',
+ };
+ }
+ ],
+ [
+ 'attribute' => 'user_id',
+ 'label' => '用户',
+ 'format' => 'html',
+ 'value' => function (Tickets $model) {
+ return $model->user->username . '
(ID: ' . $model->user->id . ')';
+ }
+ ],
+ [
+ 'attribute' => 'ip',
+ 'label' => '用户IP',
+ ],
+ [
+ 'attribute' => 'created_at',
+ 'label' => '创建时间',
+ 'format' => 'raw', // 使用 raw 格式,这样 Yii2 不会对 value 的返回值进行 HTML 编码
+ 'value' => function (Tickets $model) {
+ $dateTime = new DateTime($model->created_at, new DateTimeZone('GMT+8'));
+ return $model->created_at . '
(' . Yii::$app->formatter->asRelativeTime($dateTime) . ')';
+ }
+ ],
+ [
+ 'attribute' => 'updated_at',
+ 'label' => '最近更新时间',
+ 'format' => 'raw', // 使用 raw 格式,这样 Yii2 不会对 value 的返回值进行 HTML 编码
+ 'value' => function (Tickets $model) {
+ $dateTime = new DateTime($model->updated_at, new DateTimeZone('GMT+8'));
+ return $model->updated_at . '
(' . Yii::$app->formatter->asRelativeTime($dateTime) . ')';
+ }
+ ]
+ ],
+ ]) ?>
+
+ = ($model->status === Tickets::STATUS_CLOSED) ? '' : Html::a('关闭工单', ['ticket-delete', 'id' => $model->id], ['class' => 'btn btn-danger',
+ 'data' => ['confirm' => '您确定要关闭这个工单吗?',
+ 'method' => 'post',],]) ?>
+
+
+
+
+
+
+user->username;
+$core_js = <<id; // 你需要在这里设置正确的工单ID
+
+ $.ajax({
+ url: 'index.php?r=admin%2Fticket-reply',
+ type: 'POST',
+ data: {
+ ticketId: ticketId,
+ content: JSON.stringify(content)
+ },
+ success: function(response) {
+ // 处理服务器的响应
+ console.log(response);
+ // 如果服务器返回的状态是成功,刷新页面
+ if (response.status === 'success') {
+ location.reload();
+ }
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ // 处理错误
+ console.error(textStatus, errorThrown);
+ }
+ });
+ });
+ function quillGetHTML(inputDelta,skipParse=false) {
+ var delta = skipParse?inputDelta:JSON.parse(inputDelta);
+ var tempQuill=new Quill(document.createElement("div"));
+ tempQuill.setContents(delta);
+ return tempQuill.root.innerHTML;
+ }
+ function generateReply(user, message, date,ip) {
+ return '';
+ }
+ function generateAdminReply(user, message, date,ip) {
+ return '';
+ }
+ var ticketContent = $model->description;
+ var ticketContentElement = $('#ticket-content');
+ ticketContentElement.append(generateReply('$user_name', quillGetHTML(ticketContent,true), '$model->created_at','$model->ip'));
+ var TicketReplies = $ticketReplies;
+ for (let i = 0; i < TicketReplies.length; i++) {
+ let reply = TicketReplies[i];
+ let message = quillGetHTML(reply.message);
+ let date = reply.created_at;
+ let ip = reply.ip;
+ if (reply.is_admin !== 1) {
+ ticketContentElement.append(generateReply(reply.name, message, date,ip));
+ } else {
+ ticketContentElement.append(generateAdminReply(reply.name, message, date,ip));
+ }
+ }
+
+JS;
+
+$this->registerJs($core_js);
+?>
\ No newline at end of file