Skip to content

Commit

Permalink
chore: sync frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
cuixiaorui committed Jul 24, 2023
1 parent 5340857 commit 7e2c427
Show file tree
Hide file tree
Showing 26 changed files with 541 additions and 65 deletions.
53 changes: 53 additions & 0 deletions apps/frontend/src/api/tests/http.new.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import MockAdapter from 'axios-mock-adapter'
import { http } from '../http'
import { setToken } from '@/utils/token'
import { messageError } from '@/composables/message'

vi.mock('@/composables/message')

describe('http.new', () => {
const mock = new MockAdapter(http)
beforeEach(() => {
mock.reset()
})

it('should set token to request headers authorization when token exists', async () => {
const token = 'token'
setToken(token)
mock.onGet('/tasks').reply(200, {
code: 0,
data: [],
message: '',
})

await http.get('/tasks')

expect(mock.history.get[0].headers?.Authorization).toBe(`Bearer ${token}`)
})

it('should return data of the responseData when code is 0', async () => {
const data = [{ name: '吃饭' }]
mock.onGet('/tasks').reply(200, {
code: 0,
data,
message: '',
})

const result = await http.get('/tasks')

expect(result).toEqual(data)
})

it('should throw an error when code is not 0', async () => {
const message = 'an error'
mock.onGet('/tasks').reply(200, {
code: -1,
data: null,
message,
})

await expect(() => http.get('/tasks')).rejects.toThrowError(message)
expect(messageError).toBeCalledWith(message)
})
})
5 changes: 4 additions & 1 deletion apps/teach-frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
<script setup lang="ts">
import { NConfigProvider } from 'naive-ui'
import { useRoute } from 'vue-router'
import BaseLayout from './layout/BaseLayout.vue'
import { useCommand } from '@/composables/command'
import { useThemeStore } from '@/store'
const route = useRoute()
const themeStore = useThemeStore()
const { initCommands } = useCommand()
initCommands()
</script>

<template>
<NConfigProvider :theme="themeStore.naiveTheme">
<BaseLayout>
<BaseLayout v-if="route.meta.layout !== false">
<RouterView />
</BaseLayout>
<RouterView v-else />
</NConfigProvider>
</template>

Expand Down
24 changes: 20 additions & 4 deletions apps/teach-frontend/src/api/http.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import type { AxiosInstance, AxiosResponse } from 'axios'
import axios from 'axios'
import { checkHaveToken, getToken } from '@/utils/token'
import { messageError, messageRedirectToSignIn } from '@/composables/message'
import { goToLogin } from '@/composables'

export const http: AxiosInstance = axios.create({
baseURL: '/api', // Replace with your API base URL
baseURL: '/api',
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
})

http.interceptors.request.use((config) => {
if (checkHaveToken())
config.headers.Authorization = `Bearer ${getToken()}`

return config
})

http.interceptors.response.use(
(response: AxiosResponse) => {
const { code, message, data } = response.data
Expand All @@ -15,12 +25,18 @@ http.interceptors.response.use(
return data
}
else {
console.error(message)
messageError(message)
return Promise.reject(new Error(message))
}
},
(error) => {
console.error('Request error:', error)
return Promise.reject(error)
if (error.response.status) {
switch (error.response.status) {
case 401:
messageRedirectToSignIn(goToLogin)
break
}
return Promise.reject(error)
}
},
)
1 change: 1 addition & 0 deletions apps/teach-frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './tasks'
export * from './projects'
export * from './user'
4 changes: 0 additions & 4 deletions apps/teach-frontend/src/api/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,3 @@ export function fetchUpdateTaskPosition(taskId: Task['id'], position: number) {
position,
})
}

// export function fetchUpdateTaskProperties(taskId: Task['id'], properties: any) {

// }
5 changes: 5 additions & 0 deletions apps/teach-frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ export interface ProjectResponse {
updatedAt: string
_id: string
}

export interface UserResponse {
username: string
token: string
}
25 changes: 25 additions & 0 deletions apps/teach-frontend/src/api/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { http } from './http'
import type { UserResponse } from './types'

export function fetchSignIn(username: string, password: string) {
return http.post<UserResponse, UserResponse>('/users/signin', {
username,
password,
})
}

