Write the Code. Change the World.

9月 23

在常规情况下,去定义组件的时候,只需要用到父子组件通讯即可。这样比较好实现。有时候,也会用到任意组件间的通讯,这个时候自定义组件的方式就有用了。不弄那么多,只弄一个。

先看常见父子通讯

# 使用组件的地方
<sky @changeColor="changeColor" />

methods: {
    changeColor(value) {

    }
}
# 组件内部,仅此而已。子组件中的数据,就可以被父组件收到了
this.$emit('changeColor', 'blue')

全局任意组件通讯

  1. 我们先定义一个中间的 bus

eventBus.js

import Vue from 'vue'

export default new Vue()
  1. 通常事件,事件是有一个地方发送,一个地方接受。我们再定义一个事件接受的地方。不过,要记得释放哟。可以在 mounted 的时候添加,beforeDestroy 的时候释放掉

xxx1.vue

import eventBus from '../utils/eventBus.js'

export default {
  data() {
    return {
      name: 'hello world'
    }
  },
  watch: {

  },
  mounted() {
    eventBus.$on('message', this.message)
  },
  beforeDestroy() {
    eventBus.$off('message', this.message)
  },
  methods: {
    message(value) {
        console.log('message = ', value)
    }
  }
  1. 我们再定义一个发送事件的地方

xxx2.vue

import eventBus from '../utils/eventBus.js'

# 发送吧
eventBus.$emit('message', 'hello world')

最后啰嗦一下

兄弟组件之间,也可以用自定义事件的方式来通讯。既然是兄弟,那他们肯定有相同的父组件。以父组件为 dispatcher 来整不就可以了。

# 兄弟一收
this.$parent.$on('message', res => { console.log('res = ', res) })

# 兄弟二发
$this.$parent.$emit('message', 'hello world')

阅读全文 >>

9月 22

在诸多 Vue.js 应用中, Lodash, Moment, Axios, Async等都是一些非常有用的 JavaScript 库. 但随着项目越来越复杂, 可能会采取组件化和模块化的方式来组织代码, 还可能要使应用支持不同环境下的服务端渲染. 除非你找到了一个简单而又健壮的方式来引入这些库供不同的组件和模块使用, 不然, 这些第三方库的管理会给你带来一些麻烦.

本文将介绍一些在 Vue.js 中使用第三方库的方式.

全局变量

在项目中添加第三方库的最简单方式是讲其作为一个全局变量, 挂载到 window 对象上:

entry.js

window._ = require('lodash');

MyComponent.vue

export default {
  created() {
    console.log(_.isEmpty() ? 'Lodash everywhere!' : 'Uh oh..');
  }
}

阅读全文 >>

9月 22

啥都不说,操作一波

先看路由,我们会看到在 routes/web.php 中增加了 Auth::routes(); 这个和以前不一样了。以前在 vendor/laravel/framework/src/Illuminate/Routing/Router.php 中,现在却在 vendor/laravel/ui/src/AuthRouteMethods.php 中,如下:

    public function auth()
    {
        return function ($options = []) {
            $namespace = class_exists($this->prependGroupNamespace('Auth\LoginController')) ? null : 'App\Http\Controllers';

            $this->group(['namespace' => $namespace], function() use($options) {
                // Login Routes...
                if ($options['login'] ?? true) {
                    $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
                    $this->post('login', 'Auth\LoginController@login');
                }

                // Logout Routes...
                if ($options['logout'] ?? true) {
                    $this->post('logout', 'Auth\LoginController@logout')->name('logout');
                }

                // Registration Routes...
                if ($options['register'] ?? true) {
                    $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
                    $this->post('register', 'Auth\RegisterController@register');
                }

                // Password Reset Routes...
                if ($options['reset'] ?? true) {
                    $this->resetPassword();
                }

                // Password Confirmation Routes...
                if ($options['confirm'] ??
                    class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
                    $this->confirmPassword();
                }

                // Email Verification Routes...
                if ($options['verify'] ?? false) {
                    $this->emailVerification();
                }
            });
        };
    }

如果不信,可以测试测试,修改原来的 login 路由为

$this->get('login', function() {return 123;})->name('login');

再请求 login 时,就可以知道了。

这些路由放在这里好深,好不明了。我们把他们移到 web.php 中,到时候,还可以适当的增减和调整。如下:

use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\Auth\RegisterController;

// Auth::routes();

Route::get('login', [LoginController::class, 'showLoginForm'])->name('login');
Route::post('login', [LoginController::class, 'login']);
Route::post('logout', [LoginController::class, 'logout'])->name('logout');
Route::get('register', [RegisterController::class, 'showRegistrationForm'])->name('register');
Route::post('register', [RegisterController::class, 'register']);

暂时就搞这么几个。 laravel8 的路由有变化。

因为之前改了 user 表,这里用户对应的字段是 account ,需要修改,重写父类:

use App\Http\Controllers\Auth\LoginController;

    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     */
    public function username()
    {
        return 'account';
    }

最后,修改 login.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="account" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="account" type="phone" class="form-control @error('account') is-invalid @enderror" name="account" value="{{ old('account') }}" required autocomplete="account" autofocus>

                                @error('account')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>

                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

只是简单改一下,很多细节没处理。再进入 http://jk.cn/login 登录看看,发现登录成功了。到此,bootstrap 已经弄进去了,也可以好好的了。至于 login 页面, register 页面,以及 登录注册逻辑,也要适当修改调整完善。

下一步

bootstrap 是引入进来了,可都是默认的东东。我们是可以定制 bootstrap 的主题和一些其他设置的。可以用的更完美。可惜到现在还不是 bootstrap5。除此,我们还可以将 vue 融合进来,某些组件啥的,使用 vue 的确要方便好多。

阅读全文 >>

9月 22

操作一波

composer require laravel/ui

# 生成基本脚手架
php artisan ui bootstrap
php artisan ui vue
php artisan ui react

# 生成 登录/注册 脚手架
php artisan ui bootstrap --auth
php artisan ui vue --auth
php artisan ui react --auth

这里,我们使用 php artisan ui bootstrap --auth 生成登录注册脚手架,后边根据需要改

# 安装依赖包
yarn --no-bin-links

# 跑
yarn run dev

如果 run 的时候,出现 **cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js –progress –hide-modules –config=node_modules/laravel-mix/setup/webpack.config.js
**,可以去掉 package.json 中的 cross-env

vim package.json

# 批量替换
:%s/cross-env /g
:wg

# 再执行
yarn run dev

如果报错

        Additional dependencies must be installed. This will only take a moment.

        Running: npm install vue-template-compiler --save-dev --production=false

继续执行

yarn add vue-template-compiler --save-dev --production=false --no-bin-links

然后继续 yarn run dev,直到成功。

 DONE  Compiled successfully in 10622ms                                                                                                                                                                                                                              9:52:56 AM

       Asset      Size   Chunks             Chunk Names
/css/app.css   178 KiB  /js/app  [emitted]  /js/app
  /js/app.js  1.07 MiB  /js/app  [emitted]  /js/app
Done in 17.16s.

提交 git

git add .
git commit -m '安装 bootstrap 以及 auth 脚手架'

下一步

处理登录,注册逻辑,字段等调整。

阅读全文 >>

9月 19

安装 vue element admin 的时候,默认带入了很多路由,模板,mock,api 等。如果从 0 开始,就需要去掉这些多余的东西。

操作一波

src/api 删除下边的文件,只留下 role.jsuser.js
.
├── role.js
└── user.js

src/components 组件暂不做处理,随着项目发展,某些组件还是很有用的。

src/router/modules/ 全删掉(如果需要,可后边对比补充)

src/router/index.js 修改成如下(其实就是去掉那些不想要的)

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'
/**
 * Note: sub-menu only appear when route children.length >= 1
 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 *
 * hidden: true                   if set true, item will not show in the sidebar(default is false)
 * alwaysShow: true               if set true, will always show the root menu
 *                                if not set alwaysShow, when item has more than one children route,
 *                                it will becomes nested mode, otherwise not show the root menu
 * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
    roles: ['admin','editor']    control the page roles (you can set multiple roles)
    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
    icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
    noCache: true                if set true, the page will no be cached(default is false)
    affix: true                  if set true, the tag will affix in the tags-view
    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
  }
 */

/**
 * constantRoutes
 * a base page that does not have permission requirements
 * all roles can be accessed
 */
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error-page/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error-page/401'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index'),
        name: '仪表盘',
        meta: { title: '仪表盘', icon: 'dashboard', affix: true }
      }
    ]
  }
]

