123 lines
3.5 KiB
Vue
123 lines
3.5 KiB
Vue
<script setup>
|
|
import { ref, computed, watch } from 'vue'
|
|
import { fetchUserOptions } from '@/api/admin'
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: Array, default: () => [] },
|
|
visible: { type: Boolean, default: false },
|
|
title: { type: String, default: '选择审批人' },
|
|
multiple: { type: Boolean, default: true }
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'update:visible', 'confirm'])
|
|
|
|
const keyword = ref('')
|
|
const loading = ref(false)
|
|
const users = ref([])
|
|
const draftIds = ref([])
|
|
|
|
const filtered = computed(() => {
|
|
const kw = String(keyword.value || '')
|
|
.trim()
|
|
.toLowerCase()
|
|
if (!kw) return users.value
|
|
return users.value.filter((u) => {
|
|
const blob = `${u.nickName} ${u.userNo} ${u.userId}`.toLowerCase()
|
|
return blob.includes(kw)
|
|
})
|
|
})
|
|
|
|
const isChecked = (userId) => draftIds.value.includes(String(userId))
|
|
|
|
const loadUsers = async () => {
|
|
loading.value = true
|
|
try {
|
|
users.value = await fetchUserOptions()
|
|
} catch (e) {
|
|
uni.showToast({ title: e.message || '加载用户失败', icon: 'none' })
|
|
users.value = []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => props.visible,
|
|
(v) => {
|
|
if (!v) return
|
|
draftIds.value = (props.modelValue || []).map(String)
|
|
keyword.value = ''
|
|
if (!users.value.length) loadUsers()
|
|
}
|
|
)
|
|
|
|
const close = () => emit('update:visible', false)
|
|
|
|
const toggle = (userId) => {
|
|
const id = String(userId)
|
|
if (!props.multiple) {
|
|
draftIds.value = [id]
|
|
return
|
|
}
|
|
const i = draftIds.value.indexOf(id)
|
|
if (i >= 0) draftIds.value.splice(i, 1)
|
|
else draftIds.value.push(id)
|
|
}
|
|
|
|
const confirm = () => {
|
|
if (!draftIds.value.length) {
|
|
uni.showToast({ title: '请至少选择一位审批人', icon: 'none' })
|
|
return
|
|
}
|
|
emit('update:modelValue', [...draftIds.value])
|
|
emit('confirm', [...draftIds.value])
|
|
close()
|
|
}
|
|
|
|
/** 根据已选 ID 解析展示名(父级摘要用) */
|
|
function resolveLabels(ids) {
|
|
const set = new Set((ids || []).map(String))
|
|
return users.value
|
|
.filter((u) => set.has(u.userId))
|
|
.map((u) => u.nickName || u.userNo || u.userId)
|
|
}
|
|
|
|
defineExpose({ loadUsers, resolveLabels, users })
|
|
</script>
|
|
|
|
<template>
|
|
<view v-if="visible" class="mp-sheet-mask" @click="close">
|
|
<view class="mp-sheet" @click.stop>
|
|
<view class="mp-sheet__head">
|
|
<text class="mp-sheet__cancel" @click="close">取消</text>
|
|
<text class="mp-sheet__title">{{ title }}</text>
|
|
<text class="mp-sheet__ok" @click="confirm">确定</text>
|
|
</view>
|
|
<view class="mp-sheet__search">
|
|
<input
|
|
v-model="keyword"
|
|
class="mp-sheet__search-input"
|
|
placeholder="搜索姓名 / 账号"
|
|
/>
|
|
</view>
|
|
<scroll-view scroll-y class="mp-sheet__body">
|
|
<view v-if="loading" class="mp-sheet__hint">加载中…</view>
|
|
<view v-else-if="!filtered.length" class="mp-sheet__hint">暂无用户</view>
|
|
<view
|
|
v-for="u in filtered"
|
|
:key="u.userId"
|
|
class="mp-sheet__row"
|
|
@click="toggle(u.userId)"
|
|
>
|
|
<view class="mp-sheet__check" :class="{ 'mp-sheet__check--on': isChecked(u.userId) }" />
|
|
<view class="mp-sheet__meta">
|
|
<text class="mp-sheet__name">{{ u.nickName || u.userNo || u.userId }}</text>
|
|
<text class="mp-sheet__sub">{{ u.userNo || u.userId }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<view class="mp-sheet__foot">已选 {{ draftIds.length }} 人</view>
|
|
</view>
|
|
</view>
|
|
</template>
|