feat: 用户列表重构
This commit is contained in:
parent
9a0259de5c
commit
755cea0990
|
@ -0,0 +1,105 @@
|
|||
import config from '@/config/axios/config'
|
||||
import { MockMethod } from 'vite-plugin-mock'
|
||||
import { toAnyString } from '@/utils'
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const { code } = config
|
||||
|
||||
const departmentList: any = []
|
||||
|
||||
const citys = ['厦门总公司', '北京分公司', '上海分公司', '福州分公司', '深圳分公司', '杭州分公司']
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
departmentList.push({
|
||||
// 部门名称
|
||||
departmentName: citys[i],
|
||||
id: toAnyString(),
|
||||
children: [
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '研发部',
|
||||
id: toAnyString()
|
||||
},
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '产品部',
|
||||
id: toAnyString()
|
||||
},
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '运营部',
|
||||
id: toAnyString()
|
||||
},
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '市场部',
|
||||
id: toAnyString()
|
||||
},
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '销售部',
|
||||
id: toAnyString()
|
||||
},
|
||||
{
|
||||
// 部门名称
|
||||
departmentName: '客服部',
|
||||
id: toAnyString()
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: '/department/list',
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
data: {
|
||||
code: code,
|
||||
data: {
|
||||
list: departmentList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/department/users',
|
||||
method: 'get',
|
||||
timeout: 1000,
|
||||
response: ({ query }) => {
|
||||
const { pageSize } = query
|
||||
// 根据pageSize来创建数据
|
||||
const mockList: any = []
|
||||
for (let i = 0; i < pageSize; i++) {
|
||||
mockList.push(
|
||||
Mock.mock({
|
||||
// 用户名
|
||||
username: '@cname',
|
||||
// 账号
|
||||
account: '@first',
|
||||
// 邮箱
|
||||
email: '@EMAIL',
|
||||
// 创建时间
|
||||
createTime: '@datetime',
|
||||
// 角色
|
||||
role: '@first',
|
||||
// 用户id
|
||||
id: toAnyString()
|
||||
})
|
||||
)
|
||||
}
|
||||
return {
|
||||
data: {
|
||||
code: code,
|
||||
data: {
|
||||
total: 100,
|
||||
list: mockList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
] as MockMethod[]
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/config/axios'
|
||||
import { DepartmentListResponse, DepartmentUserParams, DepartmentUserResponse } from './types'
|
||||
|
||||
export const getDepartmentApi = () => {
|
||||
return request.get<DepartmentListResponse>({ url: '/department/list' })
|
||||
}
|
||||
|
||||
export const getUserByIdApi = (params: DepartmentUserParams) => {
|
||||
return request.get<DepartmentUserResponse>({ url: '/department/users', params })
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
export interface DepartmentItem {
|
||||
id: string
|
||||
departmentName: string
|
||||
children?: DepartmentItem[]
|
||||
}
|
||||
|
||||
export interface DepartmentListResponse {
|
||||
list: DepartmentItem[]
|
||||
}
|
||||
|
||||
export interface DepartmentUserParams {
|
||||
pageSize: number
|
||||
pageIndex: number
|
||||
id: string
|
||||
username?: string
|
||||
account?: string
|
||||
}
|
||||
|
||||
export interface DepartmentUserItem {
|
||||
id: string
|
||||
username: string
|
||||
account: string
|
||||
email: string
|
||||
createTime: string
|
||||
role: string
|
||||
}
|
||||
|
||||
export interface DepartmentUserResponse {
|
||||
list: DepartmentUserItem[]
|
||||
total: number
|
||||
}
|
|
@ -255,7 +255,7 @@ export default defineComponent({
|
|||
const renderFormItem = (item: FormSchema) => {
|
||||
// 如果有optionApi,优先使用optionApi
|
||||
if (item.optionApi) {
|
||||
// 内部自动调用接口,不影响其他渲染
|
||||
// 内部自动调用接口,不影响其它渲染
|
||||
getOptions(item.optionApi, item)
|
||||
}
|
||||
const formItemSlots: Recordable = {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import Sticky from './src/Sticky.vue'
|
||||
|
||||
export { Sticky }
|
|
@ -1,141 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ref, onMounted, onActivated, shallowRef } from 'vue'
|
||||
import { useEventListener, useWindowSize, isClient } from '@vueuse/core'
|
||||
import type { CSSProperties } from 'vue'
|
||||
const props = defineProps({
|
||||
// 距离顶部或者底部的距离(单位px)
|
||||
offset: propTypes.number.def(0),
|
||||
// 设置元素的堆叠顺序
|
||||
zIndex: propTypes.number.def(999),
|
||||
// 设置指定的class
|
||||
className: propTypes.string.def(''),
|
||||
// 定位方式,默认为(top),表示距离顶部位置,可以设置为top或者bottom
|
||||
position: {
|
||||
type: String,
|
||||
validator: function (value: string) {
|
||||
return ['top', 'bottom'].indexOf(value) !== -1
|
||||
},
|
||||
default: 'top'
|
||||
}
|
||||
})
|
||||
const width = ref('auto' as string)
|
||||
const height = ref('auto' as string)
|
||||
const isSticky = ref(false)
|
||||
const refSticky = shallowRef<HTMLElement>()
|
||||
const scrollContainer = shallowRef<HTMLElement | Window>()
|
||||
const { height: windowHeight } = useWindowSize()
|
||||
onMounted(() => {
|
||||
height.value = refSticky.value?.getBoundingClientRect().height + 'px'
|
||||
|
||||
scrollContainer.value = getScrollContainer(refSticky.value!, true)
|
||||
useEventListener(scrollContainer, 'scroll', handleScroll)
|
||||
useEventListener('resize', handleReize)
|
||||
handleScroll()
|
||||
})
|
||||
onActivated(() => {
|
||||
handleScroll()
|
||||
})
|
||||
|
||||
const camelize = (str: string): string => {
|
||||
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
|
||||
}
|
||||
|
||||
const getStyle = (element: HTMLElement, styleName: keyof CSSProperties): string => {
|
||||
if (!isClient || !element || !styleName) return ''
|
||||
|
||||
let key = camelize(styleName)
|
||||
if (key === 'float') key = 'cssFloat'
|
||||
try {
|
||||
const style = element.style[styleName]
|
||||
if (style) return style
|
||||
const computed = document.defaultView?.getComputedStyle(element, '')
|
||||
return computed ? computed[styleName] : ''
|
||||
} catch {
|
||||
return element.style[styleName]
|
||||
}
|
||||
}
|
||||
const isScroll = (el: HTMLElement, isVertical?: boolean): boolean => {
|
||||
if (!isClient) return false
|
||||
const key = (
|
||||
{
|
||||
undefined: 'overflow',
|
||||
true: 'overflow-y',
|
||||
false: 'overflow-x'
|
||||
} as const
|
||||
)[String(isVertical)]!
|
||||
const overflow = getStyle(el, key)
|
||||
return ['scroll', 'auto', 'overlay'].some((s) => overflow.includes(s))
|
||||
}
|
||||
|
||||
const getScrollContainer = (
|
||||
el: HTMLElement,
|
||||
isVertical: boolean
|
||||
): Window | HTMLElement | undefined => {
|
||||
if (!isClient) return
|
||||
let parent = el
|
||||
while (parent) {
|
||||
if ([window, document, document.documentElement].includes(parent)) return window
|
||||
if (isScroll(parent, isVertical)) return parent
|
||||
parent = parent.parentNode as HTMLElement
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
width.value = refSticky.value!.getBoundingClientRect().width! + 'px'
|
||||
if (props.position === 'top') {
|
||||
const offsetTop = refSticky.value?.getBoundingClientRect().top
|
||||
if (offsetTop !== undefined && offsetTop < props.offset) {
|
||||
sticky()
|
||||
return
|
||||
}
|
||||
reset()
|
||||
} else {
|
||||
const offsetBottom = refSticky.value?.getBoundingClientRect().bottom
|
||||
|
||||
if (offsetBottom !== undefined && offsetBottom > windowHeight.value - props.offset) {
|
||||
sticky()
|
||||
return
|
||||
}
|
||||
reset()
|
||||
}
|
||||
}
|
||||
const handleReize = () => {
|
||||
if (isSticky.value && refSticky.value) {
|
||||
width.value = refSticky.value.getBoundingClientRect().width + 'px'
|
||||
}
|
||||
}
|
||||
const sticky = () => {
|
||||
if (isSticky.value) {
|
||||
return
|
||||
}
|
||||
isSticky.value = true
|
||||
}
|
||||
const reset = () => {
|
||||
if (!isSticky.value) {
|
||||
return
|
||||
}
|
||||
width.value = 'auto'
|
||||
isSticky.value = false
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div :style="{ height: height, zIndex: zIndex }" ref="refSticky">
|
||||
<div
|
||||
:class="className"
|
||||
:style="{
|
||||
top: position === 'top' ? offset + 'px' : '',
|
||||
bottom: position !== 'top' ? offset + 'px' : '',
|
||||
zIndex: zIndex,
|
||||
position: isSticky ? 'fixed' : 'static',
|
||||
width: width,
|
||||
height: height
|
||||
}"
|
||||
>
|
||||
<slot>
|
||||
<div>sticky</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -75,7 +75,7 @@ const closeAllTags = () => {
|
|||
toLastView()
|
||||
}
|
||||
|
||||
// 关闭其他
|
||||
// 关闭其它
|
||||
const closeOthersTags = () => {
|
||||
tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||
}
|
||||
|
@ -482,7 +482,8 @@ watch(
|
|||
|
||||
&__tool {
|
||||
position: relative;
|
||||
&:before {
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
|
@ -493,14 +494,14 @@ watch(
|
|||
}
|
||||
|
||||
&--first {
|
||||
&:before {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(~'100% - 1px');
|
||||
border-left: none;
|
||||
border-right: 1px solid var(--el-border-color);
|
||||
border-left: none;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
@ -553,7 +554,7 @@ watch(
|
|||
.@{prefix-cls} {
|
||||
&__tool {
|
||||
&--first {
|
||||
&:after {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -487,7 +487,14 @@ export default {
|
|||
role: 'Role',
|
||||
remark: 'Remark',
|
||||
remarkMessage1: 'Back end control routing permission',
|
||||
remarkMessage2: 'Front end control routing permission'
|
||||
remarkMessage2: 'Front end control routing permission',
|
||||
// 部门列表
|
||||
departmentList: 'Department list',
|
||||
// 搜索部门
|
||||
searchDepartment: 'Search department',
|
||||
account: 'Account',
|
||||
email: 'Email',
|
||||
createTime: 'Create time'
|
||||
},
|
||||
inputPasswordDemo: {
|
||||
title: 'InputPassword',
|
||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
|||
closeTab: '关闭标签页',
|
||||
closeTheLeftTab: '关闭左侧标签页',
|
||||
closeTheRightTab: '关闭右侧标签页',
|
||||
closeOther: '关闭其他标签页',
|
||||
closeOther: '关闭其它标签页',
|
||||
closeAll: '关闭全部标签页',
|
||||
prevLabel: '上一步',
|
||||
nextLabel: '下一步',
|
||||
|
@ -106,7 +106,7 @@ export default {
|
|||
register: '注册',
|
||||
checkPassword: '确认密码',
|
||||
login: '登录',
|
||||
otherLogin: '其他登录方式',
|
||||
otherLogin: '其它登录方式',
|
||||
remember: '记住我',
|
||||
hasUser: '已有账号?去登录',
|
||||
forgetPassword: '忘记密码',
|
||||
|
@ -480,7 +480,13 @@ export default {
|
|||
role: '角色',
|
||||
remark: '备注',
|
||||
remarkMessage1: '后端控制路由权限',
|
||||
remarkMessage2: '前端控制路由权限'
|
||||
remarkMessage2: '前端控制路由权限',
|
||||
// 部门列表
|
||||
departmentList: '部门列表',
|
||||
searchDepartment: '搜索部门',
|
||||
account: '账号',
|
||||
email: '邮箱',
|
||||
createTime: '创建时间'
|
||||
},
|
||||
inputPasswordDemo: {
|
||||
title: '密码输入框',
|
||||
|
|
|
@ -311,14 +311,6 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
title: t('router.inputPassword')
|
||||
}
|
||||
}
|
||||
// {
|
||||
// path: 'sticky',
|
||||
// component: () => import('@/views/Components/Sticky.vue'),
|
||||
// name: 'Sticky',
|
||||
// meta: {
|
||||
// title: t('router.sticky')
|
||||
// }
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -339,15 +331,15 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
meta: {
|
||||
title: 'useWatermark'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'useCrudSchemas',
|
||||
component: () => import('@/views/hooks/useCrudSchemas.vue'),
|
||||
name: 'UseCrudSchemas',
|
||||
meta: {
|
||||
title: 'useCrudSchemas'
|
||||
}
|
||||
}
|
||||
// {
|
||||
// path: 'useCrudSchemas',
|
||||
// component: () => import('@/views/hooks/useCrudSchemas.vue'),
|
||||
// name: 'UseCrudSchemas',
|
||||
// meta: {
|
||||
// title: 'useCrudSchemas'
|
||||
// }
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -513,36 +505,36 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/authorization',
|
||||
component: Layout,
|
||||
redirect: '/authorization/user',
|
||||
name: 'Authorization',
|
||||
meta: {
|
||||
title: t('router.authorization'),
|
||||
icon: 'eos-icons:role-binding',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: () => import('@/views/Authorization/User.vue'),
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: t('router.user')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: () => import('@/views/Authorization/Role.vue'),
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: t('router.role')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
// {
|
||||
// path: '/authorization',
|
||||
// component: Layout,
|
||||
// redirect: '/authorization/user',
|
||||
// name: 'Authorization',
|
||||
// meta: {
|
||||
// title: t('router.authorization'),
|
||||
// icon: 'eos-icons:role-binding',
|
||||
// alwaysShow: true
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'user',
|
||||
// component: () => import('@/views/Authorization/User.vue'),
|
||||
// name: 'User',
|
||||
// meta: {
|
||||
// title: t('router.user')
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'role',
|
||||
// component: () => import('@/views/Authorization/Role.vue'),
|
||||
// name: 'Role',
|
||||
// meta: {
|
||||
// title: t('router.role')
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
|
|
@ -37,7 +37,7 @@ interface AppState {
|
|||
export const useAppStore = defineStore('app', {
|
||||
state: (): AppState => {
|
||||
return {
|
||||
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
|
||||
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其它项目冲突
|
||||
sizeMap: ['default', 'large', 'small'],
|
||||
mobile: false, // 是否是移动端
|
||||
title: import.meta.env.VITE_APP_TITLE, // 标题
|
||||
|
@ -225,7 +225,7 @@ export const useAppStore = defineStore('app', {
|
|||
},
|
||||
setLayout(layout: LayoutType) {
|
||||
if (this.mobile && layout !== 'classic') {
|
||||
ElMessage.warning('移动端模式下不支持切换其他布局')
|
||||
ElMessage.warning('移动端模式下不支持切换其它布局')
|
||||
return
|
||||
}
|
||||
this.layout = layout
|
||||
|
|
|
@ -87,12 +87,12 @@ export const useTagsViewStore = defineStore('tagsView', {
|
|||
// const affixTags = this.visitedViews.filter((tag) => tag.meta.affix)
|
||||
this.visitedViews = []
|
||||
},
|
||||
// 删除其他
|
||||
// 删除其它
|
||||
delOthersViews(view: RouteLocationNormalizedLoaded) {
|
||||
this.delOthersVisitedViews(view)
|
||||
this.addCachedView()
|
||||
},
|
||||
// 删除其他tag
|
||||
// 删除其它tag
|
||||
delOthersVisitedViews(view: RouteLocationNormalizedLoaded) {
|
||||
this.visitedViews = this.visitedViews.filter((v) => {
|
||||
return v?.meta?.affix || v.path === view.path
|
||||
|
|
|
@ -1,20 +1,35 @@
|
|||
<script setup lang="ts">
|
||||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table } from '@/components/Table'
|
||||
import { getUserListApi } from '@/api/login'
|
||||
import { UserType } from '@/api/login/types'
|
||||
import { ref, h } from 'vue'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { TableColumn, TableSlotDefault } from '@/types/table'
|
||||
|
||||
interface Params {
|
||||
pageIndex?: number
|
||||
pageSize?: number
|
||||
}
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ref, unref, nextTick, watch } from 'vue'
|
||||
import { ElButton, ElTree, ElInput, ElDivider } from 'element-plus'
|
||||
import { getDepartmentApi, getUserByIdApi } from '@/api/department'
|
||||
import type { DepartmentItem, DepartmentUserItem } from '@/api/department/types'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { tableRegister, tableState, tableMethods } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { pageSize, currentPage } = tableState
|
||||
const res = await getUserByIdApi({
|
||||
id: unref(currentNodeKey),
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize),
|
||||
...unref(searchParams)
|
||||
})
|
||||
return {
|
||||
list: res.data.list || [],
|
||||
total: res.data.total || 0
|
||||
}
|
||||
}
|
||||
})
|
||||
const { total, loading, dataList, pageSize, currentPage } = tableState
|
||||
const { getList } = tableMethods
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
field: 'index',
|
||||
|
@ -26,65 +41,141 @@ const columns: TableColumn[] = [
|
|||
label: t('userDemo.username')
|
||||
},
|
||||
{
|
||||
field: 'password',
|
||||
label: t('userDemo.password')
|
||||
field: 'account',
|
||||
label: t('userDemo.account')
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
label: t('userDemo.role')
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: t('userDemo.remark'),
|
||||
formatter: (row: UserType) => {
|
||||
return h(
|
||||
'span',
|
||||
row.username === 'admin' ? t('userDemo.remarkMessage1') : t('userDemo.remarkMessage2')
|
||||
)
|
||||
}
|
||||
field: 'email',
|
||||
label: t('userDemo.email')
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
label: t('userDemo.createTime')
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('userDemo.action')
|
||||
label: t('userDemo.action'),
|
||||
slots: {
|
||||
default: (data) => {
|
||||
return (
|
||||
<ElButton type="primary" onClick={() => actionFn(data[0].row)}>
|
||||
{t('tableDemo.action')}
|
||||
</ElButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let tableDataList = ref<UserType[]>([])
|
||||
|
||||
const getTableList = async (params?: Params) => {
|
||||
const res = await getUserListApi({
|
||||
params: params || {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
})
|
||||
// .catch(() => {})
|
||||
// .finally(() => {
|
||||
// loading.value = false
|
||||
// })
|
||||
if (res) {
|
||||
tableDataList.value = res.data.list
|
||||
loading.value = false
|
||||
const searchSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: t('userDemo.username'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'account',
|
||||
label: t('userDemo.account'),
|
||||
component: 'Input'
|
||||
}
|
||||
]
|
||||
|
||||
const searchParams = ref({})
|
||||
const setSearchParams = (params: any) => {
|
||||
currentPage.value = 1
|
||||
searchParams.value = params
|
||||
getList()
|
||||
}
|
||||
|
||||
getTableList()
|
||||
|
||||
const actionFn = (data: TableSlotDefault) => {
|
||||
const actionFn = (data: DepartmentUserItem) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
const treeEl = ref<typeof ElTree>()
|
||||
|
||||
const currentNodeKey = ref('')
|
||||
const departmentList = ref<DepartmentItem[]>([])
|
||||
const fetchDepartment = async () => {
|
||||
const res = await getDepartmentApi()
|
||||
departmentList.value = res.data.list
|
||||
currentNodeKey.value =
|
||||
(res.data.list[0] && res.data.list[0]?.children && res.data.list[0].children[0].id) || ''
|
||||
await nextTick()
|
||||
unref(treeEl)?.setCurrentKey(currentNodeKey.value)
|
||||
}
|
||||
fetchDepartment()
|
||||
|
||||
const currentDepartment = ref('')
|
||||
watch(
|
||||
() => currentDepartment.value,
|
||||
(val) => {
|
||||
unref(treeEl)!.filter(val)
|
||||
}
|
||||
)
|
||||
|
||||
const currentChange = (data: DepartmentItem) => {
|
||||
if (data.children) return
|
||||
currentNodeKey.value = data.id
|
||||
currentPage.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const filterNode = (value: string, data: DepartmentItem) => {
|
||||
if (!value) return true
|
||||
return data.departmentName.includes(value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('userDemo.title')" :message="t('userDemo.message')">
|
||||
<Table :columns="columns" :data="tableDataList" :loading="loading" :selection="false">
|
||||
<template #action="data">
|
||||
<ElButton type="primary" @click="actionFn(data as TableSlotDefault)">
|
||||
{{ t('tableDemo.action') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
<div class="flex w-100% h-100%">
|
||||
<ContentWrap class="flex-1">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="flex-1">{{ t('userDemo.departmentList') }}</div>
|
||||
<ElInput
|
||||
v-model="currentDepartment"
|
||||
class="flex-[2]"
|
||||
:placeholder="t('userDemo.searchDepartment')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<ElTree
|
||||
ref="treeEl"
|
||||
:data="departmentList"
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:current-node-key="currentNodeKey"
|
||||
:props="{
|
||||
label: 'departmentName'
|
||||
}"
|
||||
:filter-node-method="filterNode"
|
||||
@current-change="currentChange"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<ContentWrap class="flex-[2] ml-20px">
|
||||
<Search
|
||||
:schema="searchSchema"
|
||||
@reset="setSearchParams"
|
||||
@search="setSearchParams"
|
||||
:search-loading="loading"
|
||||
/>
|
||||
<div>
|
||||
<Table
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
@register="tableRegister"
|
||||
:pagination="{
|
||||
total
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Sticky } from '@/components/Sticky'
|
||||
import { ElAffix } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('stickyDemo.sticky')">
|
||||
<Sticky :offset="90">
|
||||
<div style="padding: 10px; background-color: lightblue"> Sticky 距离顶部90px </div>
|
||||
</Sticky>
|
||||
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
|
||||
<el-affix :offset="150">
|
||||
<div style="padding: 10px; background-color: lightblue">Affix 距离顶部150px </div>
|
||||
</el-affix>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
|
||||
<el-affix :offset="150" position="bottom">
|
||||
<div style="padding: 10px; background-color: lightblue">Affix 距离底部150px </div>
|
||||
</el-affix>
|
||||
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
|
||||
<Sticky :offset="90" position="bottom">
|
||||
<div style="padding: 10px; background-color: lightblue"> Sticky 距离底部90px </div>
|
||||
</Sticky>
|
||||
<p style="margin: 80px">Content</p>
|
||||
<p style="margin: 80px">Content</p>
|
||||
</ContentWrap>
|
||||
</template>
|
Loading…
Reference in New Issue