/**
 * asyncRoutes
 * the routes that need to be dynamically loaded based on user roles
 */
export const asyncRoutes = [
  {
    path: '/permission',
    component: Layout,
    redirect: '/permission/page',
    alwaysShow: true, // will always show the root menu
    name: 'Permission',
    meta: {
      title: '权限控制',
      icon: 'lock',
      roles: ['admin', 'editor'] // you can set roles in root nav
    },
    children: [
      {
        path: 'page',
        component: () => import('@/views/permission/page'),
        name: 'PagePermission',
        meta: {
          title: 'Page Permission',
          roles: ['admin'] // or you can only set roles in sub nav
        }
      },
      {
        path: 'directive',
        component: () => import('@/views/permission/directive'),
        name: 'DirectivePermission',
        meta: {
          title: 'Directive Permission'
          // if do not set roles, means: this page does not require permission
        }
      },
      {
        path: 'role',
        component: () => import('@/views/permission/role'),
        name: 'RolePermission',
        meta: {
          title: 'Role Permission',
          roles: ['admin']
        }
      }
    ]
  },
  {
    path: 'baidu',
    component: Layout,
    children: [
      {
        path: 'https://www.baidu.com',
        meta: { title: '百度', icon: 'link' }
      }
    ]
  },

  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  mode: 'history', // require service support
  base: '/admin/',
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

然后 src/views 里边的删除掉,留下边这些:

.
├── dashboard
├── error-page
├── login
├── permission
└── redirect

如果有报错,处理报错,就是引入一些文件,之前被删了这些。

继续删 icons,icon 都是一个一个的 svg ,根据自己的需要,留着几个用到的,其他的都可以干掉吧。先把之前的 git 一下,后边删多了,还可以 checkout。

win 下查看 svg https://www.cnblogs.com/fanful/p/12404342.html

git add .
git commit -m '删掉冗余的路由,view,api 这些'

如果有强迫症,喜欢干净的可以一个一个的比,看。还有 npm remove 不需要的包。

这一步,其实,也可以在刚下载下来的时候做。

下一步,安装 bootstrap

阅读全文 >>

9月 18

1
如果说,两个人在一起的际遇有很多种,那不在一起的理由里“性格不合”一定排在首位。

曾看过街头随机采访情侣分手的视频,百分之九十的回答都是:

“还是性格原因吧。我们不合适。”
“他太大男子主义了,我受不了。”
“她脾气不好,要求太多,我太累了。”

或许,在我们的感情经历里,多多少少都遇到类似的经历。

本来各方面条件都还不错的对象,因为性格,最终还是因无法长久和谐的相处而分手;再多的怦然心动,也抗衡不了性格不合的互相消耗和折磨。

那是不是我们每个人在遇到性格合适的人之前,都会面临争吵、冷战、over的结局呢?

还是说这个世界上真的存在俩人一开始就十分合拍,惺惺相惜的人呢?

用我的亲身经历来回答,是有的。

2
闺蜜最近和我说,她陷入爱河了,一个这么挑剔又冷傲的人居然被对方迷得死死的。起因是,她在面试他时,俩人聊的格外投机。

都喜欢看《遥远的救世主》这本书,对方形容她像书里的女主,黑色长发,外柔内刚,举手投足间都有书里形容女主的气息。

她感觉对方像书里的男主,温文尔雅,清新脱俗。

说不出是哪里,但冥冥之中感觉彼此有相似的地方,能引起磁场反应。之后,她加了男生的微信,多了份留意和主动。

好不容易逮到机会,鼓起勇气约男生出来吃饭。没想到,饭桌上聊下来,彼此都心生爱的火花。

除了都喜欢看书,一些观念和看法都能不谋而合,即使话未说完,也都能懂彼此的意思,很有一见如故的感觉。

毕竟在这个偌大的世界里,能有缘分的牵引,遇到相似的灵魂,既是惊喜,也是生命的馈赠。

有句话说的很好,遇见谁,跟谁在一起真的不一样。
能够彼此靠近、吸引到惺惺相惜的过程,都源于这种相似性产生的共鸣。

3
但感情这个东西,很难用一种标准去定义。

两个人太相似,又难以逃脱像和另一个自己谈恋爱的“陷阱”里,未免会感到无趣。

我见过一对情侣,俩人经历相似,都内敛沉稳,不爱社交。

当男生遇到另一个骨子叛逆、爱自由、喜欢新鲜和挑战的女孩时,这对于习惯了循规蹈矩的男生来说,是不一样的、极具诱惑力的存在。

因而,他被深深吸引。

他在女孩身上看到了自己被压抑的灵魂,女孩身上有他渴求但又缺失的那部分。

所以,当遇到互补的人时,我们又会感到新鲜和有趣。

不可否认的是这种因性格、特质、需求等等的差异,会让两个人的关系更有活力。

找伴侣,是找相似的还是互补的,是一直在争议的问题。

我的答案是:大部分相似,小部分互补。

社会心理学研究表明:
相似度高的人,更容易形成相互需求的共同体:在结成夫妻后,10年后还能相爱如初的可能性更大。

所以,从长远来看,跟相似的人在一起,更稳定舒服。

相似的人,更容易互相理解,能站在对方的思维和立场上考量,就算吵闹,也更能够有效沟通。
它是构成你们相处关系里长久稳定和谐的主线。而互补的那部分,是爱情里的调味剂,是点缀。

你们可以有一致的三观、相似的习惯、心有灵犀的默契,但也可以有不同的擅长领域、不同的爱好,能够为你们的爱情植入新鲜血液。

当然,选择合适的人的确很重要,但也要基于爱,还有彼此愿意为了维持恋情共同努力。

文章来源网络

阅读全文 >>

9月 18

这里的登录和传统的登录有一点不一样,传统的登录提交账号密码,正确就可以了。这里得先请求 /sanctum/csrf-cookie,再进行登录请求。

操作一波

在 vue element admin 目录中,先修改前端页面。

src/api/user.js 中增加:

export function csrfCookie() {
  return request({
    url: '/sanctum/csrf-cookie',
    method: 'get'
  })
}

修改src/utils/validate.js

# 先偷懒一会,不去做任何检查,直接返回 true
export function validUsername(str) {
  return true;
  const valid_map = ['admin', 'editor']
  return valid_map.indexOf(str.trim()) >= 0
}

修改
src/store/modules/user.js

# token 先随便写一波,我们也不会用到
const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      csrfCookie().then(res => {
        login({ account: username.trim(), password: password }).then(response => {
          const data = response
          data.token = 'abcdefghigklmnopqrstuvwxyz'
          commit('SET_TOKEN', data.token)
          setToken(data.token)
          resolve()
        }).catch(error => {
          reject(error)
        })
      }).catch(error => {
        reject(error)
      })
    })
  },

