Merge pull request #8222 from dataease/pr@dev-v2_dzz_mobile

feat(移动端): 移动端切换组织
This commit is contained in:
dataeaseShu 2024-02-28 16:19:19 +08:00 committed by GitHub
commit 6c1cd0d021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 318 additions and 41 deletions

View 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>

View File

@ -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"

View File

@ -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>

View File

@ -44,5 +44,10 @@ const hiddenTabbar = ref(false)
width: 100vw;
height: 100vh;
overflow: hidden;
.van-hairline--top-bottom:after {
bottom: auto;
top: auto;
}
}
</style>

View File

@ -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>