ykt-wxapp/pages/work/department-select.vue

403 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="dept-page">
<view class="dept-container bg-white">
<view class="sidebar">
<view class="sidebar-search">
<input
v-model="keyword"
class="search-input"
type="text"
confirm-type="search"
placeholder="搜索一级科室"
placeholder-class="search-placeholder"
/>
<view v-if="keyword" class="clear-btn" @click="keyword = ''"
>清空</view
>
</view>
<scroll-view class="sidebar-list" scroll-y>
<view
v-for="item in filteredLevel1List"
:key="item.deptId"
:class="[
'sidebar-item',
item.deptId === selectedParentId ? 'active' : '',
]"
@click="selectParent(item)"
>
<text class="sidebar-text">{{
item.hlwDeptName || item.deptName
}}</text>
</view>
</scroll-view>
</view>
<scroll-view class="content" scroll-y>
<view v-if="contentList.length" class="content-list">
<view
v-for="child in contentList"
:key="child.deptId"
:class="['dept-item', isSelected(child) ? 'selected' : '']"
@click="selectDept(child)"
>
<view class="dept-name-row">
<view class="dept-name">{{ child.deptName }}</view>
<text v-if="isSelected(child)" class="check-tag">已选</text>
</view>
</view>
</view>
<view v-else class="empty-wrapper">
<empty-data text="暂无科室" />
</view>
</scroll-view>
</view>
<view class="footer-tip">
如没有符合的内容请联系客服
<text class="link" @click="contactService">点击添加客服</text>
</view>
</view>
</template>
<script setup>
import { computed, ref, watch } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { storeToRefs } from "pinia";
import useGuard from "@/hooks/useGuard.js";
import useAccountStore from "@/store/account.js";
import EmptyData from "@/components/empty-data.vue";
import api from "@/utils/api";
import { toast } from "@/utils/widget";
const { useLoad } = useGuard();
const deptList = ref([]);
const selectedParentId = ref("");
const selectedDeptId = ref("");
const keyword = ref("");
const sorter = (a, b) => {
const sortA = typeof a.sort === "number" ? a.sort : 0;
const sortB = typeof b.sort === "number" ? b.sort : 0;
if (sortA !== sortB) return sortA - sortB;
const timeA = a.createTime || 0;
const timeB = b.createTime || 0;
return timeA - timeB;
};
const level1List = computed(() =>
deptList.value.filter((i) => i.level === 1).sort(sorter)
);
const filteredLevel1List = computed(() => {
const key = keyword.value.trim().toLowerCase();
if (!key) return level1List.value;
return level1List.value.filter((i) =>
i.deptName.toString().toLowerCase().includes(key)
);
});
const contentList = computed(() => {
const children = deptList.value
.filter(
(i) =>
i.level === 2 && i.parentId && i.parentId === selectedParentId.value
)
.sort(sorter);
return children;
});
function isSelected(item) {
const targetId = selectedDeptId.value;
return !!targetId && (item.deptId === targetId || item.deptId === targetId);
}
async function fetchDeptList() {
uni.showLoading({ title: "加载中..." });
try {
const res = await api("getDeptList");
if (res && res.success && Array.isArray(res.data)) {
deptList.value = res.data;
hydrateSelectedParent();
} else {
toast(res?.message || "获取科室失败");
}
} catch (error) {
console.error("获取科室失败", error);
toast("获取科室失败");
} finally {
uni.hideLoading();
}
}
function hydrateSelectedParent() {
// 优先根据已选科室找到对应父级
if (selectedDeptId.value) {
const match =
deptList.value.find(
(i) =>
i.deptId === selectedDeptId.value || i.deptId === selectedDeptId.value
) || null;
if (match?.parentId) {
selectedParentId.value = match.parentId;
return;
}
}
// 再次兜底使用入参的 parentId
if (selectedParentId.value) return;
// 最后兜底选第一个一级科室
const first = level1List.value[0];
if (first) {
selectedParentId.value = first.deptId;
}
}
watch(
filteredLevel1List,
(list) => {
if (!list.length) return;
const exists = list.some((i) => i.deptId === selectedParentId.value);
if (!exists) {
selectedParentId.value = list[0].deptId;
}
},
{ immediate: false }
);
function selectParent(item) {
selectedParentId.value = item.deptId;
console.log("selectedParentId", selectedParentId.value);
}
function emitSelection(item) {
const eventChannel =
typeof getOpenerEventChannel === "function"
? getOpenerEventChannel()
: null;
eventChannel?.emit("deptSelected", {
id: item.deptId,
deptId: item.deptId || item.deptId,
name: item.deptName,
level: item.level,
parentId: item.parentId,
});
}
function selectDept(item) {
selectedDeptId.value = item.deptId || item.deptId;
emitSelection(item);
uni.navigateBack();
}
function contactService() {
uni.showToast({
title: "请联系客服添加科室",
icon: "none",
});
}
useLoad(() => {
fetchDeptList();
});
onLoad((opts) => {
// 支持从外部传入默认选中的一级科室
if (opts.parentId) {
selectedParentId.value = opts.parentId;
}
const passedDeptId = opts.deptId || opts.departmentId || opts.id;
if (passedDeptId) {
selectedDeptId.value = passedDeptId;
}
});
</script>
<style lang="scss" scoped>
.dept-page {
min-height: 100vh;
background: #f5f5f5;
display: flex;
flex-direction: column;
}
.dept-container {
display: flex;
border-radius: 12rpx;
overflow: hidden;
background: linear-gradient(180deg, #fdfdfd 0%, #f6f8fb 100%);
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.06);
height: 88vh;
}
.sidebar {
width: 230rpx;
background: #f8f9fb;
border-right: 1px solid #eef0f5;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-search {
padding: 16rpx 14rpx 12rpx;
display: flex;
align-items: center;
gap: 10rpx;
}
.search-input {
flex: 1;
height: 64rpx;
padding: 0 20rpx;
border-radius: 32rpx;
background: #fff;
border: 1px solid #e5e8ef;
font-size: 26rpx;
}
.search-placeholder {
color: #a0a8b8;
}
.clear-btn {
font-size: 24rpx;
color: #2a6ff2;
padding: 6rpx 10rpx;
}
.sidebar-list {
flex: 1;
}
.sidebar-item {
padding: 30rpx 18rpx;
font-size: 28rpx;
color: #333;
text-align: center;
border-bottom: 1px solid #eef0f5;
transition: all 0.2s ease;
&.active {
background: #fff;
color: #2a6ff2;
font-weight: 600;
box-shadow: inset 4rpx 0 0 #2a6ff2;
}
}
.sidebar-text {
display: inline-block;
line-height: 1.4;
}
.sidebar-empty {
padding: 40rpx 16rpx;
font-size: 24rpx;
color: #8c94a6;
text-align: center;
}
.content {
flex: 1;
background: #fff;
border-top-right-radius: 12rpx;
border-bottom-right-radius: 12rpx;
}
.content-list {
padding: 12rpx 20rpx 30rpx;
}
.dept-item {
padding: 26rpx 22rpx 14rpx;
border-bottom: 1px solid #f2f4f7;
border-radius: 12rpx;
margin-bottom: 14rpx;
background: #fff;
transition: box-shadow 0.2s ease, transform 0.15s ease;
&.selected {
box-shadow: 0 8rpx 24rpx rgba(42, 111, 242, 0.08);
border: 1px solid #e1e9ff;
transform: translateY(-2rpx);
}
}
.dept-name-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12rpx;
}
.dept-name {
font-size: 30rpx;
color: #333;
font-weight: 600;
}
.dept-sub-list {
margin-top: 12rpx;
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.dept-sub-item {
padding: 12rpx 16rpx;
background: #f6f8fb;
border-radius: 10rpx;
font-size: 26rpx;
color: #3a3a3a;
border: 1px solid #e5e9f2;
display: flex;
align-items: center;
gap: 12rpx;
transition: all 0.15s ease;
&.selected {
border-color: #2a6ff2;
background: #eaf1ff;
color: #1f5ed6;
}
}
.dept-sub-text {
flex: 1;
}
.check-tag {
padding: 6rpx 14rpx;
font-size: 22rpx;
color: #2a6ff2;
background: #eaf1ff;
border-radius: 20rpx;
}
.check-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #2a6ff2;
}
.empty-wrapper {
padding-top: 120rpx;
}
.footer-tip {
padding: 24rpx 20rpx 40rpx;
font-size: 26rpx;
color: #666;
text-align: center;
line-height: 1.6;
}
.link {
color: #2a6ff2;
}
</style>