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