feat:优化 user-pciker、dept-picker、user-picker 的封装

This commit is contained in:
YunaiV
2025-12-15 16:04:35 +08:00
parent d96d9da0da
commit 817098a04d
7 changed files with 197 additions and 201 deletions

View File

@@ -1,129 +0,0 @@
<!-- TODO @芋艿优化看看后续要不要抽成组件 -->
<template>
<wd-col-picker
v-model="selectedValue"
label="归属部门"
label-width="180rpx"
:columns="deptColumns"
:column-change="handleColumnChange"
:display-format="displayFormat"
@confirm="handleConfirm"
/>
</template>
<script lang="ts" setup>
import type { Dept } from '@/api/system/dept'
import { onMounted, ref, watch } from 'vue'
import { getSimpleDeptList } from '@/api/system/dept'
const props = defineProps<{
modelValue?: number
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: number | undefined): void
}>()
const deptList = ref<Dept[]>([])
const deptColumns = ref<any[]>([])
const selectedValue = ref<number[]>([])
/** 监听外部值变化,回显选中值 */
watch(
() => props.modelValue,
(val) => {
if (val && deptList.value.length > 0) {
const path = findDeptPath(val)
selectedValue.value = path
// 构建列数据以支持回显
buildColumnsForPath(path)
}
else {
selectedValue.value = []
}
},
)
/** 加载部门列表 */
async function loadDeptList() {
deptList.value = await getSimpleDeptList()
// 构建第一列数据(顶级部门)
const topDepts = deptList.value.filter(item => item.parentId === 0)
deptColumns.value = [topDepts.map(item => ({ value: item.id, label: item.name }))]
// 如果有初始值,回显
if (props.modelValue) {
const path = findDeptPath(props.modelValue)
selectedValue.value = path
buildColumnsForPath(path)
}
}
/** 查找部门路径 */
function findDeptPath(targetId: number): number[] {
const path: number[] = []
const findPath = (parentId: number, id: number): boolean => {
const items = deptList.value.filter(d => d.parentId === parentId)
for (const item of items) {
if (item.id === id) {
path.push(item.id)
return true
}
if (findPath(item.id, id)) {
path.unshift(item.id)
return true
}
}
return false
}
findPath(0, targetId)
return path
}
/** 根据路径构建列数据 */
function buildColumnsForPath(path: number[]) {
if (path.length === 0) {
return
}
// 第一列已经有了,从第二列开始构建
const columns = [deptColumns.value[0]]
for (let i = 0; i < path.length - 1; i++) {
const parentId = path[i]
const children = deptList.value.filter(item => item.parentId === parentId)
if (children.length > 0) {
columns.push(children.map(item => ({ value: item.id, label: item.name })))
}
}
deptColumns.value = columns
}
/** 列变化 */
function handleColumnChange({ selectedItem, resolve, finish }: any) {
const children = deptList.value.filter(item => item.parentId === selectedItem.value)
if (children.length > 0) {
resolve(children.map(item => ({ value: item.id, label: item.name })))
}
else {
finish()
}
}
/** 格式化显示 */
function displayFormat(selectedItems: any[]) {
return selectedItems.map(item => item.label).join('/')
}
/** 确认选择 */
function handleConfirm({ value }: { value: number[] }) {
if (value && value.length > 0) {
emit('update:modelValue', value[value.length - 1])
}
else {
emit('update:modelValue', undefined)
}
}
/** 初始化 */
onMounted(() => {
loadDeptList()
})
</script>

View File

