fix(产品需求): 修复产品需求使用状态和终止态字典的问题。
fix(组织): 修复组织编码下拉框的数据显示问题、修复组织编码负责人无法新增的问题。 fix(管理链路): 修复管理链路高度没固定,节点全部收缩等问题。
This commit is contained in:
@@ -36,14 +36,6 @@ export const RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE = 'rdms_product_direction';
|
||||
*/
|
||||
export const SYSTEM_USER_COMPANY_DICT_CODE = 'system_user_company';
|
||||
|
||||
/**
|
||||
* 需求终态状态字典编码
|
||||
*
|
||||
* 对应业务字段:需求相关接口和页面中的 terminal status
|
||||
* 来源口径:产品需求权限文档中定义,标签包括已关闭、已取消、已拒绝
|
||||
*/
|
||||
export const RDMS_REQ_TERMINAL_STATUS_DICT_CODE = 'rdms_req_terminal_status';
|
||||
|
||||
/**
|
||||
* 需求来源类型字典编码
|
||||
*
|
||||
@@ -67,11 +59,3 @@ export const RDMS_REQ_PRIORITY_DICT_CODE = 'rdms_req_priority';
|
||||
* 来源口径:产品需求文档中定义,标签包括工程需求、用户需求、安全需求、体验优化、功能需求
|
||||
*/
|
||||
export const RDMS_REQ_CATEGORY_DICT_CODE = 'rdms_req_category';
|
||||
|
||||
/**
|
||||
* 需求状态字典编码
|
||||
*
|
||||
* 对应业务字段:需求相关接口和页面中的 statusCode
|
||||
* 来源口径:产品需求文档中定义
|
||||
*/
|
||||
export const RDMS_REQ_STATUS_DICT_CODE = 'rdms_req_status';
|
||||
|
||||
@@ -320,6 +320,28 @@ export async function fetchGetRequirementLifecycle(requirementId: string, produc
|
||||
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementLifecycleInfo>, data => data);
|
||||
}
|
||||
|
||||
/** 获取需求所有状态字典 */
|
||||
export async function fetchGetRequirementStatusDict() {
|
||||
const result = await request<Api.Product.RequirementStatusDict[]>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${REQUIREMENT_PREFIX}/status/dict`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementStatusDict[]>, data => data);
|
||||
}
|
||||
|
||||
/** 获取需求终止态状态字典 */
|
||||
export async function fetchGetRequirementTerminalStatusDict() {
|
||||
const result = await request<Api.Product.RequirementStatusDict[]>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${REQUIREMENT_PREFIX}/status/dict/terminal`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementStatusDict[]>, data => data);
|
||||
}
|
||||
|
||||
// ========== 模块管理 API ==========
|
||||
type RequirementModuleResponse = Omit<Api.Product.RequirementModule, 'id' | 'parentId' | 'productId'> & {
|
||||
id: string | number;
|
||||
|
||||
15
src/typings/api/product.d.ts
vendored
15
src/typings/api/product.d.ts
vendored
@@ -319,6 +319,21 @@ declare namespace Api {
|
||||
children?: RequirementModule[];
|
||||
}
|
||||
|
||||
// ========== 需求状态字典 ==========
|
||||
|
||||
interface RequirementStatusDict {
|
||||
/** 状态编码 */
|
||||
statusCode: string;
|
||||
/** 状态名称 */
|
||||
statusName: string;
|
||||
/** 排序值 */
|
||||
sort: number;
|
||||
/** 是否初始状态 */
|
||||
initialFlag: boolean;
|
||||
/** 是否终态 */
|
||||
terminalFlag: boolean;
|
||||
}
|
||||
|
||||
// ========== 需求生命周期 ==========
|
||||
|
||||
interface RequirementLifecycleAction {
|
||||
|
||||
6
src/typings/api/system-manage.d.ts
vendored
6
src/typings/api/system-manage.d.ts
vendored
@@ -103,7 +103,7 @@ declare namespace Api {
|
||||
interface OrgLeaderRelation {
|
||||
id: number;
|
||||
deptId: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
userNickname: string;
|
||||
effectiveFrom?: number | null;
|
||||
effectiveUntil?: number | null;
|
||||
@@ -115,7 +115,7 @@ declare namespace Api {
|
||||
type OrgLeaderRelationList = OrgLeaderRelation[];
|
||||
|
||||
interface OrgLeaderCandidateUser {
|
||||
id: number;
|
||||
id: string;
|
||||
nickname: string;
|
||||
deptId: number;
|
||||
deptName?: string | null;
|
||||
@@ -125,7 +125,7 @@ declare namespace Api {
|
||||
|
||||
type SaveOrgLeaderRelationParams = {
|
||||
deptId: number;
|
||||
userId: number;
|
||||
userId: string | null;
|
||||
effectiveFrom?: number | null;
|
||||
effectiveUntil?: number | null;
|
||||
remark?: string | null;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<script setup lang="tsx">
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import type { TableInstance } from 'element-plus';
|
||||
import { ElButton, ElTag } from 'element-plus';
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
RDMS_REQ_CATEGORY_DICT_CODE,
|
||||
RDMS_REQ_PRIORITY_DICT_CODE,
|
||||
RDMS_REQ_STATUS_DICT_CODE,
|
||||
RDMS_REQ_TERMINAL_STATUS_DICT_CODE
|
||||
RDMS_REQ_PRIORITY_DICT_CODE
|
||||
} from '@/constants/dict';
|
||||
import {
|
||||
fetchChangeRequirementStatus,
|
||||
fetchDeleteRequirement,
|
||||
fetchGetProductMembers,
|
||||
fetchGetRequirementAllowedTransitions,
|
||||
fetchGetRequirementStatusDict,
|
||||
fetchGetRequirementTerminalStatusDict,
|
||||
fetchGetRequirementTree
|
||||
} from '@/service/api';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
import { useDict } from '@/hooks/business/dict';
|
||||
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
||||
import DictTag from '@/components/custom/dict-tag.vue';
|
||||
import { useCurrentProduct } from '../shared/use-current-product';
|
||||
@@ -42,7 +41,38 @@ defineOptions({ name: 'ProductRequirement' });
|
||||
const { currentObjectId } = useCurrentProduct();
|
||||
const { hasObjectAuth } = useAuth();
|
||||
|
||||
const { dictOptions: terminalStatusOptions } = useDict(RDMS_REQ_TERMINAL_STATUS_DICT_CODE);
|
||||
const statusOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||
const terminalStatusOptions = ref<string[]>([]);
|
||||
|
||||
async function loadStatusOptions() {
|
||||
const { error, data } = await fetchGetRequirementStatusDict();
|
||||
|
||||
if (error || !data) {
|
||||
statusOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
statusOptions.value = data.map(item => ({
|
||||
label: item.statusName,
|
||||
value: item.statusCode
|
||||
}));
|
||||
}
|
||||
|
||||
async function loadTerminalStatusOptions() {
|
||||
const { error, data } = await fetchGetRequirementTerminalStatusDict();
|
||||
|
||||
if (error || !data) {
|
||||
terminalStatusOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
terminalStatusOptions.value = data.map(item => item.statusCode);
|
||||
}
|
||||
|
||||
function getStatusLabel(statusCode: string) {
|
||||
const item = statusOptions.value.find(opt => opt.value === statusCode);
|
||||
return item ? item.label : statusCode;
|
||||
}
|
||||
|
||||
const priorityTagTypeMap: Record<number, UI.ThemeColor> = {
|
||||
0: 'info',
|
||||
@@ -60,7 +90,7 @@ function formatDateTime(value?: string | null) {
|
||||
}
|
||||
|
||||
function isTerminalStatus(statusCode: string) {
|
||||
return terminalStatusOptions.value.some(option => option.value === statusCode);
|
||||
return terminalStatusOptions.value.some(option => option === statusCode);
|
||||
}
|
||||
|
||||
function canSplitRequirement(row: Api.Product.Requirement) {
|
||||
@@ -233,15 +263,15 @@ const columns = computed(() => [
|
||||
minWidth: 120,
|
||||
formatter: (row: Api.Product.Requirement) => row.category
|
||||
},
|
||||
{
|
||||
prop: 'description',
|
||||
label: '描述',
|
||||
minWidth: 200,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Api.Product.Requirement) => {
|
||||
return row.description?.replace(/<[^>]+>/g, '').trim() || '--';
|
||||
}
|
||||
},
|
||||
// {
|
||||
// prop: 'description',
|
||||
// label: '描述',
|
||||
// minWidth: 200,
|
||||
// showOverflowTooltip: true,
|
||||
// formatter: (row: Api.Product.Requirement) => {
|
||||
// return row.description?.replace(/<[^>]+>/g, '').trim() || '--';
|
||||
// }
|
||||
// },
|
||||
{
|
||||
prop: 'priority',
|
||||
label: '优先级',
|
||||
@@ -257,11 +287,9 @@ const columns = computed(() => [
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: (row: Api.Product.Requirement) => (
|
||||
<DictTag
|
||||
dictCode={RDMS_REQ_STATUS_DICT_CODE}
|
||||
value={row.statusCode}
|
||||
type={getRequirementStatusTagType(row.statusCode)}
|
||||
/>
|
||||
<ElTag type={getRequirementStatusTagType(row.statusCode)}>
|
||||
{getStatusLabel(row.statusCode)}
|
||||
</ElTag>
|
||||
)
|
||||
},
|
||||
{
|
||||
@@ -601,6 +629,10 @@ watch(
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadStatusOptions(), loadTerminalStatusOptions()]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { RDMS_REQ_SOURCE_TYPE_DICT_CODE } from '@/constants/dict';
|
||||
import { fetchGetRequirementStatusDict } from '@/service/api';
|
||||
import DictSelect from '@/components/custom/dict-select.vue';
|
||||
import TableSearchPanel from '@/components/custom/table-search-panel.vue';
|
||||
import MemberSelectOption from './member-select-option.vue';
|
||||
@@ -29,16 +31,21 @@ const emit = defineEmits<Emits>();
|
||||
|
||||
const model = defineModel<Api.Product.RequirementSearchParams>('model', { required: true });
|
||||
|
||||
const requirementStatusOptions = [
|
||||
{ label: '待确认', value: 'pending_confirm' },
|
||||
{ label: '待评审', value: 'pending_review' },
|
||||
{ label: '待分流', value: 'pending_dispatch' },
|
||||
{ label: '实施中', value: 'implementing' },
|
||||
{ label: '已验收', value: 'accepted' },
|
||||
{ label: '已关闭', value: 'closed' },
|
||||
{ label: '已拒绝', value: 'rejected' },
|
||||
{ label: '已取消', value: 'cancelled' }
|
||||
];
|
||||
const requirementStatusOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||
|
||||
async function loadStatusOptions() {
|
||||
const { error, data } = await fetchGetRequirementStatusDict();
|
||||
|
||||
if (error || !data) {
|
||||
requirementStatusOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
requirementStatusOptions.value = data.map(item => ({
|
||||
label: item.statusName,
|
||||
value: item.statusCode
|
||||
}));
|
||||
}
|
||||
|
||||
function reset() {
|
||||
emit('reset');
|
||||
@@ -47,6 +54,10 @@ function reset() {
|
||||
function search() {
|
||||
emit('search');
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadStatusOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* - 支持节点的展开/折叠
|
||||
* - 支持单选/多选节点
|
||||
* - 提供新增、编辑、删除(单个/批量)功能
|
||||
* - 支持按管理者用户 ID 和被管理用户 ID 搜索
|
||||
* - 支持按上级用户 ID 和下级用户 ID 搜索
|
||||
*
|
||||
* 树形结构特点:
|
||||
* - 根节点:最高领导,没有上级
|
||||
@@ -134,6 +134,10 @@ async function loadTreeData() {
|
||||
|
||||
if (!error) {
|
||||
treeData.value = data || [];
|
||||
|
||||
// 数据加载完成后,展开前两层节点
|
||||
await nextTick();
|
||||
expandFirstTwoLevels();
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@@ -167,6 +171,9 @@ async function loadTreeDataByQuery(query: Api.SystemManage.UserManagementRelatio
|
||||
* 清空选中状态并重新加载数据
|
||||
*/
|
||||
async function reloadTreeData() {
|
||||
// 保存当前展开状态
|
||||
saveExpandedState();
|
||||
|
||||
checkedNodeKeys.value = [];
|
||||
await loadTreeData();
|
||||
await nextTick();
|
||||
@@ -216,15 +223,25 @@ const { bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateM
|
||||
const operateType = ref<UI.TableOperateType>('add');
|
||||
const editingData = ref<Api.SystemManage.UserManagementRelation | null>(null);
|
||||
|
||||
/**
|
||||
* 是否在管理链路树中选中节点后点击新增
|
||||
* 用于控制新增对话框中上级用户下拉框是否禁用
|
||||
*/
|
||||
const isAddFromTreeNode = ref(false);
|
||||
|
||||
/**
|
||||
* 打开新增对话框
|
||||
*
|
||||
* @param item 当前节点数据,用于设置默认管理者为此节点用户
|
||||
* @param item 当前节点数据,用于设置默认上级为此节点用户
|
||||
*/
|
||||
function openAdd(item?: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
||||
operateType.value = 'add';
|
||||
// 如果是从某一行的新增按钮触发,则默认管理者为当前节点用户
|
||||
// 否则默认管理者为当前登录用户(在对话框组件中处理)
|
||||
|
||||
// 如果是从树节点点击的新增按钮,标记为来自树节点
|
||||
isAddFromTreeNode.value = Boolean(item);
|
||||
|
||||
// 如果是从某一行的新增按钮触发,则默认上级为当前节点用户
|
||||
// 否则默认上级为当前登录用户(在对话框组件中处理)
|
||||
editingData.value = item
|
||||
? {
|
||||
id: null,
|
||||
@@ -309,14 +326,127 @@ function handleNodeCheck(checkedData: any, checkedInfo: any) {
|
||||
.filter((id: string | null): id is string => Boolean(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前展开的节点 ID 列表
|
||||
* 用于在刷新数据后恢复展开状态
|
||||
*/
|
||||
const expandedNodeKeys = ref<string[]>([]);
|
||||
|
||||
/**
|
||||
* 保存当前展开的节点状态
|
||||
*/
|
||||
function saveExpandedState() {
|
||||
if (!relationTreeRef.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const store = (relationTreeRef.value as any).store;
|
||||
if (!store) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allNodes = store.nodesMap || {};
|
||||
expandedNodeKeys.value = [];
|
||||
|
||||
Object.keys(allNodes).forEach(key => {
|
||||
const node = allNodes[key];
|
||||
if (node && node.expanded && node.data && node.data.userId) {
|
||||
expandedNodeKeys.value.push(node.data.userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理对话框提交事件
|
||||
*
|
||||
* @param relationId 提交后的关系 ID
|
||||
*/
|
||||
function handleSubmitted(_relationId: string) {
|
||||
async function handleSubmitted(_relationId: string) {
|
||||
closeOperateModal();
|
||||
reloadTreeData();
|
||||
await reloadTreeData();
|
||||
|
||||
// 操作完成后恢复树节点的展开状态
|
||||
await restoreExpandedState();
|
||||
|
||||
// 重置标记
|
||||
isAddFromTreeNode.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开所有子节点(递归)
|
||||
*
|
||||
* @param tree 树形组件实例
|
||||
* @param nodes 节点数据数组
|
||||
*/
|
||||
function expandNodes(tree: InstanceType<typeof ElTree>, nodes: Api.SystemManage.UserManagementRelationTreeRespVO[]) {
|
||||
if (!tree || !nodes || !nodes.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
// 展开当前节点
|
||||
const treeNode = tree.getNode(node.userId);
|
||||
if (treeNode) {
|
||||
treeNode.expand();
|
||||
}
|
||||
|
||||
// 递归展开子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
expandNodes(tree, node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开树的前两层节点
|
||||
*
|
||||
* 只展开根节点和它们的直接子节点,第三层及更深层保持折叠
|
||||
*/
|
||||
function expandFirstTwoLevels() {
|
||||
const tree = relationTreeRef.value;
|
||||
if (!tree || !treeData.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 展开第一层(根节点)
|
||||
for (const rootNode of treeData.value) {
|
||||
const treeNode = tree.getNode(rootNode.userId);
|
||||
if (treeNode) {
|
||||
treeNode.expand();
|
||||
}
|
||||
|
||||
// 展开第二层(根节点的直接子节点)
|
||||
if (rootNode.children && rootNode.children.length > 0) {
|
||||
for (const childNode of rootNode.children) {
|
||||
const childTreeNode = tree.getNode(childNode.userId);
|
||||
if (childTreeNode) {
|
||||
childTreeNode.expand();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复树节点的展开状态
|
||||
*
|
||||
* 根据之前保存的展开状态,恢复对应的节点展开
|
||||
*/
|
||||
async function restoreExpandedState() {
|
||||
await nextTick();
|
||||
|
||||
const tree = relationTreeRef.value;
|
||||
if (!tree || !expandedNodeKeys.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 恢复之前展开的节点
|
||||
for (const key of expandedNodeKeys.value) {
|
||||
const node = tree.getNode(key);
|
||||
if (node) {
|
||||
node.expand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +486,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-col-stretch gap-16px overflow-hidden">
|
||||
<div class="flex-col-stretch gap-16px overflow-hidden" style="height: calc(70vh - 120px)">
|
||||
<!-- 搜索区域 -->
|
||||
<RelationSearch
|
||||
v-model:model="searchParams"
|
||||
@@ -366,7 +496,7 @@ onMounted(async () => {
|
||||
/>
|
||||
|
||||
<!-- 树形卡片区域 -->
|
||||
<ElCard class="flex-1-hidden card-wrapper">
|
||||
<ElCard class="flex-1-hidden card-wrapper min-h-0">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-12px">
|
||||
<div class="flex items-center gap-10px">
|
||||
@@ -463,6 +593,7 @@ onMounted(async () => {
|
||||
:operate-type="operateType"
|
||||
:row-data="editingData"
|
||||
:user-list="userList"
|
||||
:is-add-from-tree-node="isAddFromTreeNode"
|
||||
@submitted="handleSubmitted"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* - 表单验证和提交
|
||||
*
|
||||
* 表单字段:
|
||||
* - 管理者用户:必填,下拉选择,默认当前登录用户
|
||||
* - 被管理用户:必填,下拉选择,默认空
|
||||
* - 上级用户:必填,下拉选择,默认当前登录用户
|
||||
* - 下级用户用户:必填,下拉选择,默认空
|
||||
* - 生效开始时间:可选
|
||||
* - 生效结束时间:可选
|
||||
* - 备注:可选
|
||||
@@ -39,6 +39,8 @@ interface Props {
|
||||
rowData?: Api.SystemManage.UserManagementRelation | null;
|
||||
/** 用户列表,由父组件统一提供 */
|
||||
userList: Api.SystemManage.UserSimple[];
|
||||
/** 是否从树节点点击新增(用于控制上级用户下拉框禁用) */
|
||||
isAddFromTreeNode?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -115,8 +117,8 @@ function createDefaultModel(): Model {
|
||||
* 表单验证规则
|
||||
*/
|
||||
const rules = {
|
||||
managerUserId: createRequiredRule('请选择管理者用户'),
|
||||
subordinateUserId: createRequiredRule('请选择被管理用户')
|
||||
managerUserId: createRequiredRule('请选择上级用户'),
|
||||
subordinateUserId: createRequiredRule('请选择下级用户')
|
||||
} satisfies Record<string, App.Global.FormRule>;
|
||||
|
||||
/**
|
||||
@@ -162,16 +164,16 @@ async function initModel() {
|
||||
model.value = createDefaultModel();
|
||||
|
||||
if (!isEdit.value) {
|
||||
// 新增模式:设置管理者用户
|
||||
// 优先使用 rowData 中传入的管理者用户 ID(如从树形节点新增)
|
||||
// 新增模式:设置上级用户
|
||||
// 优先使用 rowData 中传入的上级用户 ID(如从树形节点新增)
|
||||
// 否则使用当前登录用户
|
||||
let managerUserIdToSet = resolveDefaultManagerUserId();
|
||||
|
||||
if (props.rowData && props.rowData.managerUserId) {
|
||||
// 从树形节点点击新增,管理者为当前节点用户
|
||||
// 从树形节点点击新增,上级为当前节点用户
|
||||
managerUserIdToSet = props.rowData.managerUserId;
|
||||
} else if (authStore.userInfo.userId) {
|
||||
// 头部新增,管理者为当前登录用户
|
||||
// 头部新增,上级为当前登录用户
|
||||
const currentUserId = authStore.userInfo.userId;
|
||||
const currentUserName = authStore.userInfo.userName;
|
||||
|
||||
@@ -301,18 +303,24 @@ watch(visible, value => {
|
||||
<ElForm ref="formRef" :model="model" :rules="rules" label-position="top">
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="管理者用户" prop="managerUserId">
|
||||
<ElSelect v-model="model.managerUserId" class="w-full" placeholder="请选择管理者用户" filterable>
|
||||
<ElFormItem label="上级用户" prop="managerUserId">
|
||||
<ElSelect
|
||||
v-model="model.managerUserId"
|
||||
class="w-full"
|
||||
placeholder="请选择上级用户"
|
||||
filterable
|
||||
:disabled="props.operateType === 'add' && props.isAddFromTreeNode"
|
||||
>
|
||||
<ElOption v-for="user in props.userList" :key="user.id" :label="user.nickname" :value="user.id" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="被管理用户" prop="subordinateUserId">
|
||||
<ElFormItem label="下级用户" prop="subordinateUserId">
|
||||
<ElSelect
|
||||
v-model="model.subordinateUserId"
|
||||
class="w-full"
|
||||
placeholder="请选择被管理用户"
|
||||
placeholder="请选择下级用户"
|
||||
filterable
|
||||
:disabled="isEdit"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 用户管理链路搜索组件
|
||||
*
|
||||
* 功能说明:
|
||||
* - 提供管理者和被管理者用户下拉选择
|
||||
* - 提供上级和下级用户下拉选择
|
||||
* - 支持搜索和重置操作
|
||||
* - 与树形结构数据联动
|
||||
*
|
||||
@@ -63,11 +63,11 @@ function search() {
|
||||
<template>
|
||||
<TableSearchPanel :model="model" :action-col-lg="8" @reset="reset" @search="search">
|
||||
<!-- <ElCol :lg="8" :md="12" :sm="12">-->
|
||||
<!-- <ElFormItem label="管理者用户" prop="managerUserId">-->
|
||||
<!-- <ElFormItem label="上级用户" prop="managerUserId">-->
|
||||
<!-- <ElSelect-->
|
||||
<!-- v-model="model.managerUserId"-->
|
||||
<!-- class="w-full"-->
|
||||
<!-- placeholder="请选择管理者用户"-->
|
||||
<!-- placeholder="请选择上级用户"-->
|
||||
<!-- clearable-->
|
||||
<!-- filterable-->
|
||||
<!-- >-->
|
||||
|
||||
@@ -66,7 +66,7 @@ function mapUsersToCandidateUsers(users: Api.SystemManage.User[]): Api.SystemMan
|
||||
return users
|
||||
.filter(item => !item.resignedAt || item.resignedAt > now)
|
||||
.map(item => ({
|
||||
id: item.id,
|
||||
id: String(item.id),
|
||||
nickname: item.nickname?.trim() || item.username,
|
||||
deptId: item.deptId,
|
||||
deptName: item.deptName ?? null
|
||||
|
||||
@@ -44,7 +44,7 @@ const title = computed(() => {
|
||||
});
|
||||
|
||||
type Model = {
|
||||
userId: number | null;
|
||||
userId: string | null;
|
||||
effectiveFrom: Date | null;
|
||||
effectiveUntil: Date | null;
|
||||
remark: string;
|
||||
@@ -119,7 +119,7 @@ async function handleSubmit() {
|
||||
|
||||
const payload: Api.SystemManage.SaveOrgLeaderRelationParams = {
|
||||
deptId: props.dept.id,
|
||||
userId: Number(model.value.userId),
|
||||
userId: model.value.userId,
|
||||
effectiveFrom,
|
||||
effectiveUntil,
|
||||
remark: model.value.remark.trim() || null
|
||||
@@ -129,10 +129,10 @@ async function handleSubmit() {
|
||||
|
||||
const request =
|
||||
isEdit.value && props.rowData
|
||||
? fetchUpdateOrgLeaderRelation({ id: props.rowData.id, ...payload })
|
||||
: fetchCreateOrgLeaderRelation(payload);
|
||||
? await fetchUpdateOrgLeaderRelation({ id: props.rowData.id, ...payload })
|
||||
: await fetchCreateOrgLeaderRelation(payload);
|
||||
|
||||
const { error } = await request;
|
||||
const { error } = request;
|
||||
|
||||
submitting.value = false;
|
||||
|
||||
@@ -186,10 +186,11 @@ watch(visible, async value => {
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem :label="$t('page.system.user.effectiveFrom')" prop="effectiveFrom">
|
||||
<ElFormItem :label="$t('page.system.user.effectiveFrom')" prop="effectiveFrom" style="width: 100%">
|
||||
<ElDatePicker
|
||||
v-model="model.effectiveFrom"
|
||||
class="w-full"
|
||||
style="width: 100%"
|
||||
type="datetime"
|
||||
clearable
|
||||
:placeholder="$t('page.system.user.form.effectiveFrom')"
|
||||
@@ -197,10 +198,11 @@ watch(visible, async value => {
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem :label="$t('page.system.user.effectiveUntil')" prop="effectiveUntil">
|
||||
<ElFormItem :label="$t('page.system.user.effectiveUntil')" prop="effectiveUntil" style="width: 100%">
|
||||
<ElDatePicker
|
||||
v-model="model.effectiveUntil"
|
||||
class="w-full"
|
||||
style="width: 100%"
|
||||
type="datetime"
|
||||
clearable
|
||||
:placeholder="$t('page.system.user.form.effectiveUntil')"
|
||||
|
||||
@@ -232,7 +232,7 @@ watch(visible, async value => {
|
||||
<ElOption
|
||||
v-for="item in props.orgCodeOptions"
|
||||
:key="item.value"
|
||||
:label="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
|
||||
Reference in New Issue
Block a user