This commit is contained in:
Your Name
2026-04-30 14:04:12 +08:00
parent eb782305e6
commit d3003ce0ac
19 changed files with 1560 additions and 313 deletions
+95 -31
View File
@@ -4,7 +4,10 @@
<div class="group">
<div class="group__header">
<div class="group__accent" />
<div class="group__name">{{ node.dept_name }}</div>
<div class="group__name">
<el-icon class="group__name-icon"><FolderOpened /></el-icon>
{{ node.dept_name }}
</div>
<div class="group__amount-wrap">
<el-input-number
v-model="node.target_amount"
@@ -25,7 +28,8 @@
/>
</div>
<div class="group__badge-wrap">
<el-tag v-if="node.target_id" size="small" type="success" effect="plain">已保存</el-tag>
<el-tag v-if="node.target_id" size="small" type="success" effect="plain" round>已保存</el-tag>
<span v-else class="group__unsaved-dot" />
</div>
</div>
@@ -43,22 +47,19 @@
<!-- 子节点表格行 -->
<template v-else>
<div class="row" :data-depth="String(Math.min(depth, 4))">
<!-- 缩进空间 + 连接线 -->
<div class="row__indent" :style="{ width: `${(depth - 1) * 20 + 16}px` }">
<div class="row__connector" />
</div>
<!-- 部门名 -->
<div class="row__dept">
<div class="row__dot" />
<div class="row__dept-text">
<span class="row__name">{{ node.dept_name }}</span>
<span v-if="parentPath" class="row__path">{{ parentPath }}</span>
</div>
<el-tag v-if="node.target_id" size="small" type="success" effect="plain" class="row__saved">已保存</el-tag>
<el-tag v-if="node.target_id" size="small" type="success" effect="plain" round class="row__saved">已保存</el-tag>
</div>
<!-- 金额 -->
<div class="row__amount">
<el-input-number
v-model="node.target_amount"
@@ -71,7 +72,6 @@
/>
</div>
<!-- 备注 -->
<div class="row__remark">
<el-input
v-model="node.remark"
@@ -82,7 +82,6 @@
</div>
</div>
<!-- 递归子节点 -->
<MubiaoDeptNode
v-for="c in node.children"
:key="c.dept_id"
@@ -94,6 +93,7 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { FolderOpened } from '@element-plus/icons-vue'
defineOptions({ name: 'MubiaoDeptNode' })
@@ -124,7 +124,7 @@ const parentPath = computed(() =>
<style lang="scss" scoped>
/* ── 根节点:分组区块 ── */
.group {
border-bottom: 2px solid var(--el-border-color-lighter);
border-bottom: 1px solid var(--el-border-color-lighter);
&:last-child {
border-bottom: none;
@@ -135,14 +135,23 @@ const parentPath = computed(() =>
display: flex;
align-items: center;
gap: 0;
min-height: 52px;
background: linear-gradient(90deg, var(--el-color-primary-light-9) 0%, var(--el-fill-color-extra-light) 60%);
border-bottom: 1px solid var(--el-color-primary-light-7);
min-height: 54px;
background: linear-gradient(90deg,
color-mix(in srgb, var(--el-color-primary) 6%, var(--el-bg-color)) 0%,
color-mix(in srgb, var(--el-color-primary) 2%, var(--el-bg-color)) 50%,
var(--el-bg-color) 100%
);
border-bottom: 1px solid color-mix(in srgb, var(--el-color-primary) 12%, var(--el-border-color-lighter));
padding-right: 16px;
transition: background 0.15s;
cursor: default;
transition: background 0.18s ease;
&:hover {
background: linear-gradient(90deg, var(--el-color-primary-light-8) 0%, var(--el-fill-color-light) 60%);
background: linear-gradient(90deg,
color-mix(in srgb, var(--el-color-primary) 10%, var(--el-bg-color)) 0%,
color-mix(in srgb, var(--el-color-primary) 4%, var(--el-bg-color)) 50%,
var(--el-fill-color-extra-light) 100%
);
}
}
@@ -150,7 +159,7 @@ const parentPath = computed(() =>
flex-shrink: 0;
width: 4px;
align-self: stretch;
background: var(--el-color-primary);
background: linear-gradient(180deg, var(--el-color-primary), var(--el-color-primary-light-3));
border-radius: 0 2px 2px 0;
margin-right: 12px;
}
@@ -158,40 +167,70 @@ const parentPath = computed(() =>
.group__name {
flex: 0 0 auto;
width: 220px;
font-size: 14px;
font-size: 13.5px;
font-weight: 700;
color: var(--el-color-primary-dark-2);
padding-right: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
align-items: center;
gap: 6px;
}
.group__name-icon {
font-size: 15px;
opacity: 0.7;
flex-shrink: 0;
}
.group__amount-wrap {
flex: 0 0 194px;
border-left: 1px solid var(--el-color-primary-light-6);
padding: 8px 12px;
border-left: 1px solid color-mix(in srgb, var(--el-color-primary) 15%, var(--el-border-color-lighter));
padding: 9px 12px;
}
.group__amount-input {
width: 170px;
:deep(.el-input__wrapper) {
border-radius: 6px;
border-radius: 7px;
background: var(--el-bg-color);
box-shadow: 0 0 0 1px var(--el-color-primary-light-5) inset;
box-shadow: 0 0 0 1.5px var(--el-color-primary-light-5) inset,
0 1px 3px rgba(0, 0, 0, 0.06);
transition: box-shadow 0.15s;
&:hover {
box-shadow: 0 0 0 1.5px var(--el-color-primary-light-3) inset,
0 2px 6px rgba(0, 0, 0, 0.08);
}
&.is-focus {
box-shadow: 0 0 0 2px var(--el-color-primary-light-3) inset !important;
}
}
}
.group__remark-wrap {
flex: 1;
border-left: 1px solid var(--el-color-primary-light-6);
padding: 8px 12px;
border-left: 1px solid color-mix(in srgb, var(--el-color-primary) 15%, var(--el-border-color-lighter));
padding: 9px 12px;
}
.group__badge-wrap {
flex: 0 0 60px;
display: flex;
justify-content: center;
align-items: center;
}
.group__unsaved-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--el-border-color);
display: block;
}
.group__body {
@@ -202,10 +241,11 @@ const parentPath = computed(() =>
.row {
display: flex;
align-items: center;
min-height: 48px;
min-height: 46px;
border-bottom: 1px solid var(--el-border-color-lighter);
transition: background 0.12s;
transition: background 0.12s ease;
padding-right: 16px;
cursor: default;
&:last-child {
border-bottom: none;
@@ -213,13 +253,29 @@ const parentPath = computed(() =>
&:hover {
background: var(--el-fill-color-extra-light);
.row__dot {
transform: scale(1.3);
}
}
/* 深度色阶 */
&[data-depth='1'] { --dot: #67c23a; }
&[data-depth='2'] { --dot: #e6a23c; }
&[data-depth='3'] { --dot: #909399; }
&[data-depth='4'] { --dot: var(--el-border-color-darker); }
&[data-depth='1'] {
--dot: #67c23a;
--connector: color-mix(in srgb, #67c23a 30%, var(--el-border-color));
}
&[data-depth='2'] {
--dot: #e6a23c;
--connector: color-mix(in srgb, #e6a23c 30%, var(--el-border-color));
}
&[data-depth='3'] {
--dot: #909399;
--connector: var(--el-border-color);
}
&[data-depth='4'] {
--dot: var(--el-border-color-darker);
--connector: var(--el-border-color-light);
}
}
.row__indent {
@@ -235,8 +291,8 @@ const parentPath = computed(() =>
top: 0;
bottom: 50%;
width: 10px;
border-left: 1px dashed var(--el-border-color);
border-bottom: 1px dashed var(--el-border-color);
border-left: 1.5px dashed var(--connector, var(--el-border-color));
border-bottom: 1.5px dashed var(--connector, var(--el-border-color));
border-radius: 0 0 0 4px;
}
@@ -255,6 +311,8 @@ const parentPath = computed(() =>
height: 7px;
border-radius: 50%;
background: var(--dot, #909399);
transition: transform 0.15s ease;
box-shadow: 0 0 0 2px color-mix(in srgb, var(--dot, #909399) 20%, transparent);
}
.row__dept-text {
@@ -297,8 +355,14 @@ const parentPath = computed(() =>
.row__amount-input {
width: 170px;
:deep(.el-input__wrapper) {
border-radius: 6px;
border-radius: 7px;
transition: box-shadow 0.15s;
&:hover {
box-shadow: 0 0 0 1px var(--el-color-primary-light-5) inset;
}
}
}