用户管理功能(1/5)

This commit is contained in:
Chenx221 2024-03-22 14:48:35 +08:00
parent 96a5dcb9a5
commit 3928763418
Signed by: chenx221
GPG Key ID: D7A9EC07024C3021
8 changed files with 367 additions and 12 deletions

View File

@ -2,10 +2,15 @@
namespace app\controllers; namespace app\controllers;
use Yii; use app\models\User;
use app\models\UserSearch;
use Throwable;
use yii\db\StaleObjectException;
use yii\filters\AccessControl; use yii\filters\AccessControl;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\Response;
class AdminController extends Controller class AdminController extends Controller
{ {
@ -22,7 +27,7 @@ class AdminController extends Controller
'rules' => [ 'rules' => [
[ [
'allow' => true, 'allow' => true,
'actions' => ['index', 'system', 'user', 'info'], 'actions' => ['index', 'system', 'user', 'info', 'user-view', 'user-create', 'user-update', 'user-delete'],
'roles' => ['admin'], // only admin can do these 'roles' => ['admin'], // only admin can do these
] ]
], ],
@ -33,6 +38,10 @@ class AdminController extends Controller
'index' => ['GET'], 'index' => ['GET'],
'system' => ['GET'], 'system' => ['GET'],
'user' => ['GET'], 'user' => ['GET'],
'user-view' => ['GET'],
'user-create' => ['GET', 'POST'],
'user-update' => ['GET', 'POST'],
'user-delete' => ['POST'],
'info' => ['GET'], 'info' => ['GET'],
], ],
], ],
@ -63,11 +72,106 @@ class AdminController extends Controller
} }
/** /**
* Lists all User.
*
* @return string * @return string
*/ */
public function actionUser(): string public function actionUser(): string
{ {
return $this->render('user'); $searchModel = new UserSearch();
$dataProvider = $searchModel->search($this->request->queryParams);
return $this->render('user', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single User model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUserView(int $id): string
{
return $this->render('user_view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new User model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return string|Response
*/
public function actionUserCreate(): Response|string
{
$model = new User();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model->save()) {
return $this->redirect(['user_view', 'id' => $model->id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('user_create', [
'model' => $model,
]);
}
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param int $id ID
* @return string|Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUserUpdate(int $id): Response|string
{
$model = $this->findModel($id);
if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) {
return $this->redirect(['user_view', 'id' => $model->id]);
}
return $this->render('user_update', [
'model' => $model,
]);
}
/**
* Deletes an existing User model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param int $id ID
* @return Response
* @throws NotFoundHttpException if the model cannot be found
* @throws Throwable
* @throws StaleObjectException
*/
public function actionUserDelete(int $id): Response
{
$this->findModel($id)->delete();
return $this->redirect(['user']);
}
/**
* Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param int $id ID
* @return User the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel(int $id): User
{
if (($model = User::findOne(['id' => $id])) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
} }
/** /**

View File

@ -16,8 +16,8 @@ class UserSearch extends User
public function rules(): array public function rules(): array
{ {
return [ return [
[['id', 'status'], 'integer'], [['id', 'status', 'is_encryption_enabled', 'is_otp_enabled', 'storage_limit', 'dark_mode'], 'integer'],
[['username', 'password', 'auth_key', 'email'], 'safe'], [['username', 'name', 'password', 'auth_key', 'email', 'created_at', 'last_login', 'last_login_ip', 'bio', 'role', 'encryption_key', 'otp_secret', 'recovery_codes', 'vault_secret', 'vault_salt'], 'safe'],
]; ];
} }
@ -59,12 +59,27 @@ class UserSearch extends User
$query->andFilterWhere([ $query->andFilterWhere([
'id' => $this->id, 'id' => $this->id,
'status' => $this->status, 'status' => $this->status,
'created_at' => $this->created_at,
'last_login' => $this->last_login,
'is_encryption_enabled' => $this->is_encryption_enabled,
'is_otp_enabled' => $this->is_otp_enabled,
'storage_limit' => $this->storage_limit,
'dark_mode' => $this->dark_mode,
]); ]);
$query->andFilterWhere(['like', 'username', $this->username]) $query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'password', $this->password]) ->andFilterWhere(['like', 'password', $this->password])
->andFilterWhere(['like', 'auth_key', $this->auth_key]) ->andFilterWhere(['like', 'auth_key', $this->auth_key])
->andFilterWhere(['like', 'email', $this->email]); ->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['like', 'last_login_ip', $this->last_login_ip])
->andFilterWhere(['like', 'bio', $this->bio])
->andFilterWhere(['like', 'role', $this->role])
->andFilterWhere(['like', 'encryption_key', $this->encryption_key])
->andFilterWhere(['like', 'otp_secret', $this->otp_secret])
->andFilterWhere(['like', 'recovery_codes', $this->recovery_codes])
->andFilterWhere(['like', 'vault_secret', $this->vault_secret])
->andFilterWhere(['like', 'vault_salt', $this->vault_salt]);
return $dataProvider; return $dataProvider;
} }

View File

@ -0,0 +1,42 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var app\models\User $model */
/** @var yii\widgets\ActiveForm $form */
?>
<div class="user-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username')->textInput(['maxlength' => true])->label('用户名') ?>
<?= $form->field($model, 'email')->input('email')->label('电子邮箱地址') ?>
<?= $form->field($model, 'password')->passwordInput(['maxlength' => true])->label('密码') ?>
<div class="form-group field-user-role">
<div id="user-role" class="form-check" role="radiogroup">
<input class="form-check-input" type="radio" name="User[role]" value="user" id="userRadio" checked>
<label class="form-check-label" for="userRadio">
用户
</label>
</div>
<div id="user-role" class="form-check" role="radiogroup">
<input class="form-check-input" type="radio" name="User[role]" value="admin" id="adminRadio">
<label class="form-check-label" for="adminRadio">
管理员
</label>
</div>
</div>
<div class="form-group">
<?= Html::submitButton('创建', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -0,0 +1,27 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var app\models\User $model */
/** @var yii\widgets\ActiveForm $form */
?>
<div class="user-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => true])->label('昵称') ?>
<?= $form->field($model, 'email')->input('email')->label('电子邮箱地址') ?>
<?= $form->field($model, 'password')->passwordInput(['maxlength' => true])->label('密码') ?>
<div class="form-group">
<?= Html::submitButton('创建', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -1,8 +1,77 @@
<?php <?php
use app\models\User;
use app\utils\FileSizeHelper;
use app\utils\IPLocation;
use yii\grid\ActionColumn;
use yii\grid\GridView;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\Pjax;
/** @var yii\web\View $this */ /** @var yii\web\View $this */
/** @var app\models\UserSearch $searchModel */
/** @var yii\data\ActiveDataProvider $dataProvider */
$IPLocation = new IPLocation();
$this->title = '用户管理';
$this->params['breadcrumbs'][] = $this->title;
?> ?>
<h1>admin/index</h1> <div class="user-index">
<h1><?= Html::encode($this->title) ?></h1>
<p> <p>
这里是管理员页面.建设中 <?= Html::a('创建用户', ['user-create'], ['class' => 'btn btn-success']) ?>
</p> </p>
<?php Pjax::begin(); ?>
<div class="table-responsive">
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\CheckboxColumn'],
['attribute' => 'id', 'label' => 'ID'],
['attribute' => 'username', 'label' => '用户名'],
['attribute' => 'name', 'label' => '昵称'],
['attribute' => 'email', 'format' => 'email', 'label' => '电子邮箱'],
['attribute' => 'status', 'label' => '账户启用', 'value' => function ($model) {
return $model->status == 0 ? '禁用' : '启用';
}, 'filter' => ['0' => '禁用', '1' => '启用']],
['attribute' => 'created_at', 'label' => '账户创建时间', 'filter' => false],
['attribute' => 'last_login', 'label' => '上次登陆时间', 'filter' => false],
['attribute' => 'last_login_ip', 'label' => '上次登录IP', 'value' => function ($model) use ($IPLocation) {
if (Yii::$app->params['enableIpInfo']) {
return $IPLocation->getFormatDetails($model->last_login_ip);
} else {
return $model->last_login_ip;
}
}, 'filter' => false],// 给这个加位置显示也许会更好但ipinfo那边就不好了
['attribute' => 'role', 'label' => '用户身份', 'value' => function ($model) {
return $model->role == 'user' ? '用户' : '管理员';
}, 'filter' => ['user' => '用户', 'admin' => '管理员']],
['attribute' => 'is_otp_enabled', 'label' => '多因素登录', 'value' => function ($model) {
return $model->is_otp_enabled == 0 ? '禁用' : '启用';
}, 'filter' => ['0' => '禁用', '1' => '启用']],
['attribute' => 'storage_limit', 'label' => '空间使用情况', 'value' => function ($model) {
if ($model->role == 'user') {
return FileSizeHelper::getFormatUserAllDirSize($model->id) . ' / ' . FileSizeHelper::formatMegaBytes($model->storage_limit);
} else {
return '不可用';
}
}, 'filter' => false],
[
'class' => ActionColumn::class,
'header' => '操作',
'template' => '{view} {update}',
'urlCreator' => function ($action, User $model, $key, $index, $column) {
return Url::toRoute(['user-' . $action, 'id' => $model->id]);
}
],
],
]); ?>
</div>
<?php Pjax::end(); ?>
</div>

View File

@ -0,0 +1,20 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var app\models\User $model */
$this->title = '创建用户';
$this->params['breadcrumbs'][] = ['label' => '用户管理', 'url' => ['user']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="user-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_user_add_form', [
'model' => $model,
]) ?>
</div>

View File

@ -0,0 +1,21 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var app\models\User $model */
$this->title = 'Update User: ' . $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['user']];
$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['user-view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="user-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_user_form', [
'model' => $model,
]) ?>
</div>

57
views/admin/user_view.php Normal file
View File

@ -0,0 +1,57 @@
<?php
use yii\helpers\Html;
use yii\web\YiiAsset;
use yii\widgets\DetailView;
/** @var yii\web\View $this */
/** @var app\models\User $model */
$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['user']];
$this->params['breadcrumbs'][] = $this->title;
YiiAsset::register($this);
?>
<div class="user-view">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a('Update', ['user-update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a('Delete', ['user-delete', 'id' => $model->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
]) ?>
</p>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'username',
'name',
'password',
'auth_key',
'email:email',
'status',
'created_at',
'last_login',
'last_login_ip',
'bio:ntext',
'role',
'encryption_key',
'otp_secret',
'is_encryption_enabled',
'is_otp_enabled',
'storage_limit',
'recovery_codes',
'dark_mode',
'vault_secret',
'vault_salt',
],
]) ?>
</div>