| | |
| | | <template> |
| | | <view class="contact-select"> |
| | | <!-- 顶部标题栏 --> |
| | | <PageHeader title="选择联系人" @back="goBack"> |
| | | <PageHeader title="选择联系人" |
| | | @back="goBack"> |
| | | <template #right> |
| | | <text class="confirm-btn" @click="confirmSelect">确定</text> |
| | | <text class="confirm-btn" |
| | | @click="confirmSelect">确定</text> |
| | | </template> |
| | | </PageHeader> |
| | | |
| | | <!-- 搜索框 --> |
| | | <!-- <view class="search-section">--> |
| | | <!-- <van-search--> |
| | | <!-- v-model="searchValue"--> |
| | | <!-- placeholder="搜索联系人"--> |
| | | <!-- @search="onSearch"--> |
| | | <!-- @input="onSearch"--> |
| | | <!-- />--> |
| | | <!-- </view>--> |
| | | |
| | | <!-- <view class="search-section">--> |
| | | <!-- <van-search--> |
| | | <!-- v-model="searchValue"--> |
| | | <!-- placeholder="搜索联系人"--> |
| | | <!-- @search="onSearch"--> |
| | | <!-- @input="onSearch"--> |
| | | <!-- />--> |
| | | <!-- </view>--> |
| | | <!-- 已选择的联系人 --> |
| | | <view class="selected-section" v-if="selectedContact"> |
| | | <view class="selected-section" |
| | | v-if="selectedContact"> |
| | | <view class="selected-header"> |
| | | <text class="selected-title">已选择</text> |
| | | <text class="clear-btn" @click="clearSelected">清空</text> |
| | | <text class="clear-btn" |
| | | @click="clearSelected">清空</text> |
| | | </view> |
| | | <view class="selected-item"> |
| | | <view class="contact-avatar"> |
| | |
| | | <view class="contact-details"> |
| | | <text class="contact-name">{{ selectedContact.nickName }}</text> |
| | | </view> |
| | | <u-icon name="close" size="16" color="#999" @click="clearSelected" /> |
| | | <u-icon name="close" |
| | | size="16" |
| | | color="#999" |
| | | @click="clearSelected" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 联系人列表 --> |
| | | <view class="contact-list"> |
| | | <view class="list-header"> |
| | | <text class="list-title">全部联系人</text> |
| | | </view> |
| | | |
| | | <u-list |
| | | v-model:loading="loading" |
| | | :finished="finished" |
| | | finished-text="没有更多了" |
| | | @load="onLoad" |
| | | > |
| | | <view |
| | | v-for="contact in userList" |
| | | :key="contact.userId" |
| | | class="contact-item" |
| | | :class="{ 'selected': isSelected(contact) }" |
| | | @click="selectContact(contact)" |
| | | > |
| | | <u-list v-model:loading="loading" |
| | | :finished="finished" |
| | | finished-text="没有更多了" |
| | | @load="onLoading"> |
| | | <view v-for="contact in userList" |
| | | :key="contact.userId" |
| | | class="contact-item" |
| | | :class="{ 'selected': isSelected(contact) }" |
| | | @click="selectContact(contact)"> |
| | | <view class="contact-info"> |
| | | <view class="contact-avatar"> |
| | | <text class="avatar-text">{{ contact.nickName.charAt(0) }}</text> |
| | | </view> |
| | | <view class="contact-details"> |
| | | <text class="contact-name">{{ contact.nickName }}</text> |
| | | <!-- <text class="contact-dept">{{ contact.department }}</text>--> |
| | | <!-- <text class="contact-dept">{{ contact.department }}</text>--> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { userListNoPageByTenantId } from "@/api/system/user" |
| | | import { ref, onMounted } from "vue"; |
| | | import { userListNoPageByTenantId, approveUserList } from "@/api/system/user"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | const loading = ref(false); |
| | | const finished = ref(false); |
| | | const selectedContact = ref(null); |
| | | const userList = ref([]); |
| | | |
| | | const loading = ref(false) |
| | | const finished = ref(false) |
| | | const selectedContact = ref(null) |
| | | const userList = ref([]) |
| | | // 接收传递的参数 |
| | | const stepIndex = ref(0); |
| | | |
| | | // 接收传递的参数 |
| | | const stepIndex = ref(0) |
| | | onMounted(() => { |
| | | // 从本地存储获取参数 |
| | | const storedStepIndex = uni.getStorageSync("stepIndex"); |
| | | if (storedStepIndex !== undefined && storedStepIndex !== null) { |
| | | stepIndex.value = parseInt(storedStepIndex); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // 从本地存储获取参数 |
| | | const storedStepIndex = uni.getStorageSync('stepIndex'); |
| | | if (storedStepIndex !== undefined && storedStepIndex !== null) { |
| | | stepIndex.value = parseInt(storedStepIndex) |
| | | } |
| | | |
| | | // 初始化联系人数据 |
| | | initContacts() |
| | | }) |
| | | // 初始化联系人数据 |
| | | initContacts(); |
| | | }); |
| | | |
| | | const initContacts = () => { |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data |
| | | }) |
| | | finished.value = true |
| | | } |
| | | const initContacts = () => { |
| | | if (approveType.value) { |
| | | approveUserList({ approveType: approveType.value }).then(res => { |
| | | userList.value = [...res.data]; |
| | | userList.value.forEach(item => { |
| | | item.nickName = item.nickName || item.userName; |
| | | }); |
| | | }); |
| | | } else { |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | } |
| | | finished.value = true; |
| | | }; |
| | | |
| | | const onLoad = () => { |
| | | // 模拟加载更多数据 |
| | | setTimeout(() => { |
| | | loading.value = false |
| | | finished.value = true |
| | | }, 1000) |
| | | } |
| | | const onLoading = () => { |
| | | // 模拟加载更多数据 |
| | | setTimeout(() => { |
| | | loading.value = false; |
| | | finished.value = true; |
| | | }, 1000); |
| | | }; |
| | | |
| | | const isSelected = (contact) => { |
| | | return selectedContact.value && selectedContact.value.userId === contact.userId |
| | | } |
| | | const isSelected = contact => { |
| | | return ( |
| | | selectedContact.value && selectedContact.value.userId === contact.userId |
| | | ); |
| | | }; |
| | | |
| | | const selectContact = (contact) => { |
| | | // 单选模式,直接替换选中的联系人 |
| | | selectedContact.value = contact |
| | | } |
| | | const selectContact = contact => { |
| | | // 单选模式,直接替换选中的联系人 |
| | | selectedContact.value = contact; |
| | | }; |
| | | |
| | | const clearSelected = () => { |
| | | selectedContact.value = null |
| | | } |
| | | const clearSelected = () => { |
| | | selectedContact.value = null; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.removeStorageSync('stepIndex'); |
| | | uni.navigateBack() |
| | | } |
| | | const goBack = () => { |
| | | uni.removeStorageSync("stepIndex"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const confirmSelect = () => { |
| | | if (!selectedContact.value) { |
| | | uni.showToast({ |
| | | title: '请选择一个联系人', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | // 使用 uni.$emit 发送数据 |
| | | uni.$emit('selectContact', { |
| | | stepIndex: stepIndex.value, |
| | | contact: selectedContact.value |
| | | }) |
| | | uni.navigateBack() |
| | | } |
| | | const confirmSelect = () => { |
| | | if (!selectedContact.value) { |
| | | uni.showToast({ |
| | | title: "请选择一个联系人", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | // 使用 uni.$emit 发送数据 |
| | | uni.$emit("selectContact", { |
| | | stepIndex: stepIndex.value, |
| | | contact: selectedContact.value, |
| | | }); |
| | | uni.navigateBack(); |
| | | }; |
| | | const approveType = ref(null); |
| | | onLoad(options => { |
| | | if (options.approveType) { |
| | | approveType.value = options.approveType; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .contact-select { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .header { |
| | | background: #ffffff; |
| | | padding: 16px 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | position: sticky; |
| | | /* 兼容 iOS 刘海/灵动岛安全区 */ |
| | | padding-top: calc(env(safe-area-inset-top)); |
| | | top: 0; |
| | | z-index: 100; |
| | | position: relative; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .confirm-btn { |
| | | color: #006cfb; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .search-section { |
| | | background: #fff; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .selected-section { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .selected-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .selected-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .clear-btn { |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .selected-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #f0f8ff; |
| | | border: 1px solid #006cfb; |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | gap: 12px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: -2px; |
| | | right: -2px; |
| | | width: 16px; |
| | | height: 16px; |
| | | background: #52c41a; |
| | | border-radius: 50%; |
| | | border: 2px solid #fff; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | .contact-select { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | &::after { |
| | | content: '✓'; |
| | | position: absolute; |
| | | top: -1px; |
| | | right: -1px; |
| | | width: 16px; |
| | | height: 16px; |
| | | |
| | | .header { |
| | | background: #ffffff; |
| | | padding: 16px 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | position: sticky; |
| | | /* 兼容 iOS 刘海/灵动岛安全区 */ |
| | | padding-top: calc(env(safe-area-inset-top)); |
| | | top: 0; |
| | | z-index: 100; |
| | | position: relative; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .confirm-btn { |
| | | color: #006cfb; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .search-section { |
| | | background: #fff; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .selected-section { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .selected-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .selected-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .clear-btn { |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .selected-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #f0f8ff; |
| | | border: 1px solid #006cfb; |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | gap: 12px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | top: -2px; |
| | | right: -2px; |
| | | width: 16px; |
| | | height: 16px; |
| | | background: #52c41a; |
| | | border-radius: 50%; |
| | | border: 2px solid #fff; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | &::after { |
| | | content: "✓"; |
| | | position: absolute; |
| | | top: -1px; |
| | | right: -1px; |
| | | width: 16px; |
| | | height: 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 10px; |
| | | color: #fff; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .contact-list { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .list-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .list-title { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .contact-item { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f8f9fa; |
| | | transition: all 0.2s; |
| | | position: relative; |
| | | |
| | | &.selected { |
| | | background-color: #f0f8ff; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 8px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 0 4px rgba(0, 108, 251, 0.2); |
| | | } |
| | | } |
| | | |
| | | &:active { |
| | | background-color: #f5f5f5; |
| | | } |
| | | } |
| | | |
| | | .contact-info { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | padding-left: 16px; |
| | | } |
| | | |
| | | .contact-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 10px; |
| | | color: #fff; |
| | | font-weight: bold; |
| | | margin-right: 12px; |
| | | position: relative; |
| | | } |
| | | } |
| | | |
| | | .contact-list { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | } |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .list-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | .contact-details { |
| | | flex: 1; |
| | | } |
| | | |
| | | .list-title { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | .contact-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | } |
| | | |
| | | .contact-item { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f8f9fa; |
| | | transition: all 0.2s; |
| | | position: relative; |
| | | |
| | | &.selected { |
| | | background-color: #f0f8ff; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 8px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 0 4px rgba(0, 108, 251, 0.2); |
| | | .contact-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | // 自定义单选按钮样式 |
| | | /* uview-plus的radio组件样式不需要额外的deep样式穿透,保持原有样式 */ |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: scale(1.2); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | &:active { |
| | | background-color: #f5f5f5; |
| | | } |
| | | } |
| | | |
| | | .contact-info { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | padding-left: 16px; |
| | | } |
| | | |
| | | .contact-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | position: relative; |
| | | } |
| | | |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .contact-details { |
| | | flex: 1; |
| | | } |
| | | |
| | | .contact-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | } |
| | | |
| | | .contact-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | // 自定义单选按钮样式 |
| | | /* uview-plus的radio组件样式不需要额外的deep样式穿透,保持原有样式 */ |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: scale(1.2); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | </style> |