到此,登录已经完成。登录成功后,vue element admin 逻辑会去调用 user/info 接口,成功再次一举。如果能成功获取到数据,说明是请求没问题。然后你不使用 csrfCookie,再去请求 user/info 试试。或用 postman 单独请求试试,如果依然可以请求成功,那就太伤心了,说明 sanctum 是假的,没起到作用。当然是可行的,毕竟是官方推荐的成熟的东西。如果哪里有问题,就是某一个环节使用出问题了。

请求虽然成功了,逻辑需要修改。
修改 src/store/modules/user.js

  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const data  = { roles: ['admin'], ...response} 
        data.name = data.nickname;
        data.introduction = data.signature;
        if (!data) {
          reject('Verification failed, please Login again.')
        }

再 build,再跑看看。

npm run build:prod

通过下边这两个图,就可以看明白了。

最后

用户状态(登录这些已经ok了),可是 vue element admin 这个默认带了太多的 view,其实,我们在刚拉下来的时候就应该删掉的。下一步,我们删掉那些我们不需要的,再开始干其他事。

阅读全文 >>

9月 16

为了测试, 我们需要写入一些用户信息。

生数据前奏

为了方便数据的丰满性,这里给用户增加了昵称,性别,头像,签名等字段,更新后的迁移数据是这样的。

        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('account')->unique();
            $table->string('password');
            $table->string('nickname', 32)->comment('昵称');
            $table->unsignedTinyInteger('gender')->default(3)->comment('性别 1 男 2 女 3 未知');
            $table->string('avatar', 128)->nullable();
            $table->string('email')->unique();
            $table->string('signature')->nullable();
            $table->rememberToken();
            $table->timestamp('email_verified_at')->nullable();
            $table->timestamps();
        });

