Browse Source

登录页完成

pull/1/head
余菲 2 years ago
parent
commit
7666a64b92
9 changed files with 310 additions and 151 deletions
  1. +21
    -5
      src/components/DataTable/index.vue
  2. +75
    -0
      src/components/Search/index.vue
  3. +1
    -1
      src/store/modules/permission.js
  4. +7
    -30
      src/utils/http/interceptors.js
  5. +102
    -21
      src/views/login/index.vue
  6. +10
    -93
      src/views/system/role/index.vue
  7. +20
    -0
      src/views/system/role/info.js
  8. +73
    -0
      src/views/system/role/table.js
  9. +1
    -1
      src/views/system/user/index.vue

+ 21
- 5
src/components/DataTable/index.vue View File

@@ -9,7 +9,16 @@
<!--顶部右侧区域-->
<slot name="toolbar" />
<!--刷新-->
<span @click="reload">刷新</span>
<n-tooltip trigger="hover">
<template #trigger>
<div class="table-toolbar-right-icon" @click="reload">
<n-icon size="18">
<ReloadOutlined />
</n-icon>
</div>
</template>
<span>刷新</span>
</n-tooltip>
</div>
</div>
<div class="s-table">
@@ -24,12 +33,14 @@
</template>

<script>
import { ReloadOutlined } from '@vicons/antd'
import { tableProps } from './tools/props.js'
import { useDataSource } from './tools/useDataSource.js'
import { usePagination } from './tools/usePagination.js'
import { ref, unref, computed, toRaw, provide } from 'vue'
export default {
name: 'DataTable',
components: { ReloadOutlined },
props: {
...tableProps
},
@@ -66,12 +77,13 @@ export default {
/* tableData-start */
const tableData = ref([])
const { getDataSourceRef, getRowKey, reload } = useDataSource(getProps, { getPaginationInfo, setPagination, tableData, setLoading }, emit)
const isRequest = !!unref(getProps).request
const getBindProps = computed(() => {
return {
...unref(getProps),
loading: unref(getLoading),
rowKey: unref(getRowKey),
data: unref(getDataSourceRef),
data: isRequest ? unref(getDataSourceRef) : unref(getProps).data,
remote: true
}
})
@@ -84,7 +96,8 @@ export default {
getBindProps,
pagination,
updatePage,
updatePageSize
updatePageSize,
reload
}
}
}
@@ -104,12 +117,15 @@ export default {
.table-toolbar-right {
display: flex;
justify-content: flex-end;
align-items: center;
flex: 1;
.table-toolbar-icon {
.table-toolbar-right-icon {
margin-left: 12px;
font-size: 16px;
cursor: pointer;
color: var(--text-color);
.n-icon{
vertical-align: middle;
}
&:hover {
color: #1890ff;
}

+ 75
- 0
src/components/Search/index.vue View File

@@ -0,0 +1,75 @@
<template>
<div>
<n-form ref="formRef" v-bind="getFormOptions">
<template v-for="(item, index) in getFormOptions.info" :key="`${index}-${item.label}`">
<n-form-item v-if="!item.options" :label="item.label">
<n-input v-model:value="getFormOptions.form[item.key]" :placeholder="item.placeholder" />
</n-form-item>
<n-form-item v-if="item.options" :label="item.label">
<n-select v-model:value="getFormOptions.form[item.key]" :options="item.options" />
</n-form-item>
</template>
</n-form>
</div>
</template>

<script>
import { computed, reactive, ref, unref, toRaw } from 'vue'
import { NForm } from 'naive-ui'
export default {
name: 'SearchPage',
props: {
...NForm.props,
info: {
type: Array,
default: () => []
}
},
setup(props, { emit }) {
// const formOption = reactive({
// form: {

// }
// })
const getFormRef = computed(() => {
const { info } = unref(props)
const form = ref({})
info.forEach((item) => {
form.value[item.key] = ''
})
return unref(form)
})
console.log(getFormRef)
const getFormOptions = computed(() => {
return {
form: unref(getFormRef),
labelWidth: 'auto',
labelPlacement: 'left',
inline: true,
info: [...unref(props).info]
}
})
function getFormInfo() {
console.log(getFormOptions)
}
return {
getFormOptions,
getFormInfo
}
}
}

</script>
<style scoped lang='scss'>
.n-form{
flex-wrap: wrap;
}
.n-form-item{
.n-input{
width: 200px;
}
.n-select{
width: 200px;
}
}
</style>

+ 1
- 1
src/store/modules/permission.js View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { asyncRoutes, basicRoutes } from '@/router/routes'
import { getMenu } from '@/api/system'
import { getMenu } from '@/api/system/menu'
import Layout from '@/layout/index.vue'
import modules from '@/utils/module.js'


+ 7
- 30
src/utils/http/interceptors.js View File

@@ -17,12 +17,7 @@ export function setupInterceptor(service) {

const token = getToken()
if (token) {
/**
* * jwt token
* ! 认证方案: Bearer
*/
config.headers.Authorization = token

return config
}
/**
@@ -40,43 +35,25 @@ export function setupInterceptor(service) {
)

service.interceptors.response.use(
(response) => response?.data,
(error) => {
const { code, message } = error.response?.data
// return Promise.reject({ code, message })

/**
* TODO 此处可以根据后端返回的错误码自定义框架层面的错误处理
*/
// const { currentRoute } = router
(response) => {
const { code } = response?.data
const { currentRoute } = router
switch (code) {
case 401:
// 未登录(可能是token过期或者无效了)
console.error(message)
removeToken()
router.replace({
path: '/login'
// query: { ...currentRoute.query, redirect: currentRoute.path }
})
break
case 403:
// 没有权限
console.error(message)
break
case 404:
// 资源不存在
console.error(message)
break
default:
break
}
// 已知错误resolve,在业务代码中作提醒,未知错误reject,捕获错误统一提示接口异常(9000以上为业务类型错误,需要跟后端确定好)
if ([401, 403, 404].includes(code) || code >= 9000) {
return Promise.resolve({ code, message })
} else {
console.error('【err】' + error)
return Promise.reject({ message: '接口异常,请稍后重试!' })
}
return response?.data
},
(error) => {
return Promise.reject(error)
}
)
}

+ 102
- 21
src/views/login/index.vue View File

@@ -1,43 +1,124 @@
<template>
<div>
<n-button @click="handleLogin">登录</n-button>
<div class="login_bg">
<n-form
ref="formRef"
:model="loginForm"
:rules="rules"
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging"
:style="{
maxWidth: '640px',
backgroundColor: '#fff',
padding: '20px',
borderRadius: '10px'
}"
@keyup.enter="handleLogin"
>
<n-form-item label="用户名" path="username">
<n-input v-model:value="loginForm.username" placeholder="请输入用户名" />
</n-form-item>
<n-form-item label="密码" path="password">
<n-input v-model:value="loginForm.password" type="password" placeholder="请输入用户名" />
</n-form-item>
<n-form-item label="验证码" path="captcha">
<n-input v-model:value="loginForm.captcha" placeholder="请输入验证码" />
<img v-if="captcha" :src="captcha" alt="" @click="changeCode">
</n-form-item>
<n-form-item path="remember">
<n-checkbox v-model:checked="loginForm.remember" size="medium" label="记住密码" />
</n-form-item>
<n-form-item>
<n-button @click="handleLogin">登录</n-button>
</n-form-item>
</n-form>
</div>
</template>

<script>
import { userLogin, userCaptcha } from '@/api/login/index.js'
import { setToken } from '@/utils/token'
import { ref, reactive, onMounted } from 'vue'
export default {
name: 'LoginPage',
setup() {
async function handleLogin() {
try {
const params = {
captcha: '1',
key: '9132e06a-e2f0-4a9d-88da-b43ced72bb2e',
password: '123456',
remember: false,
username: 'admin'
}
const res = await userLogin(params)
if (res.code === 0) {
setToken(res.data.access_token)
}
} catch (error) {
// console.log(error)
const loginForm = reactive({
username: '',
password: '',
key: '',
captcha: '',
remember: false
})
onMounted(() => {
changeCode(loginForm)
})
// 获取图形验证码
const captcha = ref('')
async function changeCode(form) {
const params = {
username: form.username,
password: form.password,
captcha: form.captcha
}
const captForm = await userCaptcha(params)
captcha.value = captForm.data.captcha
loginForm.key = captForm.data.key
}

// userCaptcha()

return {
handleLogin
loginForm,
captcha,
changeCode,
rules: reactive({
username: {
required: true,
trigger: ['blur', 'input'],
message: '请输入用户名'
},
password: {
required: true,
trigger: ['blur', 'input'],
message: '请输入密码'
},
captcha: {
required: true,
trigger: ['blur', 'input'],
message: '请输入验证码'
}
})
}
},
methods: {
handleLogin() {
console.log(this.loginForm)
userLogin(this.loginForm).then((res) => {
if (res.code === 0) {
// 登录成功存储token,跳转首页
setToken(res.data.access_token)
this.goPage()
} else {
console.log(res)
}
})
},
// 登录成功跳转
goPage() {
// const query = this.$route.query
// const path = query && query.from ? query.from : '/'
// this.$router.push(path)
this.$router.replace('/home')
}
}
}
</script>

<style lang="scss" scoped>

.login_bg {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
</style>


+ 10
- 93
src/views/system/role/index.vue View File

@@ -1,6 +1,9 @@
<template>
<div>
<n-card>

<headSearch :info="data.info" />

<data-table
:columns="data.columns"
:row-key="(row) => row.id"
@@ -22,87 +25,21 @@
</template>

<script>
import headSearch from '@/components/Search/index.vue'
import dataTable from '@/components/DataTable/index.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import TableImage from '@/components/DataTable/tools/Image.vue'
import { getUserList } from '@/api/system/index.js'
import { h, onMounted, unref } from 'vue'
import { h, ref, unref } from 'vue'
import { reactive } from 'vue'
import table from './table.js'
import info from './info.js'
export default {
name: 'MenuPage',
components: { dataTable },
components: { headSearch, dataTable },
setup() {
const data = reactive({
columns: [
{
title: '用户编号',
key: 'code',
align: 'center',
minWidth: 80
},
{
title: '头像',
key: 'avatar',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.avatar
}
})
},
minWidth: 80
},
{
title: '用户账号',
key: 'username',
align: 'center',
minWidth: 80
},
{
title: '用户姓名',
key: 'realname',
align: 'center',
minWidth: 80
},
{
title: '用户类型',
key: 'type',
align: 'center',
minWidth: 80
},
{
title: '角色',
key: 'roles',
align: 'center',
minWidth: 100
},
{
title: '状态',
key: 'status',
align: 'center',
minWidth: 80
},
{
title: '部门',
key: 'deptName',
align: 'center',
minWidth: 120
},
{
title: '创建时间',
key: 'createTime',
align: 'center',
minWidth: 160
},
{
title: '更新时间',
key: 'updateTime',
align: 'center',
minWidth: 160
},
...table.columns,
{
title: '操作',
align: 'center',
@@ -135,29 +72,13 @@ export default {
}
}
],
data: [],
pagination: {
pageSize: 10
}
info: ref(info)
})

function play(row) {
console.log(row)
}

/**
* @description: 获取用户数据
* @return {*}
*/
async function fetchList() {
const params = {
page: 1,
limit: 10
}
const res = await getUserList(params)
data.data = res.data
}

const params = reactive({
name: 'xiaoMa'
})
@@ -170,10 +91,6 @@ export default {
return await getUserList(_params)
}

onMounted(() => {
fetchList()
})

return { data, loadDataTable }
}
}

+ 20
- 0
src/views/system/role/info.js View File

@@ -0,0 +1,20 @@
const data = [
{
label: '角色名称',
key: 'name',
placeholder: '请输入角色名称'
},
{
label: '角色备注',
key: 'desc'
},
{
label: '角色类型',
key: 'op',
options: [{
label: 11, value: 1
}]
}
]

export default data

+ 73
- 0
src/views/system/role/table.js View File

@@ -0,0 +1,73 @@
import TableImage from '@/components/DataTable/tools/Image.vue'
import { h } from 'vue'

const data = {
columns: [
{
title: '用户编号',
key: 'code',
align: 'center'
},
{
title: '头像',
key: 'avatar',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.avatar
}
})
}
},
{
title: '用户账号',
key: 'username',
align: 'center'
},
{
title: '用户姓名',
key: 'realname',
align: 'center'
},
{
title: '用户类型',
key: 'type',
align: 'center',
width: 100
},
{
title: '角色',
key: 'roles',
align: 'center'

},
{
title: '状态',
key: 'status',
align: 'center',
width: 100
},
{
title: '部门',
key: 'deptName',
align: 'center'
},
{
title: '创建时间',
key: 'createTime',
align: 'center',
width: 160
},
{
title: '更新时间',
key: 'updateTime',
align: 'center',
width: 160
}
]
}

export default data

+ 1
- 1
src/views/system/user/index.vue View File

@@ -147,7 +147,7 @@ export default {
limit: 10
}
const res = await getUserList(params)
data.data = res.data
data.data = res.data.list
}

const params = reactive({

Loading…
Cancel
Save