- MyTree 新增受控 prop expandAll(默认 false):在 zTree 初始化之后套用并监听变化, 数据重载(切组/搜索过滤)后自动重新展开,规避调用方与 initTree 抢 nextTick 时序 - 组授权 / 角色授权的应用类型卡片头部加「全部展开 + small Switch」,默认关闭, 控制该类型下全部应用与模块的展开收起;每个卡片独立,状态按 category id 保存 - 开关外层 @click.stop:卡片头部本身绑定 toggleCollapse,不阻断冒泡会连带折叠卡片 - 角色授权「用户管理」表格:账号/姓名改定宽(110/90)、操作 80,部门 minWidth 吃剩余 宽度,把空间留给组路径列
This commit is contained in:
@@ -43,6 +43,12 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
/** 受控的「全部展开」:true 时展开全部节点。由本组件在树初始化之后套用,
|
||||||
|
* 避免调用方在数据重载(切组/搜索过滤)时与 initTree 抢时序而丢失展开状态 */
|
||||||
|
expandAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
isCheck: {
|
isCheck: {
|
||||||
typeof: Boolean,
|
typeof: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@@ -64,6 +70,12 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 受控「全部展开」:开关切换即时生效(树未初始化时忽略,initTree 会按当前值套用)
|
||||||
|
this.$watch("expandAll", val => {
|
||||||
|
if (this.treeObj) {
|
||||||
|
this.treeObj.expandAll(!!val);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.domId = this.uuid();
|
this.domId = this.uuid();
|
||||||
@@ -128,6 +140,10 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_this.treeObj = $.fn.zTree.init($("#" + _domId), _this.treeSetting, _data);
|
_this.treeObj = $.fn.zTree.init($("#" + _domId), _this.treeSetting, _data);
|
||||||
|
// 数据重载后重新套用「全部展开」(节点 open 会被新数据重置)
|
||||||
|
if (_this.expandAll) {
|
||||||
|
_this.treeObj.expandAll(true);
|
||||||
|
}
|
||||||
if (_this.autoClick) {
|
if (_this.autoClick) {
|
||||||
if (Array.isArray(this.treeData)) {
|
if (Array.isArray(this.treeData)) {
|
||||||
let id = this.treeData[0].id;
|
let id = this.treeData[0].id;
|
||||||
|
|||||||
+22
-1
@@ -40,10 +40,15 @@
|
|||||||
<span class="cat-name" :title="item.cat.name">{{ item.cat.name }}</span>
|
<span class="cat-name" :title="item.cat.name">{{ item.cat.name }}</span>
|
||||||
<span class="cat-count">{{ item.treeData.length }}个应用</span>
|
<span class="cat-count">{{ item.treeData.length }}个应用</span>
|
||||||
<span class="inherit-cap">子组继承</span>
|
<span class="inherit-cap">子组继承</span>
|
||||||
|
<!-- 开关外层 @click.stop:否则点击会冒泡到卡片头部触发折叠 -->
|
||||||
|
<span class="expand-all" @click.stop>
|
||||||
|
<span class="expand-all-label">全部展开</span>
|
||||||
|
<Switch size="small" v-model="expandAllMap[String(item.cat.id)]" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!collapsedMap[String(item.cat.id)]" class="cat-card-body">
|
<div v-show="!collapsedMap[String(item.cat.id)]" class="cat-card-body">
|
||||||
<MyTree :treeData="item.treeData" :setting="treeSetting" :autoClick="false" :isCheck="true"
|
<MyTree :treeData="item.treeData" :setting="treeSetting" :autoClick="false" :isCheck="true"
|
||||||
@on-tree-check="onTreeCheck" />
|
:expandAll="!!expandAllMap[String(item.cat.id)]" @on-tree-check="onTreeCheck" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,6 +139,9 @@ const searchKeyword = ref("");
|
|||||||
const appliedKeyword = ref("");
|
const appliedKeyword = ref("");
|
||||||
const collapsedMap = reactive<Record<string, boolean>>({});
|
const collapsedMap = reactive<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
/** 各应用类型卡片的「全部展开」开关(默认关闭):状态随卡片独立保存,数据重载后仍生效 */
|
||||||
|
const expandAllMap = reactive<Record<string, boolean>>({});
|
||||||
|
|
||||||
const fallbackModeItems: DictItem[] = [
|
const fallbackModeItems: DictItem[] = [
|
||||||
{ k: 0, v: "追加" },
|
{ k: 0, v: "追加" },
|
||||||
{ k: 1, v: "覆盖" },
|
{ k: 1, v: "覆盖" },
|
||||||
@@ -674,6 +682,19 @@ onMounted(async () => {
|
|||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.expand-all {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-all-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.cat-card-body {
|
.cat-card-body {
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
/* 子组继承复选框的绝对定位锚点:保证所有层级节点的复选框右对齐成一列 */
|
/* 子组继承复选框的绝对定位锚点:保证所有层级节点的复选框右对齐成一列 */
|
||||||
|
|||||||
@@ -26,10 +26,15 @@
|
|||||||
:type="collapsedMap[String(item.cat.id)] ? 'ios-arrow-forward' : 'ios-arrow-down'" />
|
:type="collapsedMap[String(item.cat.id)] ? 'ios-arrow-forward' : 'ios-arrow-down'" />
|
||||||
<span class="cat-name" :title="item.cat.name">{{ item.cat.name }}</span>
|
<span class="cat-name" :title="item.cat.name">{{ item.cat.name }}</span>
|
||||||
<span class="cat-count">{{ item.treeData.length }}个应用</span>
|
<span class="cat-count">{{ item.treeData.length }}个应用</span>
|
||||||
|
<!-- 开关外层 @click.stop:否则点击会冒泡到卡片头部触发折叠 -->
|
||||||
|
<span class="expand-all" @click.stop>
|
||||||
|
<span class="expand-all-label">全部展开</span>
|
||||||
|
<Switch size="small" v-model="expandAllMap[String(item.cat.id)]" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!collapsedMap[String(item.cat.id)]" class="cat-card-body">
|
<div v-show="!collapsedMap[String(item.cat.id)]" class="cat-card-body">
|
||||||
<MyTree :treeData="item.treeData" :setting="treeSetting" :autoClick="false" :isCheck="true"
|
<MyTree :treeData="item.treeData" :setting="treeSetting" :autoClick="false" :isCheck="true"
|
||||||
@on-tree-check="onTreeCheck" />
|
:expandAll="!!expandAllMap[String(item.cat.id)]" @on-tree-check="onTreeCheck" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,6 +87,9 @@ const searchKeyword = ref("");
|
|||||||
const appliedKeyword = ref("");
|
const appliedKeyword = ref("");
|
||||||
const collapsedMap = reactive<Record<string, boolean>>({});
|
const collapsedMap = reactive<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
/** 各应用类型卡片的「全部展开」开关(默认关闭):状态随卡片独立保存,数据重载后仍生效 */
|
||||||
|
const expandAllMap = reactive<Record<string, boolean>>({});
|
||||||
|
|
||||||
const treeSetting = {
|
const treeSetting = {
|
||||||
view: { dblClickExpand: false, showIcon: false },
|
view: { dblClickExpand: false, showIcon: false },
|
||||||
check: {
|
check: {
|
||||||
@@ -402,6 +410,19 @@ watch(
|
|||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.expand-all {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-all-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-tip {
|
.empty-tip {
|
||||||
color: #999;
|
color: #999;
|
||||||
padding: 48px 0;
|
padding: 48px 0;
|
||||||
|
|||||||
@@ -39,12 +39,13 @@ const existingBindings = computed(() =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 部门列为查询时按 groupId 拼接的组路径(非超管按数据范围裁剪,学校管理员只看到本校路径)
|
// 部门列为查询时按 groupId 拼接的组路径(非超管按数据范围裁剪,学校管理员只看到本校路径)
|
||||||
|
// 账号/姓名用定宽、部门用 minWidth 吃剩余宽度,把空间留给组路径
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: "账号", key: "account", minWidth: 140 },
|
{ title: "账号", key: "account", width: 110 },
|
||||||
{ title: "姓名", key: "realName", minWidth: 110 },
|
{ title: "姓名", key: "realName", width: 90 },
|
||||||
{ title: "部门", key: "groupPath", minWidth: 200 },
|
{ title: "部门", key: "groupPath", minWidth: 200 },
|
||||||
{
|
{
|
||||||
title: "操作", key: "action", width: 90, align: "center",
|
title: "操作", key: "action", width: 80, align: "center",
|
||||||
render: (h: any, { row }: any) => $utils.operatingTextHandle(h, "删除", "text-error", () => removeUser(row)),
|
render: (h: any, { row }: any) => $utils.operatingTextHandle(h, "删除", "text-error", () => removeUser(row)),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user