这里将 name 改成 account 了,email 后移了。使用 account 和 password 登录。而非 email。当然,account 你可以和 email 的数据一样。我个人喜好用手机作为 account。

模型白名单更改:

    protected $fillable = [
        'account', 'password', 'nickname', 'gender', 'avatar', 'email',  'signature'
    ];

生成数据

可以先了解 faker。

https://github.com/fzaninotto/Faker

https://www.jianshu.com/p/6576144f73fc

在测试阶段,使用 seeder 来生是非常方便的。在使用 seeder 之前,先定义好 UserFactory,它在 database/factories/ 下,修改成:

    public function definition()
    {
        return [
            'account' => $this->faker->unique()->phoneNumber,
            'password' => Hash::make('123456'), 
            'nickname' => $this->faker->name,
            'gender' => $this->faker->numberBetween(1, 3),
            'avatar' => $this->faker->imageUrl(256,256),
            'email' => $this->faker->unique()->safeEmail,
            'signature' => $this->faker->text(96),
            'email_verified_at' => now(),
            'remember_token' => Str::random(10),
        ];
    }

我们可以先在 tinker 里边尝试尝试。

php artisan tinker;

$faker = Faker\Factory::create('zh_CN');
$faker->name;
$faker->safeEmail;

好了,上边前奏好了,我们生一个 seeder。

