forked from github/dataease
Merge pull request #8222 from dataease/pr@dev-v2_dzz_mobile
feat(移动端): 移动端切换组织
This commit is contained in:
commit
6c1cd0d021
131
core/core-frontend/src/views/mobile/components/OrgCell.vue
Normal file
131
core/core-frontend/src/views/mobile/components/OrgCell.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
prefixIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tips: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
nextlevel: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['click'])
|
||||
const handleLeftClick = () => {
|
||||
emits('click', !props.nextlevel ? 'all' : 'left')
|
||||
}
|
||||
|
||||
const handleRightClick = () => {
|
||||
emits('click', !props.nextlevel ? 'all' : 'right')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="org-cell">
|
||||
<div class="label" :class="active && 'active'">
|
||||
<el-icon v-if="!!prefixIcon">
|
||||
<Icon :name="prefixIcon"></Icon>
|
||||
</el-icon>
|
||||
{{ label }}
|
||||
</div>
|
||||
<div class="switch" v-if="nextlevel">
|
||||
<div class="tips">
|
||||
{{ tips }}
|
||||
</div>
|
||||
<el-icon>
|
||||
<Icon name="icon_right_outlined"></Icon>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="left-area" @click="handleLeftClick"></div>
|
||||
<div class="right-area" @click="handleRightClick"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.org-cell {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
background: #fff;
|
||||
&::after {
|
||||
content: '';
|
||||
background: #e4e5e7;
|
||||
height: 1px;
|
||||
width: calc(100% - 32px);
|
||||
transform: scaleY(0.5);
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.active {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
.ed-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #8f959e;
|
||||
.tips {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ed-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.left-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
|
||||
.right-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -97,7 +97,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="overflow-y: auto; height: calc(100vh - 50px)">
|
||||
<div>
|
||||
<van-sticky>
|
||||
<van-search
|
||||
v-if="!directName.length"
|
||||
@ -113,7 +113,7 @@ onMounted(() => {
|
||||
@click-left="onClickLeft"
|
||||
/>
|
||||
</van-sticky>
|
||||
<van-cell-group>
|
||||
<van-cell-group style="overflow-y: auto; height: calc(100vh - 104px)">
|
||||
<van-cell
|
||||
v-for="ele in activeTableData"
|
||||
:key="ele.id"
|
||||
|
@ -123,7 +123,7 @@ const formatterTime = val => {
|
||||
></van-tab>
|
||||
</van-tabs>
|
||||
</van-sticky>
|
||||
<van-cell-group>
|
||||
<van-cell-group style="overflow-y: auto; height: calc(100vh - 94px)">
|
||||
<van-cell
|
||||
@click="handleCellClick(ele)"
|
||||
v-for="ele in state.tableData"
|
||||
@ -134,6 +134,5 @@ const formatterTime = val => {
|
||||
icon="bar-chart-o"
|
||||
/>
|
||||
</van-cell-group>
|
||||
<div style="width: 100%; height: 50px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -44,5 +44,10 @@ const hiddenTabbar = ref(false)
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.van-hairline--top-bottom:after {
|
||||
bottom: auto;
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,44 +1,155 @@
|
||||
<script lang="ts" setup>
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import userImg from '@/assets/img/user.png'
|
||||
import { mountedOrg, switchOrg } from '@/api/user'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import OrgCell from '@/views/mobile/components/OrgCell.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { logoutApi } from '@/api/login'
|
||||
import { logoutHandler } from '@/utils/logout'
|
||||
import VanButton from 'vant/es/button'
|
||||
import VanCell from 'vant/es/cell'
|
||||
import VanIcon from 'vant/es/icon'
|
||||
import VanNavBar from 'vant/es/nav-bar'
|
||||
import VanImage from 'vant/es/image'
|
||||
import 'vant/es/image/style'
|
||||
import 'vant/es/icon/style'
|
||||
import 'vant/es/cell/style'
|
||||
import 'vant/es/button/style'
|
||||
import 'vant/es/nav-bar/style'
|
||||
|
||||
interface OrgTreeNode {
|
||||
id: string | number
|
||||
name: string
|
||||
readOnly: boolean
|
||||
children?: OrgTreeNode[]
|
||||
}
|
||||
const userStore = useUserStoreWithOut()
|
||||
const { push } = useRouter()
|
||||
|
||||
const navBarTitle = ref('请选择')
|
||||
const name = ref('')
|
||||
const showNavBar = ref(true)
|
||||
const logout = async () => {
|
||||
await logoutApi()
|
||||
logoutHandler()
|
||||
push('/login')
|
||||
}
|
||||
|
||||
const orgClick = () => {
|
||||
showNavBar.value = false
|
||||
}
|
||||
const activeDirectName = ref('')
|
||||
|
||||
let orgOption = null
|
||||
const findName = () => {
|
||||
const stack = [...orgOption]
|
||||
while (stack.length) {
|
||||
const item = stack.pop()
|
||||
if (item?.id === userStore.getOid) {
|
||||
name.value = item?.name
|
||||
break
|
||||
}
|
||||
if (item?.children?.length) {
|
||||
item.children.forEach(kid => stack.push(kid))
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
mountedOrg().then(res => {
|
||||
orgOption = res.data as OrgTreeNode[]
|
||||
tableData.value = res.data as OrgTreeNode[]
|
||||
findName()
|
||||
})
|
||||
})
|
||||
|
||||
const switchHandler = (id: number | string) => {
|
||||
switchOrg(id).then(res => {
|
||||
const token = res.data.token
|
||||
userStore.setToken(token)
|
||||
userStore.setExp(res.data.exp)
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
|
||||
const onClickLeft = () => {
|
||||
directName.value.pop()
|
||||
activeDirectName.value = directName.value[directName.value.length - 1]
|
||||
directId.value.pop()
|
||||
if (!directName.value.length) {
|
||||
showNavBar.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const orgCellClick = (type, val) => {
|
||||
if (type !== 'right') {
|
||||
switchHandler(val.id)
|
||||
} else {
|
||||
directName.value.push(val.name)
|
||||
activeDirectName.value = val.name
|
||||
directId.value.push(val.id)
|
||||
}
|
||||
}
|
||||
|
||||
const tableData = ref([])
|
||||
const directName = ref([])
|
||||
const directId = ref([])
|
||||
|
||||
const dfsTree = (ids, arr) => {
|
||||
const id = ids.shift()
|
||||
return arr.reduce((pre, ele) => {
|
||||
if (id && ele.id === id) {
|
||||
if (!ids.length) {
|
||||
return ele.children || []
|
||||
}
|
||||
const children = dfsTree([...ids], ele.children || [])
|
||||
pre = children || []
|
||||
}
|
||||
return pre
|
||||
}, [])
|
||||
}
|
||||
|
||||
const activeTableData = computed(() => {
|
||||
return directId.value.length ? dfsTree([...directId.value], tableData.value) : tableData.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="de-mobile-user">
|
||||
<div class="mobile-user-top">
|
||||
<van-image width="75" height="75" :src="userImg" />
|
||||
<div class="user-name">
|
||||
{{ userStore.name }}
|
||||
<template v-if="showNavBar">
|
||||
<div class="logout flex-center">我的</div>
|
||||
<div class="mobile-user-top">
|
||||
<van-image round width="48" height="48" :src="userImg" />
|
||||
<div class="user-name">
|
||||
{{ userStore.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-cell value="系统信息" is-link>
|
||||
<template #title>
|
||||
<van-icon name="user-o" class="search-icon" />
|
||||
<span class="custom-title">关于</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<div style="margin: 16px">
|
||||
<van-button round block type="primary" @click="logout"> 注销 </van-button>
|
||||
</div>
|
||||
<OrgCell
|
||||
@click="orgClick"
|
||||
label="切换组织"
|
||||
prefix-icon="icon_switch_outlined"
|
||||
:tips="name"
|
||||
nextlevel
|
||||
></OrgCell>
|
||||
<div class="logout flex-center danger" @click="logout">注销</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<van-nav-bar
|
||||
safe-area-inset-top
|
||||
:title="activeDirectName || navBarTitle"
|
||||
left-arrow
|
||||
@click-left="onClickLeft"
|
||||
/>
|
||||
<div class="grey">
|
||||
<div class="flex-align-center" v-for="(ele, index) in directName" :key="ele">
|
||||
<span :class="ele !== activeDirectName && 'active'">{{ ele }}</span>
|
||||
<el-icon v-if="directName.length > 1 && index !== directName.length - 1">
|
||||
<Icon name="icon_right_outlined"></Icon>
|
||||
</el-icon>
|
||||
</div>
|
||||
<span v-if="!directName.length">请选择组织</span>
|
||||
</div>
|
||||
<OrgCell
|
||||
@click="type => orgCellClick(type, ele)"
|
||||
v-for="ele in activeTableData"
|
||||
:key="ele.id"
|
||||
:label="ele.name"
|
||||
:nextlevel="ele.children"
|
||||
></OrgCell>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -46,33 +157,64 @@ const logout = async () => {
|
||||
.de-mobile-user {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
|
||||
background: #f5f6f7;
|
||||
--van-nav-bar-height: 44px;
|
||||
--van-nav-bar-arrow-size: 20px;
|
||||
--van-nav-bar-icon-color: #1f2329;
|
||||
--van-nav-bar-title-text-color: #1f2329;
|
||||
--van-font-bold: 500;
|
||||
--van-nav-bar-title-font-size: 17px;
|
||||
.mobile-user-top {
|
||||
padding: 24px 24px 16px 24px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
height: 80px;
|
||||
background: #fff;
|
||||
|
||||
.user-name {
|
||||
height: 45px;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
padding-top: 20px;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-cell__title) {
|
||||
.logout {
|
||||
margin-top: 8px;
|
||||
height: 48px;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
background: #fff;
|
||||
&.danger {
|
||||
color: var(--ed-color-danger);
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.grey {
|
||||
height: 44px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
color: #646a73;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.custom-title {
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
margin-left: 12px;
|
||||
}
|
||||
.active {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
font-size: 26px;
|
||||
line-height: inherit;
|
||||
.ed-icon {
|
||||
font-size: 12px;
|
||||
margin: 0 4px;
|
||||
color: #8f959e;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user