feat:优化 user-pciker、dept-picker、user-picker 的封装
This commit is contained in:
@@ -67,7 +67,7 @@ const userList = ref<User[]>([]) // 用户列表
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
navigateBackPlus('/pages-system/dept/index')
|
||||
}
|
||||
|
||||
/** 获取上级部门名称 */
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<!-- TODO @芋艿:【优化】看看后续要不要抽成组件! -->
|
||||
<template>
|
||||
<wd-col-picker
|
||||
v-model="selectedValue"
|
||||
label="归属部门"
|
||||
:label="label"
|
||||
label-width="180rpx"
|
||||
:columns="deptColumns"
|
||||
:column-change="handleColumnChange"
|
||||
@@ -12,13 +11,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Dept } from '@/api/system/dept'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { getSimpleDeptList } from '@/api/system/dept'
|
||||
import type {Dept} from '@/api/system/dept'
|
||||
import {getSimpleDeptList} from '@/api/system/dept'
|
||||
import {onMounted, ref, watch} from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: number
|
||||
}>()
|
||||
label?: string
|
||||
showRoot?: boolean // 是否显示顶级部门节点
|
||||
}>(), {
|
||||
label: '上级部门',
|
||||
showRoot: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: number | undefined): void
|
||||
@@ -32,26 +36,54 @@ const selectedValue = ref<number[]>([])
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val && deptList.value.length > 0) {
|
||||
// 0 或 undefined 都视为顶级部门(如果允许显示顶级)
|
||||
if (val && val !== 0 && deptList.value.length > 0) {
|
||||
const path = findDeptPath(val)
|
||||
selectedValue.value = path
|
||||
// 构建列数据以支持回显
|
||||
buildColumnsForPath(path)
|
||||
}
|
||||
else {
|
||||
selectedValue.value = []
|
||||
} else {
|
||||
if (props.showRoot) {
|
||||
// 顶级部门或未选择,重置
|
||||
selectedValue.value = [0]
|
||||
} else {
|
||||
selectedValue.value = []
|
||||
}
|
||||
// 重新构建第一列,确保正确
|
||||
if (deptList.value.length > 0) {
|
||||
initFirstColumn()
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 初始化第一列 */
|
||||
function initFirstColumn() {
|
||||
const topDepts = deptList.value.filter(item => item.parentId === 0)
|
||||
if (props.showRoot) {
|
||||
deptColumns.value = [
|
||||
[
|
||||
{ label: '顶级部门', value: 0 },
|
||||
...topDepts.map(item => ({ value: item.id, label: item.name }))
|
||||
]
|
||||
]
|
||||
} else {
|
||||
deptColumns.value = [
|
||||
topDepts.map(item => ({ value: item.id, label: item.name }))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载部门列表 */
|
||||
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 }))]
|
||||
|
||||
// 初始化第一列
|
||||
initFirstColumn()
|
||||
|
||||
// 如果有初始值,回显
|
||||
if (props.modelValue) {
|
||||
if (props.modelValue && props.modelValue !== 0) {
|
||||
const path = findDeptPath(props.modelValue)
|
||||
selectedValue.value = path
|
||||
buildColumnsForPath(path)
|
||||
@@ -98,11 +130,14 @@ function buildColumnsForPath(path: number[]) {
|
||||
|
||||
/** 列变化 */
|
||||
function handleColumnChange({ selectedItem, resolve, finish }: any) {
|
||||
if (selectedItem.value === 0) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
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 {
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -116,9 +151,9 @@ function displayFormat(selectedItems: any[]) {
|
||||
function handleConfirm({ value }: { value: number[] }) {
|
||||
if (value && value.length > 0) {
|
||||
emit('update:modelValue', value[value.length - 1])
|
||||
}
|
||||
else {
|
||||
emit('update:modelValue', undefined)
|
||||
} else {
|
||||
// 如果允许 root,默认顶级 0;否则 undefined
|
||||
emit('update:modelValue', props.showRoot ? 0 : undefined)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,10 @@
|
||||
<view>
|
||||
<wd-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
title="上级部门"
|
||||
title-width="180rpx"
|
||||
prop="parentId"
|
||||
is-link
|
||||
:value="getParentName()"
|
||||
@click="showDeptPicker = true"
|
||||
<DeptPicker
|
||||
v-model="formData.parentId"
|
||||
label="上级部门"
|
||||
:show-root="true"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
@@ -33,13 +30,9 @@
|
||||
:min="0"
|
||||
/>
|
||||
</wd-cell>
|
||||
<wd-cell
|
||||
title="负责人"
|
||||
title-width="180rpx"
|
||||
prop="leaderUserId"
|
||||
is-link
|
||||
:value="getLeaderName()"
|
||||
@click="showUserPicker = true"
|
||||
<UserPicker
|
||||
v-model="formData.leaderUserId"
|
||||
type="radio"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.phone"
|
||||
@@ -79,35 +72,18 @@
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 上级部门选择器 -->
|
||||
<wd-picker
|
||||
:model-value="showDeptPicker"
|
||||
:columns="deptPickerColumns"
|
||||
title="选择上级部门"
|
||||
@confirm="handleDeptConfirm"
|
||||
@close="showDeptPicker = false"
|
||||
/>
|
||||
|
||||
<!-- 负责人选择器 -->
|
||||
<wd-picker
|
||||
:model-value="showUserPicker"
|
||||
:columns="userPickerColumns"
|
||||
title="选择负责人"
|
||||
@confirm="handleUserConfirm"
|
||||
@close="showUserPicker = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Dept } from '@/api/system/dept'
|
||||
import type { User } from '@/api/system/user'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { createDept, getDept, getSimpleDeptList, updateDept } from '@/api/system/dept'
|
||||
import { getSimpleUserList } from '@/api/system/user'
|
||||
import { createDept, getDept, updateDept } from '@/api/system/dept'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import DeptPicker from './components/dept-picker.vue'
|
||||
import UserPicker from '@/pages-system/user/form/components/user-picker.vue'
|
||||
import {navigateBackPlus} from '@/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number | any
|
||||
@@ -142,54 +118,9 @@ const formRules = {
|
||||
}
|
||||
const formRef = ref()
|
||||
|
||||
const deptList = ref<Dept[]>([]) // 部门列表
|
||||
const userList = ref<User[]>([]) // 用户列表
|
||||
const showDeptPicker = ref(false) // 部门选择器
|
||||
const showUserPicker = ref(false) // 负责人选择器
|
||||
|
||||
/** 部门选择器列 */
|
||||
const deptPickerColumns = computed(() => {
|
||||
const items = [{ label: '顶级部门', value: 0 }]
|
||||
deptList.value.forEach((dept) => {
|
||||
// 编辑时排除自己和子部门
|
||||
if (props.id && dept.id === props.id) {
|
||||
return
|
||||
}
|
||||
items.push({ label: dept.name, value: dept.id! })
|
||||
})
|
||||
return items
|
||||
})
|
||||
|
||||
/** 用户选择器列 */
|
||||
const userPickerColumns = computed(() => {
|
||||
const items = [{ label: '不设置', value: 0 }]
|
||||
userList.value.forEach((user) => {
|
||||
items.push({ label: user.nickname, value: user.id! })
|
||||
})
|
||||
return items
|
||||
})
|
||||
|
||||
/** 获取上级部门名称 */
|
||||
function getParentName(): string {
|
||||
if (!formData.value.parentId || formData.value.parentId === 0) {
|
||||
return '顶级部门'
|
||||
}
|
||||
const parent = deptList.value.find(d => d.id === formData.value.parentId)
|
||||
return parent?.name || '请选择'
|
||||
}
|
||||
|
||||
/** 获取负责人名称 */
|
||||
function getLeaderName(): string {
|
||||
if (!formData.value.leaderUserId) {
|
||||
return '请选择'
|
||||
}
|
||||
const user = userList.value.find(u => u.id === formData.value.leaderUserId)
|
||||
return user?.nickname || '请选择'
|
||||
}
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
uni.navigateBack()
|
||||
navigateBackPlus('/pages-system/dept/index')
|
||||
}
|
||||
|
||||
/** 加载部门详情 */
|
||||
@@ -200,16 +131,6 @@ async function getDetail() {
|
||||
formData.value = await getDept(props.id)
|
||||
}
|
||||
|
||||
/** 部门选择确认 */
|
||||
function handleDeptConfirm({ value }: { value: number }) {
|
||||
formData.value.parentId = value
|
||||
}
|
||||
|
||||
/** 负责人选择确认 */
|
||||
function handleUserConfirm({ value }: { value: number }) {
|
||||
formData.value.leaderUserId = value || undefined
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value.validate()
|
||||
@@ -236,10 +157,6 @@ async function handleSubmit() {
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
// 获取部门列表
|
||||
deptList.value = await getSimpleDeptList()
|
||||
// 获取用户列表
|
||||
userList.value = await getSimpleUserList()
|
||||
// 获取详情
|
||||
await getDetail()
|
||||
})
|
||||
|
||||
56
src/pages-system/post/form/components/post-picker.vue
Normal file
56
src/pages-system/post/form/components/post-picker.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<wd-select-picker
|
||||
v-model="selectedIds"
|
||||
label="岗位"
|
||||
label-width="180rpx"
|
||||
:columns="columns"
|
||||
type="checkbox"
|
||||
filterable
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</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 postList = ref<Post[]>([])
|
||||
const selectedIds = ref<number[]>([])
|
||||
|
||||
/** 构建 columns 数据 */
|
||||
const columns = computed(() => {
|
||||
return postList.value.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}))
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
selectedIds.value = val || []
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
async function loadPostList() {
|
||||
postList.value = await getSimplePostList()
|
||||
}
|
||||
|
||||
function handleConfirm({ value }: { value: number[] }) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadPostList()
|
||||
})
|
||||
</script>
|
||||
@@ -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>
|
||||
67
src/pages-system/user/form/components/user-picker.vue
Normal file
67
src/pages-system/user/form/components/user-picker.vue
Normal 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>
|
||||
@@ -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<{
|
||||
|
||||
Reference in New Issue
Block a user