xyc
5 天以前 f620fcd86c8f389f6ca24023f36ddbb4fc70a671
traffic-audit-web/src/views/Login.vue
@@ -10,106 +10,91 @@
          <el-input v-model="form.password" type="password" placeholder="密码" @keyup.enter.native="login"></el-input>
        </el-form-item>
        <el-form-item>
          <div class="captcha-wrap" v-loading="captchaLoading">
            <shape-captcha v-if="captcha.shapes.length"
                           :shapes="captcha.shapes"
                           :instruction="captcha.instruction"
                           @complete="onCaptchaComplete"
                           @refresh="loadCaptcha"
                           @reset="onCaptchaReset"></shape-captcha>
            <div v-else class="captcha-empty">正在加载验证码…</div>
          </div>
          <captcha3-d ref="captcha" @verified="onCaptchaVerified" @reset="onCaptchaReset"></captcha3-d>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="login" :loading="loading" style="width:100%">登 录</el-button>
          <el-button type="primary" :loading="loggingIn" @click="login" style="width:100%">登 录</el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>
<script>
import api from '@/api'
import ShapeCaptcha from '@/components/ShapeCaptcha.vue'
import axios from 'axios'
import Captcha3D from '@/components/Captcha3D.vue'
export default {
  name: 'Login',
  components: { ShapeCaptcha },
  components: { Captcha3D },
  data() {
    return {
      form: { username: '', password: '' },
      loading: false,
      captchaLoading: false,
      captcha: { captchaId: '', shapes: [], instruction: '' },
      seq: [],
      rules: {
        username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
      }
      },
      captchaVerified: false,
      captchaId: '',
      clickIds: [],
      loggingIn: false
    }
  },
  created() { this.loadCaptcha() },
  methods: {
    loadCaptcha() {
      this.captchaLoading = true
      this.seq = []
      this.captcha = { captchaId: '', shapes: [], instruction: '' }
      api.get('/captcha/shape')
        .then(res => {
          const d = res && res.data
          if (!d || !d.captchaId) { this.$message.error((res && res.message) || '验证码加载失败'); return }
          this.captcha = d
        })
        .catch(err => {
          const d = err && err.response && err.response.data
          this.$message.error((d && d.message) || '验证码加载失败,请稍后重试')
        })
        .finally(() => { this.captchaLoading = false })
    onCaptchaVerified(payload) {
      this.captchaVerified = true
      this.captchaId = payload.captchaId
      this.clickIds = payload.clickIds
    },
    onCaptchaComplete(sequence) { this.seq = sequence || [] },
    onCaptchaReset() { this.seq = [] },
    onCaptchaReset() {
      this.captchaVerified = false
      this.captchaId = ''
      this.clickIds = []
    },
    login() {
      this.$refs.form.validate(valid => {
        if (!valid) return
        if (!this.captcha.captchaId) { this.$message.warning('验证码加载中,请稍候'); return }
        if (!this.seq || this.seq.length < this.captcha.total) {
          this.$message.warning('请先按提示顺序点击验证码图形')
        if (!this.captchaVerified) {
          this.$message.warning('请先完成行为验证')
          return
        }
        this.loading = true
        api.post('/captcha/verify', { captchaId: this.captcha.captchaId, sequence: this.seq })
          .then(res => {
            if (!res || res.code !== 200) {
              this.$message.error((res && res.message) || '验证码校验失败')
              this.loadCaptcha()
              return
            }
            return api.post('/auth/login', this.form)
          })
          .then(res => {
            if (!res) return
            const d = res.data
            if (!d || !d.token) { this.$message.error((res && res.message) || '登录失败'); return }
            localStorage.setItem('token', d.token)
            localStorage.setItem('username', d.username || '')
            localStorage.setItem('realName', d.realName || '')
            localStorage.setItem('modules', d.modules || '')
            this.$message.success('欢迎,' + (d.realName || d.username))
        this.loggingIn = true
        axios.post('/api/auth/login', {
          username: this.form.username,
          password: this.form.password,
          captchaId: this.captchaId,
          captchaClickIds: this.clickIds
        }).then(res => {
          this.loggingIn = false
          const d = res.data
          if (d.code === 200) {
            localStorage.setItem('token', d.data.token)
            localStorage.setItem('username', d.data.username)
            localStorage.setItem('realName', d.data.realName || '')
            localStorage.setItem('modules', d.data.modules || '')
            this.$message.success('登录成功')
            this.$router.push('/dashboard')
          })
          .catch(err => {
            const d = err && err.response && err.response.data
            this.$message.error((d && d.message) || '登录失败:请检查用户名或密码')
            this.loadCaptcha()
          })
          .finally(() => { this.loading = false })
          } else if (d.code === 401) {
            this.$message.error(d.message || '用户名或密码错误')
            this.$refs.captcha.load()
          } else if (d.code === 4001 || d.code === 4002 || d.code === 4003) {
            this.$message.error(d.message || '验证未通过,请重新验证')
            this.$refs.captcha.load()
          } else {
            this.$message.error(d.message || '登录失败,请重试')
            this.$refs.captcha.load()
          }
        }).catch(() => {
          this.loggingIn = false
          this.$message.error('网络错误,登录失败')
        })
      })
    }
  }
}
</script>
<style scoped>
.login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f2f5; }
.login-card { width: 420px; }
.login-card h2 { text-align: center; margin-bottom: 20px; }
.captcha-wrap { min-height: 168px; }
.captcha-empty { display: flex; justify-content: center; align-items: center; height: 168px; color: #909399; font-size: 13px; }
</style>
.login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px 0; box-sizing: border-box; background: #f0f2f5; }
.login-card { width: 400px; }
.login-card h2 { text-align: center; margin-bottom: 18px; }
.login-card .el-form-item { margin-bottom: 16px; }
</style>