@@ -1,82 +0,0 @@
<!-- TODO @芋艿优化看看后续要不要抽成组件 -->
<template>
<!-- 岗位选择单元格 -->
<wd-cell title="岗位" title-width="180rpx" is-link @click="popupVisible = true">
<view class="text-left">
{{ displayText }}
</view>
</wd-cell>
<!-- 岗位选择弹窗 -->
<wd-popup v-model="popupVisible" position="bottom" custom-style="border-radius: 24rpx 24rpx 0 0;">
<view class="p-32rpx">
<view class="mb-24rpx flex items-center justify-between">
<text class="text-32rpx text-[#333] font-semibold">选择岗位</text>
<wd-icon name="close" size="20px" @click="popupVisible = false" />
</view>
<wd-checkbox-group v-model="selectedIds" cell shape="button">
<wd-checkbox v-for="item in postList" :key="item.id" :model-value="item.id">
{{ item.name }}
</wd-checkbox>
</wd-checkbox-group>
<view class="mt-32rpx">
<wd-button type="primary" block @click="handleConfirm">
确定
</wd-button>
</view>
</view>
</wd-popup>
</template>
<script lang="ts" setup>
import type { Post } from '@/api/system/post'
import { computed, onMounted, ref, watch } from 'vue'
import { getSimplePostList } from '@/api/system/post'
const props = defineProps<{
modelValue?: number[]
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: number[]): void
}>()
const popupVisible = ref(false)
const postList = ref<Post[]>([])
const selectedIds = ref<number[]>([])
const displayText = computed(() => {
if (!selectedIds.value || selectedIds.value.length === 0) {
return ''
}
return postList.value
.filter(item => selectedIds.value.includes(item.id))
.map(item => item.name)
.join('、')
}) // 显示文本
/** 监听外部值变化 */
watch(
() => props.modelValue,
(val) => {
selectedIds.value = val || []
},
{ immediate: true },
)
/** 加载岗位列表 */
async function loadPostList() {
postList.value = await getSimplePostList()
}
/** 确认选择 */
function handleConfirm() {
emit('update:modelValue', selectedIds.value)
popupVisible.value = false
}
/** 初始化 */
onMounted(() => {
loadPostList()
})
</script>

View File

@@ -0,0 +1,67 @@
<template>
<wd-select-picker
v-model="selectedId"
:label="label"
label-width="180rpx"
:columns="columns"
:type="type"
filterable
@confirm="handleConfirm"
/>
</template>
<script lang="ts" setup>
import type { User } from '@/api/system/user'
import { computed, onMounted, ref, watch } from 'vue'
import { getSimpleUserList } from '@/api/system/user'
const props = withDefaults(defineProps<{
modelValue?: number | number[]
type?: 'radio' | 'checkbox'
label?: string
}>(), {
type: 'checkbox',
label: '负责人'
})
const emit = defineEmits<{
(e: 'update:modelValue', value: number | number[] | undefined): void
}>()
const userList = ref<User[]>([])
const selectedId = ref<number | string | number[]>([])
// 构建 columns 数据
const columns = computed(() => {
return userList.value.map(user => ({
label: user.nickname,
value: user.id
}))
})
watch(
() => props.modelValue,
(val) => {
if (props.type === 'radio') {
// 单选时,如果值为 undefined使用空字符串避免警告
selectedId.value = val !== undefined ? val : ''
} else {
// 多选时,确保是数组
selectedId.value = Array.isArray(val) ? val : []
}
},
{ immediate: true }
)
async function loadUserList() {
userList.value = await getSimpleUserList()
}
function handleConfirm({ value }: { value: any }) {
emit('update:modelValue', value)
}
onMounted(() => {
loadUserList()
})
</script>

View File

@@ -37,7 +37,10 @@
clearable
placeholder="请输入用户昵称"
/>
<DeptPicker v-model="formData.deptId" />
<DeptPicker
v-model="formData.deptId"
label="归属部门"
/>
<PostPicker v-model="formData.postIds" />
<wd-input
v-model="formData.email"
@@ -104,8 +107,8 @@ import { createUser, getUser, updateUser } from '@/api/system/user'
import { getIntDictOptions } from '@/hooks/useDict'
import { CommonStatusEnum, DICT_TYPE } from '@/utils/constants'
import { isEmail, isMobile } from '@/utils/validator'
import DeptPicker from './components/dept-picker.vue'
import PostPicker from './components/post-picker.vue'
import DeptPicker from '@/pages-system/dept/form/components/dept-picker.vue'
import PostPicker from '@/pages-system/post/form/components/post-picker.vue'
import { navigateBackPlus } from '@/utils';
const props = defineProps<{