php artisan make:seeder UserSeeder

# 填充 UserSeeder (生成十个用户)
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        User::factory(10)->create();
    }
}

# 将该 seeder 添加到 DatabaseSeeder 里边
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call([
            UserSeeder::class
        ]);
    }
}

好了,下边来执行删除表并执行迁移和seeder。

php artisan migrate:fresh --seed

# 出现下边的结果,表示已生好了

Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (147.18ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (86.80ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (128.32ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (139.25ms)
Seeding: Database\Seeders\UserSeeder
Seeded:  Database\Seeders\UserSeeder (1,104.20ms)
Database seeding completed successfully.

# 我们来用 tinker 喵喵
php artisan tinker;
use App\Models\User;
User::limit(20)->get()->all();

# 这个时候发现只有 10 条数据,故意用 20 的。因为我们只生了10个。

上边的 seeder 很方便,也是官方的一种标准写法。可是上边生成的数据还是不够自由。比如我想生成特定的手机号码的用户呢。上边不好搞。这时,我们可以直接在 seeder 中来操作,不必去关心 factory 。

这时,我需要生成一条特定的数据,可以在 UserSeeder 中,在 run 中,增加一个独有的数据就好了。

修改 UserSeeder 如下:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Models\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $faker = \Faker\Factory::create('zh_CN');
        $data = [
            'account' => 'xxxxxxxx',
            'password' => Hash::make('123456'),
            'nickname' => '七月羽歌',
            'gender' => 1,
            'avatar' => $faker->imageUrl(256,256),
            'email' => 'xxx@xxx.com',
            'signature' => '虚幻之物对应着冥冥之路。',
            'email_verified_at' => now(),
            'remember_token' => Str::random(10),
        ];
        User::create($data);

        User::factory(10)->create();
    }
}

好了,知道怎么会事就可以了。

最后

数据也生了,下边就开始做 vue element admin 的登录以及用户信息展示的对接了。

阅读全文 >>

9月 16

身份验证使用 Laravel Sanctum

https://learnku.com/docs/laravel/8.x/sanctum/9421

安装 Laravel Sanctum

composer require laravel/sanctum

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

php artisan migrate

修改 app/Http/Kernel.php

use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;

'api' => [
     EnsureFrontendRequestsAreStateful::class,
     'throttle:api',
     \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

修改 config/sanctum.php

# 增加

'prefix' => 'admin/api/sanctum'

在 .env 和 .env.example 中增加

SANCTUM_STATEFUL_DOMAINS=jk.cn

接下来,增加路由配置。

增加路由配置

修改 app/Providers/RouteServiceProvider

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::middleware('web')
                ->group(base_path('routes/web.php'));

            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));

            Route::prefix('admin/api')
                ->middleware('api')
                ->group(base_path('routes/admin.php'));
        });
    }

