zhangwencui
12 小时以前 ad3ecc9167a5f837e0a6292d8e697b799eb6bfc2
src/pages/cooperativeOffice/collaborativeApproval/contactSelect.vue
@@ -1,27 +1,29 @@
<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">
@@ -30,36 +32,33 @@
        <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>
@@ -69,277 +68,294 @@
</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>