- index.vue 来源切换「系统部门|自定义部门」,SYSTEM 原 DOM 原样 v-if 包装零逻辑改动,学校上下文共用 - CustomDeptPanel/CustomDeptCard:分类增删改排序(非空禁删)、部门增改移启停软删(风险二次确认) - CustomMemberModal:候选走 CandidateUsers(服务端同口径排除学生组),搜索+勾选+批量添加 - apiDefinitions/globals.d.ts 补 17 个 /api 契约(key 与后端 operationId 严格一致) - 遗留:npm run api:gen 待后端可起环境补跑校验
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<article class="cdept-card" :class="{ 'is-off': !dept.enabled }">
|
||||
<div class="row-1">
|
||||
<span class="status-dot" :class="{ 'is-off': !dept.enabled }"></span>
|
||||
<h3 class="card-name" :title="dept.name">{{ dept.name }}</h3>
|
||||
<span class="sw" :class="{ 'is-off': !dept.enabled }" :title="dept.enabled ? '点击停用' : '点击启用'"
|
||||
@click="emit('toggle', dept)"></span>
|
||||
</div>
|
||||
<div class="row-2">
|
||||
<span class="sub">{{ memberText }}</span>
|
||||
<span class="ops">
|
||||
<i class="op" :class="{ 'is-disabled': !canMoveUp }" title="上移" @click="emit('moveUp')">↑</i>
|
||||
<i class="op" :class="{ 'is-disabled': !canMoveDown }" title="下移" @click="emit('moveDown')">↓</i>
|
||||
<a class="link" @click="emit('members', dept)">成员</a>
|
||||
<a class="link" @click="emit('edit', dept)">编辑</a>
|
||||
<a class="link danger" @click="emit('remove', dept)">删除</a>
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import type { CustomDepartmentVo } from "@/api/globals";
|
||||
|
||||
/**
|
||||
* CUSTOM 部门卡片:与 SYSTEM 的 DeptCard 同款外观,但字段来自 ub_custom_department。
|
||||
* 停用在管理端照常可见(灰态),软删的部门由后端列表过滤、不会走到这里。
|
||||
*/
|
||||
const props = defineProps<{
|
||||
dept: CustomDepartmentVo;
|
||||
canMoveUp?: boolean;
|
||||
canMoveDown?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "toggle", dept: CustomDepartmentVo): void;
|
||||
(e: "edit", dept: CustomDepartmentVo): void;
|
||||
(e: "remove", dept: CustomDepartmentVo): void;
|
||||
(e: "members", dept: CustomDepartmentVo): void;
|
||||
(e: "moveUp"): void;
|
||||
(e: "moveDown"): void;
|
||||
}>();
|
||||
|
||||
const memberText = computed(() => {
|
||||
const count = Number(props.dept?.memberCount ?? 0);
|
||||
if (!props.dept?.enabled) return count ? `已停用 · ${count} 人` : "已停用 · 无成员";
|
||||
return count ? `${count} 人` : "无成员(可先建部门再配人)";
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cdept-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 9px;
|
||||
padding: 13px 14px;
|
||||
background: var(--panel, #fff);
|
||||
border: 1px solid var(--line, #e9edf5);
|
||||
border-radius: var(--r, 10px);
|
||||
box-shadow: 0 1px 2px rgba(30, 38, 53, .03);
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
}
|
||||
|
||||
.cdept-card:hover {
|
||||
border-color: #dbe2fb;
|
||||
box-shadow: 0 6px 18px rgba(30, 38, 53, .07);
|
||||
}
|
||||
|
||||
.cdept-card.is-off {
|
||||
background: #fbfcfd;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.row-1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
flex: 0 0 6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--green, #20a162);
|
||||
}
|
||||
|
||||
.status-dot.is-off {
|
||||
background: #cbd2df;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: var(--text, #1e2635);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cdept-card.is-off .card-name {
|
||||
color: var(--text-3, #98a1b2);
|
||||
}
|
||||
|
||||
.row-2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 26px;
|
||||
margin-top: auto;
|
||||
padding-top: 9px;
|
||||
border-top: 1px dashed var(--line-2, #f0f3f9);
|
||||
font-size: 12px;
|
||||
color: var(--text-3, #98a1b2);
|
||||
}
|
||||
|
||||
.row-2 .sub {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ops {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--brand, #577afb);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.link.danger {
|
||||
color: #e54545;
|
||||
}
|
||||
|
||||
.op {
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
color: var(--text-2, #5a6478);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.op:hover {
|
||||
background: #e8ecfa;
|
||||
color: var(--brand, #577afb);
|
||||
}
|
||||
|
||||
.op.is-disabled {
|
||||
color: #d3d9e6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 启停开关(与 SYSTEM 卡片同一套视觉) */
|
||||
.sw {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: var(--brand, #577afb);
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
|
||||
.sw::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 18px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(30, 38, 53, .22);
|
||||
transition: left .15s;
|
||||
}
|
||||
|
||||
.sw.is-off {
|
||||
background: #d3d9e6;
|
||||
}
|
||||
|
||||
.sw.is-off::after {
|
||||
left: 2px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user