增加 routes/admin.php

<?php

use Illuminate\Support\Facades\Route;

Route::namespace('Admin')->group(function(){

    Route::post('login', 'UserController@login')->name('admin.api.login');

    Route::group([
        'middleware' => ['auth:sanctum'],
    ], function () {
        Route::post('logout', 'UserController@logout')->name('admin.api.logout');

        Route::get('user/info', 'UserController@userInfo')->name('admin.api.userInfo');
    });
});

增加控制器 app/Http/Controllers/Admin/UserController.php,由 artisan 生成

php artisan make:controller Admin/UserController

增加 app/Http/Requests/Admin 由 artisan 生成。

php artisan make:request Admin/LoginRequest

<?php

namespace App\Http\Requests\Admin;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        $rules = [
            'account' => [
                'required',
                'regex:/^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199)\d{8}$/'
                // 'unique:users',
            ],
            'password' => 'required|string|min:6|max:20'
        ];

        return $rules;
    }

    public function messages()
    {
        return [];
    }

    public function attributes()
    {
        return [
            'account' => '账号',
            'password' => '密码'
        ];
    }
}

再来看 UserController 控制器逻辑

<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\Http\Requests\Admin\LoginRequest;

class UserController extends Controller
{
    /**
     * 登录 
     */ 
    public function login(LoginRequest $request)
    {
        $data = $request->only(['account', 'password']);

        $remember = $request->remember ?? false;

        if (Auth::attempt($data, $remember)) {
            $user = auth()->user();
            $expiredAt = null;
            if (!$remember) {
                $expiredAt = Carbon::now()->addMinute(config('session.lifetime'))->toDateTimeString();
            }
            $user->expiredAt = $expiredAt;
            return response()->json($user, 200);
        } else {
            return response()->json(['message' => '账号或密码错误'], 401);
        }
    }

    /**
     * 登出 
     */ 
    public function logout()
    {
        Auth::guard('web')->logout();

        return response()->json(['message' => '登出成功'], 200);
    }

    /**
     * 获取当前用户信息
     */ 
    public function userInfo(Request $request)
    {
        $user = $request->user();

        return response()->json($user, 200);
    }
}

到此,服务端基础逻辑完成。提交下 git。

git add .
git commit -m '安装 Sanctum,并配置路由,以及增加登录登出相关逻辑'

下一步,生成几个用户,来正式测试测试。

阅读全文 >>

9月 16

在测试前期阶段 mock data 的确很有用。可测试后期和线上的时候,就需要用到真实环境数据。

操作一波

点击进去喵喵

关掉 mock

vue.config.js

# 注释掉这几行
  // devServer: {
  //   port: port,
  //   open: true,
  //   overlay: {
  //     warnings: false,
  //     errors: true
  //   },
  //   before: require('./mock/mock-server.js')
  // },

src/main.js

# 注释掉这几行
// if (process.env.NODE_ENV === 'production') {
//   const { mockXHR } = require('../mock')
//   mockXHR()
// }

到此,mock data 已经关掉了。然后运行 npm run devnpm run build:prod 跑起来看看。打开网页,F12 打开控制台,选择 network, 选择 xhr。默认会进入到登录页面(刚运行没登录),输入用户名和密码,点击登录,这个时候,就可以看到请求地址了。地址是下边这样的。而这些只是在打包阶段。

http://192.168.1.141:8080/dev-api/vue-element-admin/user/login

既然我们禁用了 mock data,那我们怎么很好的控制请求了。vue-element-admin 这里使用了 axios,既然是 axios,那就可以去实现拦截器。

阅读全文 >>