From a9d97b150701e634bdb751eab277696abd136cca Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 六月 2026 14:39:47 +0800
Subject: [PATCH] 君歌app 1.依照web端功能修改

---
 src/pages/oa/_components/OaUserSearchPicker.vue |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 261 insertions(+), 0 deletions(-)

diff --git a/src/pages/oa/_components/OaUserSearchPicker.vue b/src/pages/oa/_components/OaUserSearchPicker.vue
new file mode 100644
index 0000000..90a704b
--- /dev/null
+++ b/src/pages/oa/_components/OaUserSearchPicker.vue
@@ -0,0 +1,261 @@
+<!--
+  OA 閫氱敤锛氬彲鎼滅储鐨勭敤鎴峰崟閫夊脊灞傦紙鐐归�夊嵆纭锛�
+-->
+<template>
+  <up-popup :show="show"
+            mode="bottom"
+            round="16"
+            :safe-area-inset-bottom="true"
+            @close="emit('update:show', false)">
+    <view class="oa-user-sheet">
+      <view class="sheet-handle" />
+      <view class="sheet-head">
+        <text class="sheet-cancel"
+              @click="emit('update:show', false)">鍙栨秷</text>
+        <text class="sheet-title">{{ title }}</text>
+        <text class="sheet-spacer" />
+      </view>
+
+      <view class="sheet-search">
+        <up-search v-model="keyword"
+                   placeholder="鎼滅储濮撳悕鎴栧伐鍙�"
+                   :show-action="false"
+                   shape="round"
+                   bg-color="#f5f7fa" />
+      </view>
+
+      <view v-if="selfUser && showSelfQuick"
+            class="self-quick"
+            @click="pickUser(selfUser)">
+        <view class="user-avatar"
+              :style="{ backgroundColor: avatarColor(selfUser.nickName || selfUser.userName) }">
+          {{ (selfUser.nickName || selfUser.userName || "鎴�").charAt(0) }}
+        </view>
+        <view class="user-meta">
+          <text class="user-name">閫夋湰浜� 路 {{ userSelectLabel(selfUser) }}</text>
+          <text class="user-sub">{{ userSubLabel(selfUser) }}</text>
+        </view>
+        <up-icon name="arrow-right"
+                 size="14"
+                 color="#c0c4cc" />
+      </view>
+
+      <scroll-view scroll-y
+                   class="user-scroll"
+                   :show-scrollbar="false">
+        <view v-for="u in filteredList"
+              :key="String(u.userId ?? u.id)"
+              class="user-item"
+              :class="{ selected: isSelected(u) }"
+              @click="pickUser(u)">
+          <view class="user-avatar"
+                :style="{ backgroundColor: avatarColor(u.nickName || u.userName) }">
+            {{ (u.nickName || u.userName || "?").charAt(0) }}
+          </view>
+          <view class="user-meta">
+            <text class="user-name">{{ userSelectLabel(u) }}</text>
+            <text class="user-sub">{{ userSubLabel(u) }}</text>
+          </view>
+          <view class="user-check"
+                :class="{ checked: isSelected(u) }">
+            <up-icon v-if="isSelected(u)"
+                     name="checkmark"
+                     size="14"
+                     color="#fff" />
+          </view>
+        </view>
+        <view v-if="!filteredList.length"
+              class="user-empty">
+          <up-empty mode="search"
+                    text="鏆傛棤鍖归厤鐢ㄦ埛" />
+        </view>
+      </scroll-view>
+    </view>
+  </up-popup>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from "vue";
+  import useUserStore from "@/store/modules/user";
+  import {
+    filterActiveUsers,
+    userAvatarColor,
+    userSelectLabel,
+    userSubLabel,
+  } from "../_utils/userPickerUtils.js";
+
+  const props = defineProps({
+    show: { type: Boolean, default: false },
+    title: { type: String, default: "閫夋嫨鍛樺伐" },
+    users: { type: Array, default: () => [] },
+    modelValue: { type: [String, Number], default: "" },
+    showSelfQuick: { type: Boolean, default: true },
+  });
+
+  const emit = defineEmits(["update:show", "update:modelValue", "select"]);
+
+  const keyword = ref("");
+  const userStore = useUserStore();
+
+  const filteredList = computed(() =>
+    filterActiveUsers(props.users, keyword.value, 100)
+  );
+
+  const selfUser = computed(() => {
+    const id = userStore.id;
+    if (!id) return null;
+    const hit = props.users.find(u => String(u.userId ?? u.id) === String(id));
+    if (hit) return hit;
+    return {
+      userId: id,
+      nickName: userStore.nickName,
+      userName: userStore.name,
+    };
+  });
+
+  watch(
+    () => props.show,
+    v => {
+      if (v) keyword.value = "";
+    }
+  );
+
+  function avatarColor(name) {
+    return userAvatarColor(name);
+  }
+
+  function isSelected(u) {
+    const id = u.userId ?? u.id;
+    return id != null && String(id) === String(props.modelValue ?? "");
+  }
+
+  function pickUser(u) {
+    const id = u.userId ?? u.id;
+    emit("update:modelValue", id);
+    emit("select", u);
+    emit("update:show", false);
+  }
+</script>
+
+<style scoped lang="scss">
+  .oa-user-sheet {
+    background: #fff;
+    border-radius: 16px 16px 0 0;
+    max-height: 78vh;
+    display: flex;
+    flex-direction: column;
+  }
+  .sheet-handle {
+    width: 36px;
+    height: 4px;
+    background: #e4e7ed;
+    border-radius: 2px;
+    margin: 8px auto 4px;
+  }
+  .sheet-head {
+    display: flex;
+    align-items: center;
+    padding: 8px 16px 12px;
+  }
+  .sheet-cancel {
+    font-size: 15px;
+    color: #909399;
+    min-width: 48px;
+  }
+  .sheet-title {
+    flex: 1;
+    text-align: center;
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+  .sheet-spacer {
+    min-width: 48px;
+  }
+  .sheet-search {
+    padding: 0 16px 10px;
+  }
+  .self-quick {
+    display: flex;
+    align-items: center;
+    margin: 0 16px 8px;
+    padding: 12px;
+    background: linear-gradient(135deg, #ecf5ff 0%, #f0f9ff 100%);
+    border-radius: 12px;
+    border: 1px solid #d9ecff;
+  }
+  .user-scroll {
+    flex: 1;
+    max-height: 52vh;
+    padding: 0 8px 16px;
+    box-sizing: border-box;
+  }
+  .user-item,
+  .self-quick {
+    &:active {
+      opacity: 0.85;
+    }
+  }
+  .user-item {
+    display: flex;
+    align-items: center;
+    padding: 12px 10px;
+    border-radius: 10px;
+    margin-bottom: 4px;
+    &.selected {
+      background: #f0f7ff;
+    }
+  }
+  .user-avatar {
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    color: #fff;
+    font-size: 16px;
+    font-weight: 600;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-shrink: 0;
+  }
+  .user-meta {
+    flex: 1;
+    margin-left: 12px;
+    min-width: 0;
+  }
+  .user-name {
+    display: block;
+    font-size: 15px;
+    color: #303133;
+    font-weight: 500;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .user-sub {
+    display: block;
+    font-size: 12px;
+    color: #909399;
+    margin-top: 2px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .user-check {
+    width: 22px;
+    height: 22px;
+    border-radius: 50%;
+    border: 2px solid #dcdfe6;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-shrink: 0;
+    &.checked {
+      background: #2979ff;
+      border-color: #2979ff;
+    }
+  }
+  .user-empty {
+    padding: 24px 0;
+  }
+</style>

--
Gitblit v1.9.3