登录页完成
This commit is contained in:
commit
7666a64b92
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
const data = [
|
||||
{
|
||||
label: '角色名称',
|
||||
key: 'name',
|
||||
placeholder: '请输入角色名称'
|
||||
},
|
||||
{
|
||||
label: '角色备注',
|
||||
key: 'desc'
|
||||
},
|
||||
{
|
||||
label: '角色类型',
|
||||
key: 'op',
|
||||
options: [{
|
||||
label: 11, value: 1
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
export default data
|
||||
|
|
@ -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
|
||||
|
|
@ -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…
Reference in New Issue