export function fetchSignUp({
username,
password,
confirmPassword,
}: {
username: string
password: string
confirmPassword: string
}) {
return http.post<UserResponse, UserResponse>('/users/signup', {
username,
password,
confirmPassword,
})
}
66 changes: 66 additions & 0 deletions apps/teach-frontend/src/components/login/SignIn.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { NButton, NForm, NFormItem, NInput } from 'naive-ui'
import type { FormInst } from 'naive-ui'
import { reactive, ref } from 'vue'
import { createPasswordRule, createUsernameRule } from './rules'
import { useUserStore } from '@/store'
import { useGoto } from '@/composables/goto'
interface SignInFormValue {
username: string
password: string
}
const userStore = useUserStore()
const { gotoHome } = useGoto()
const formRef = ref<FormInst | null>(null)
const formValue = reactive<SignInFormValue>({
username: '',
password: '',
})
const rules = {
username: createUsernameRule(),
password: createPasswordRule(),
}
function handleSignIn(e: MouseEvent) {
e.preventDefault()
formRef.value?.validate(async (errors) => {
if (!errors) {
await userStore.signIn(formValue.username, formValue.password)
gotoHome()
}
})
}
</script>

<template>
<NForm ref="formRef" :model="formValue" :rules="rules">
<NFormItem label="帐号" path="username">
<NInput v-model:value="formValue.username" placeholder="输入帐号" />
</NFormItem>
<NFormItem label="密码" path="password">
<NInput
v-model:value="formValue.password"
type="password"
placeholder="输入密码"
/>
</NFormItem>
<NFormItem>
<NButton
attr-type="button"
type="primary"
block
secondary
strong
@click="handleSignIn"
>
登录
</NButton>
</NFormItem>
</NForm>
</template>

<style scoped></style>
87 changes: 87 additions & 0 deletions apps/teach-frontend/src/components/login/SignUp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<script setup lang="ts">
import { NButton, NForm, NFormItem, NInput } from 'naive-ui'
import { reactive, ref } from 'vue'
import type { FormInst } from 'naive-ui'
import {
createConfirmPasswordRule,
createPasswordRule,
createUsernameRule,
} from './rules'
import { useUserStore } from '@/store'
import { useGoto } from '@/composables/goto'
interface SignUpFormValue {
username: string
password: string
confirmPassword: string
}
const userStore = useUserStore()
const { gotoHome } = useGoto()
const formRef = ref<FormInst | null>(null)
const formValue = reactive<SignUpFormValue>({
username: '',
password: '',
confirmPassword: '',
})
const rules = {
username: createUsernameRule(),
password: createPasswordRule(),
confirmPassword: createConfirmPasswordRule(formValue),
}
function handleSignUp(e: MouseEvent) {
e.preventDefault()
formRef.value?.validate(async (errors) => {
if (!errors) {
await userStore.signUp({
username: formValue.username,
password: formValue.password,
confirmPassword: formValue.confirmPassword,
})
gotoHome()
}
})
}
</script>

<template>
<NForm ref="formRef" :model="formValue" :rules="rules">
<NFormItem label="帐号" path="username">
<NInput v-model:value="formValue.username" placeholder="输入帐号" />
</NFormItem>
<NFormItem label="密码" path="password">
<NInput
v-model:value="formValue.password"
type="password"
placeholder="输入密码"
/>
</NFormItem>

<NFormItem label="确认密码" path="confirmPassword">
<NInput
v-model:value="formValue.confirmPassword"
type="password"
placeholder="确认密码"
/>
</NFormItem>
<NFormItem>
<NButton
attr-type="button"
type="primary"
block
secondary
strong
@click="handleSignUp"
>
注册
</NButton>
</NFormItem>
</NForm>
</template>

<style scoped></style>
54 changes: 54 additions & 0 deletions apps/teach-frontend/src/components/login/rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { FormItemRule } from 'naive-ui'
import { validatePasswordLength, validatePasswordSame, validateUsernameLength } from './validator'

export function createUsernameRule() {
return [
{
required: true,
message: '请输入帐号',
trigger: 'blur',
},
{
validator(rule: FormItemRule, value: string) {
return validateUsernameLength(value)
},
message: '长度要大于 6 小于 30',
trigger: 'blur',
},
]
}

export function createPasswordRule() {
return [
{
required: true,
message: '请输入密码',
trigger: 'blur',
},

{
validator(rule: FormItemRule, value: string) {
return validatePasswordLength(value)
},
message: '长度要大于 6 小于 30',
trigger: 'blur',
},
]
}

export function createConfirmPasswordRule(formValue: { password: string }) {
return [
{
required: true,
message: '请再次输入密码',
trigger: ['input', 'blur'],
},
{
validator: (rule: FormItemRule, value: string) => {
return validatePasswordSame(formValue.password, value)
},
message: '两次密码输入不一致',
trigger: ['blur', 'password-input'],
},
]
}
11 changes: 11 additions & 0 deletions apps/teach-frontend/src/components/login/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function validatePasswordSame(password: string, value: string): boolean {
return value === password
}

export function validateUsernameLength(username: string) {
return username.length > 6 && username.length < 30
}

export function validatePasswordLength(username: string) {
return username.length > 6 && username.length < 30
}
Loading

0 comments on commit 7e2c427

Please sign in to comment.