!42 czh-20260616-更换选人组件;调深系统颜色

Merge pull request !42 from 陈志浩/feature/tasklist
This commit is contained in:
陈志浩
2026-06-16 07:10:43 +00:00
committed by Gitee
11 changed files with 101 additions and 60 deletions
+2 -2
View File
@@ -33,7 +33,7 @@
const modeAction = (data) => { const modeAction = (data) => {
if (data.token) { if (data.token) {
if (getDarkMode.value === ThemeEnum.DARK) { if (getDarkMode.value === ThemeEnum.DARK) {
Object.assign(data.token, { colorTextBase: 'fff' }); Object.assign(data.token, { colorTextBase: '#fff' });
} else { } else {
Object.assign(data.token, { colorTextBase: '#333' }); Object.assign(data.token, { colorTextBase: '#333' });
} }
@@ -77,7 +77,7 @@
colorPrimary: primary, colorPrimary: primary,
wireframe: true, wireframe: true,
fontSize: 14, fontSize: 14,
colorTextBase: '#333', colorTextBase: '#1a1a1a',
colorSuccess: '#55D187', colorSuccess: '#55D187',
colorInfo: primary, colorInfo: primary,
borderRadius: 4, borderRadius: 4,
+2
View File
@@ -82,6 +82,7 @@ import RoleSelectInput from './jeecg/components/roleSelect/RoleSelectInput.vue';
import {DatePickerInFilter, CascaderPcaInFilter} from "@/components/InFilter"; import {DatePickerInFilter, CascaderPcaInFilter} from "@/components/InFilter";
import SzSelectUser from '@/components/semri/userComponent/SzUserSelect.vue'; import SzSelectUser from '@/components/semri/userComponent/SzUserSelect.vue';
import SzSelectUserByDept from '@/components/semri/userComponent/SzSelectUserByDept.vue'; import SzSelectUserByDept from '@/components/semri/userComponent/SzSelectUserByDept.vue';
import SzDeptUserSelect from '@/components/semri/deptUserComponent/SzDeptUserModal.vue';
const componentMap = new Map<ComponentType, Component>(); const componentMap = new Map<ComponentType, Component>();
@@ -182,6 +183,7 @@ componentMap.set('RoleSelect', RoleSelectInput);
componentMap.set('JInputSelect', JInputSelect); componentMap.set('JInputSelect', JInputSelect);
componentMap.set('SzSelectUser', SzSelectUser); componentMap.set('SzSelectUser', SzSelectUser);
componentMap.set('SzSelectUserByDept',SzSelectUserByDept) componentMap.set('SzSelectUserByDept',SzSelectUserByDept)
componentMap.set('SzDeptUserSelect', SzDeptUserSelect)
export function add(compName: ComponentType, component: Component) { export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component); componentMap.set(compName, component);
@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<a-row class="j-select-row" type="flex" :gutter="8"> <a-row v-if="!hideTrigger" class="j-select-row" type="flex" :gutter="8">
<a-col class="left"> <a-col class="left">
<a-select <a-select
:value="selectDisplayValue" :value="selectDisplayValue"
@@ -18,7 +18,7 @@
</a-row> </a-row>
<a-modal <a-modal
:width="1200" :width="1200"
title="选择人员" :title="title"
v-model:open="visible" v-model:open="visible"
centered centered
:confirm-loading="confirmLoading" :confirm-loading="confirmLoading"
@@ -60,6 +60,9 @@ const props = withDefaults(
isSpecial?: number; isSpecial?: number;
disabled?: boolean; disabled?: boolean;
multi?: boolean; multi?: boolean;
valueKey?: 'id' | 'username';
title?: string;
hideTrigger?: boolean;
}>(), }>(),
{ {
value: '', value: '',
@@ -69,10 +72,13 @@ const props = withDefaults(
isSpecial: 0, isSpecial: 0,
disabled: false, disabled: false,
multi: true, multi: true,
valueKey: 'username',
title: '选择人员',
hideTrigger: false,
} }
); );
const emit = defineEmits(['update:value', 'change']); const emit = defineEmits(['update:value', 'change', 'confirm']);
const userStore = useUserStore(); const userStore = useUserStore();
const visible = ref(false); const visible = ref(false);
@@ -100,11 +106,12 @@ watch(
{ immediate: true } { immediate: true }
); );
async function loadUsersByUsernames(usernames: string) { async function loadUsersByUsernames(valueStr: string) {
try { try {
const paramKey = props.valueKey === 'id' ? 'id' : 'username';
const res: any = await defHttp.get({ const res: any = await defHttp.get({
url: '/sys/user/queryUserComponentData', url: '/sys/user/queryUserComponentData',
params: { username: usernames, isMultiTranslate: 'true', pageNo: 1, pageSize: 9999 }, params: { [paramKey]: valueStr, isMultiTranslate: 'true', pageNo: 1, pageSize: 9999 },
}); });
innerSelectedUsers.value = [...(res.records || res)]; innerSelectedUsers.value = [...(res.records || res)];
} catch { } catch {
@@ -118,6 +125,17 @@ function openModal() {
visible.value = true; visible.value = true;
} }
async function open() {
if (props.disabled) return;
if (props.value) {
await loadUsersByUsernames(props.value);
} else {
innerSelectedUsers.value = [];
}
tempSelectedUsers.value = [...innerSelectedUsers.value];
visible.value = true;
}
function handleTempUsersChange(users: UserItem[]) { function handleTempUsersChange(users: UserItem[]) {
tempSelectedUsers.value = [...users]; tempSelectedUsers.value = [...users];
} }
@@ -128,15 +146,19 @@ function clearTemp() {
function handleOk() { function handleOk() {
innerSelectedUsers.value = [...tempSelectedUsers.value]; innerSelectedUsers.value = [...tempSelectedUsers.value];
const usernames = innerSelectedUsers.value.map((u) => u.username).join(','); const keyValue = innerSelectedUsers.value.map((u) => u[props.valueKey]).join(',');
emit('update:value', usernames); const nameValue = innerSelectedUsers.value.map((u) => u.realname).join(',');
emit('change', usernames); emit('update:value', keyValue);
emit('change', keyValue);
emit('confirm', keyValue, nameValue);
visible.value = false; visible.value = false;
} }
function handleCancel() { function handleCancel() {
visible.value = false; visible.value = false;
} }
defineExpose({ open });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@@ -38,7 +38,7 @@
:columns="columns" :columns="columns"
:data-source="dataSource" :data-source="dataSource"
:pagination="ipagination" :pagination="ipagination"
:row-selection="{ selectedRowKeys: selectedRowKeys, onSelectAll: onSelectAll, onSelect: onSelect, type: getType }" :row-selection="{ selectedRowKeys: selectedRowKeys, onSelectAll: onSelectAll, onSelect: onSelect, type: getType, preserveSelectedRowKeys: true }"
:loading="loading" :loading="loading"
@change="handleTableChange" @change="handleTableChange"
:custom-row="customRow" :custom-row="customRow"
@@ -291,22 +291,20 @@ function onSelect(record: UserItem, selected: boolean) {
} else { } else {
deleteIfExist(selectedRows.value, record, 'id'); deleteIfExist(selectedRows.value, record, 'id');
} }
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
emit('update:checkedUser', selectedRows.value); emit('update:checkedUser', selectedRows.value);
} }
function onSelectAll(selected: boolean, selectedRecords: UserItem[], changeRecords: UserItem[]) { function onSelectAll(selected: boolean, _selectedRecords: UserItem[], changeRecords: UserItem[]) {
const changedRows = Array.isArray(changeRecords) ? changeRecords.filter(Boolean) : [];
if (selected) { if (selected) {
selectedRecords.forEach((item) => { changedRows.forEach((item) => {
pushIfNotExist(selectedRows.value, item, 'id'); pushIfNotExist(selectedRows.value, item, 'id');
}); });
} else { } else {
const rowsToRemove = changeRecords && changeRecords.length > 0 ? changeRecords : dataSource.value; changedRows.forEach((item) => {
rowsToRemove.forEach((item) => {
deleteIfExist(selectedRows.value, item, 'id'); deleteIfExist(selectedRows.value, item, 'id');
}); });
} }
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
emit('update:checkedUser', selectedRows.value); emit('update:checkedUser', selectedRows.value);
} }
@@ -345,7 +343,6 @@ function selectAll() {
allUserList.value.forEach((item) => { allUserList.value.forEach((item) => {
pushIfNotExist(selectedRows.value, item, 'id'); pushIfNotExist(selectedRows.value, item, 'id');
}); });
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
emit('update:checkedUser', selectedRows.value); emit('update:checkedUser', selectedRows.value);
} }
@@ -370,7 +367,6 @@ function customRow(record: UserItem) {
} else { } else {
pushIfNotExist(selectedRows.value, record, 'id'); pushIfNotExist(selectedRows.value, record, 'id');
} }
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
emit('update:checkedUser', selectedRows.value); emit('update:checkedUser', selectedRows.value);
}, },
}; };
+5 -5
View File
@@ -5,7 +5,7 @@
html[data-theme='light'] { html[data-theme='light'] {
// update-begin--author:liaozhiyang---date:20240407---for:【QQYUN-8774】给body加上打底的字体颜色 // update-begin--author:liaozhiyang---date:20240407---for:【QQYUN-8774】给body加上打底的字体颜色
body{ body{
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.90);
} }
// update-end--author:liaozhiyang---date:20240407---for:【QQYUN-8774】给body加上打底的字体颜色 // update-end--author:liaozhiyang---date:20240407---for:【QQYUN-8774】给body加上打底的字体颜色
.text-secondary { .text-secondary {
@@ -13,22 +13,22 @@ html[data-theme='light'] {
} }
/*【美化】自定义table字体颜色*/ /*【美化】自定义table字体颜色*/
.ant-table { .ant-table {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.90);
} }
/*【美化】自定义table字体颜色*/ /*【美化】自定义table字体颜色*/
/*【美化】自定义form字体颜色*/ /*【美化】自定义form字体颜色*/
.ant-select-multiple .ant-select-selection-item-content { .ant-select-multiple .ant-select-selection-item-content {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.90);
} }
.ant-input-affix-wrapper > input.ant-input { .ant-input-affix-wrapper > input.ant-input {
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-189】统一只读样式 // update-begin--author:liaozhiyang---date:20240605---for:【TV360X-189】统一只读样式
&:not([disabled]) { &:not([disabled]) {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.90);
} }
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-189】统一只读样式 // update-end--author:liaozhiyang---date:20240605---for:【TV360X-189】统一只读样式
} }
.ant-select-single.ant-select-show-arrow .ant-select-selection-item, .ant-select-single.ant-select-show-arrow { .ant-select-single.ant-select-show-arrow .ant-select-selection-item, .ant-select-single.ant-select-show-arrow {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.90);
} }
/*【美化】自定义form字体颜色*/ /*【美化】自定义form字体颜色*/
+6 -8
View File
@@ -260,21 +260,19 @@ export const createTaskFormSchema: FormSchema[] = [
{ {
label: '负责人', label: '负责人',
field: 'assigneeId', field: 'assigneeId',
component: 'JSelectUser', component: 'SzDeptUserSelect',
componentProps: { componentProps: {
labelKey: 'realname', valueKey: 'id',
rowKey: 'id', multi: false,
isRadioSelection: true,
}, },
}, },
{ {
label: '分配人', label: '分配人',
field: 'assignId', field: 'assignId',
component: 'JSelectUser', component: 'SzDeptUserSelect',
componentProps: { componentProps: {
labelKey: 'realname', valueKey: 'id',
rowKey: 'id', multi: false,
isRadioSelection: true,
}, },
}, },
{ {
@@ -68,11 +68,11 @@
</span> </span>
</div> </div>
<div class="add-panel-body"> <div class="add-panel-body">
<SzUserSelect <SzDeptUserModal
v-model:value="addForm.userIds" v-model:value="addForm.userIds"
:labelKey="'realname'" valueKey="id"
:rowKey="'id'" :role="1"
placeholder="搜索并选择用户(支持多选)" :secLevel="secretLevel"
/> />
<div class="add-panel-row"> <div class="add-panel-row">
<span class="add-panel-label">权限</span> <span class="add-panel-label">权限</span>
@@ -106,7 +106,7 @@
import { ref, reactive, computed, nextTick } from 'vue'; import { ref, reactive, computed, nextTick } from 'vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import SzUserSelect from '/@/components/semri/userComponent/SzUserSelect.vue'; import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import { getCollaborators, addCollaborator, removeCollaborator, updateCollaboratorPermission } from '../TaskList.api'; import { getCollaborators, addCollaborator, removeCollaborator, updateCollaboratorPermission } from '../TaskList.api';
import { getAvatarColor } from '../types'; import { getAvatarColor } from '../types';
import { checkUserSecLevel } from '../utils/taskUtils'; import { checkUserSecLevel } from '../utils/taskUtils';
+23 -6
View File
@@ -11,6 +11,12 @@
<Icon icon="ant-design:plus-outlined" /> <Icon icon="ant-design:plus-outlined" />
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-input-search
v-model:value="searchKeyword"
placeholder="搜索计划名称"
class="list-search-input"
allow-clear
/>
</div> </div>
<a-tooltip title="使用帮助"> <a-tooltip title="使用帮助">
<a-button type="text" size="small" class="help-btn" @click="helpModalOpen = true"> <a-button type="text" size="small" class="help-btn" @click="helpModalOpen = true">
@@ -813,13 +819,15 @@
</template> </template>
</div> </div>
<UserSelectModal <SzDeptUserModal
ref="userSelectModalRef" ref="userSelectModalRef"
hide-trigger
:title="userSelectModalType === 'assignee' ? '选择负责人' : userSelectModalType === 'participant' ? '选择参与人' : '选择关注人'" :title="userSelectModalType === 'assignee' ? '选择负责人' : userSelectModalType === 'participant' ? '选择参与人' : '选择关注人'"
:user-ids="userSelectModalCurrentIds" :value="userSelectModalCurrentIds"
:user-names="userSelectModalCurrentNames" value-key="id"
:multiple="true" :multi="true"
:secret-level="currentList?.secretLevel || 1" :sec-level="currentList?.secretLevel || 1"
:role="1"
@confirm="onUserSelectConfirm" @confirm="onUserSelectConfirm"
/> />
<HelpModal v-model:open="helpModalOpen" /> <HelpModal v-model:open="helpModalOpen" />
@@ -834,7 +842,7 @@
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import AvatarDisplay from './AvatarDisplay.vue'; import AvatarDisplay from './AvatarDisplay.vue';
import draggable from 'vuedraggable'; import draggable from 'vuedraggable';
import UserSelectModal from './UserSelectModal.vue'; import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import { useUserStore } from '/@/store/modules/user'; import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { FIELD_CONFIG, GROUP_OPTIONS, SORT_OPTIONS, MAX_TASK_DEPTH, FILTER_FIELD_CONFIGS, FILTER_OPERATOR_LABELS, SECRET_LEVEL_COLOR } from '../types'; import { FIELD_CONFIG, GROUP_OPTIONS, SORT_OPTIONS, MAX_TASK_DEPTH, FILTER_FIELD_CONFIGS, FILTER_OPERATOR_LABELS, SECRET_LEVEL_COLOR } from '../types';
@@ -920,6 +928,7 @@
}); });
const submittingGroup = ref(false); const submittingGroup = ref(false);
const submittingRename = ref(false); const submittingRename = ref(false);
const searchKeyword = ref('');
const filterVisible = ref(false); const filterVisible = ref(false);
const fieldConfigVisible = ref(false); const fieldConfigVisible = ref(false);
const collapsedGroups = ref<Set<string>>(new Set()); const collapsedGroups = ref<Set<string>>(new Set());
@@ -1118,13 +1127,16 @@
const allTaskLists = computed(() => { const allTaskLists = computed(() => {
const lists: TaskList[] = []; const lists: TaskList[] = [];
const keyword = searchKeyword.value.trim().toLowerCase();
for (const group of props.taskListGroups) { for (const group of props.taskListGroups) {
for (const list of group.taskLists) { for (const list of group.taskLists) {
if (list.delFlag !== 1) { if (list.delFlag !== 1) {
if (!keyword || list.name.toLowerCase().includes(keyword)) {
lists.push(list); lists.push(list);
} }
} }
} }
}
return lists; return lists;
}); });
@@ -1923,6 +1935,11 @@
} }
} }
.list-search-input {
width: 220px;
margin-left: 16px;
}
.help-btn { .help-btn {
color: #888; color: #888;
font-size: 18px; font-size: 18px;
@@ -357,13 +357,15 @@
</div> </div>
</template> </template>
<UserSelectModal <SzDeptUserModal
ref="userSelectModalRef" ref="userSelectModalRef"
hide-trigger
:title="drawerUserSelectType === 'assignee' ? '选择负责人' : drawerUserSelectType === 'participant' ? '选择参与人' : '选择关注人'" :title="drawerUserSelectType === 'assignee' ? '选择负责人' : drawerUserSelectType === 'participant' ? '选择参与人' : '选择关注人'"
:user-ids="drawerUserSelectCurrentIds" :value="drawerUserSelectCurrentIds"
:user-names="drawerUserSelectCurrentNames" value-key="id"
:multiple="true" :multi="true"
:secret-level="currentListSecretLevel || 1" :sec-level="currentListSecretLevel || 1"
:role="1"
@confirm="onUserSelectModalConfirm" @confirm="onUserSelectModalConfirm"
/> />
@@ -376,6 +378,7 @@
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import AvatarDisplay from './AvatarDisplay.vue'; import AvatarDisplay from './AvatarDisplay.vue';
import SzUserSelectBySecLevel from '/@/components/semri/userComponent/SzUserSelectBySecLevel.vue'; import SzUserSelectBySecLevel from '/@/components/semri/userComponent/SzUserSelectBySecLevel.vue';
import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import TaskAttachmentUpload from './TaskAttachmentUpload.vue'; import TaskAttachmentUpload from './TaskAttachmentUpload.vue';
import TaskDateRangePicker from './TaskDateRangePicker.vue'; import TaskDateRangePicker from './TaskDateRangePicker.vue';
import draggable from 'vuedraggable'; import draggable from 'vuedraggable';
@@ -443,7 +446,7 @@
const drawerUserSelectType = ref<'assignee' | 'followers' | 'participant'>('assignee'); const drawerUserSelectType = ref<'assignee' | 'followers' | 'participant'>('assignee');
const drawerUserSelectCurrentIds = ref<string>(''); const drawerUserSelectCurrentIds = ref<string>('');
const drawerUserSelectCurrentNames = ref<string>(''); const drawerUserSelectCurrentNames = ref<string>('');
const userSelectModalRef = ref<InstanceType<typeof UserSelectModal> | null>(null); const userSelectModalRef = ref<InstanceType<typeof SzDeptUserModal> | null>(null);
const fileUploadRef = ref<InstanceType<typeof TaskAttachmentUpload> | null>(null); const fileUploadRef = ref<InstanceType<typeof TaskAttachmentUpload> | null>(null);
const parentTask = computed(() => { const parentTask = computed(() => {
@@ -336,12 +336,14 @@
</div> </div>
</div> </div>
<UserSelectModal <SzDeptUserModal
ref="userSelectModalRef" ref="userSelectModalRef"
hide-trigger
:title="userSelectModalType === 'assignee' ? '选择负责人' : userSelectModalType === 'participant' ? '选择参与人' : '选择关注人'" :title="userSelectModalType === 'assignee' ? '选择负责人' : userSelectModalType === 'participant' ? '选择参与人' : '选择关注人'"
:user-ids="userSelectModalCurrentIds" :value="userSelectModalCurrentIds"
:user-names="userSelectModalCurrentNames" value-key="id"
:multiple="true" :multi="true"
:role="1"
@confirm="onUserSelectConfirm" @confirm="onUserSelectConfirm"
/> />
</a-spin> </a-spin>
@@ -353,7 +355,7 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import AvatarDisplay from './AvatarDisplay.vue'; import AvatarDisplay from './AvatarDisplay.vue';
import UserSelectModal from './UserSelectModal.vue'; import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import { FIELD_CONFIG, SORT_OPTIONS, MAX_TASK_DEPTH } from '../types'; import { FIELD_CONFIG, SORT_OPTIONS, MAX_TASK_DEPTH } from '../types';
import { PRIORITY_OPTIONS, getPriorityBg, getPriorityLabel, isOverdue } from '../utils/taskUtils'; import { PRIORITY_OPTIONS, getPriorityBg, getPriorityLabel, isOverdue } from '../utils/taskUtils';
import type { TaskItem, StatusFilter, SortField, SortDirection, FieldKey, FlatTaskItem } from '../types'; import type { TaskItem, StatusFilter, SortField, SortDirection, FieldKey, FlatTaskItem } from '../types';
@@ -47,11 +47,12 @@
</span> </span>
</div> </div>
<div class="add-panel-body"> <div class="add-panel-body">
<SzUserSelect <SzDeptUserModal
v-model:value="addForm.userIds" v-model:value="addForm.userIds"
:labelKey="'realname'" valueKey="id"
:rowKey="'id'" :multi="multiple"
placeholder="搜索并选择用户(支持多选)" :role="1"
:secLevel="secretLevel"
/> />
</div> </div>
<div class="add-panel-footer"> <div class="add-panel-footer">
@@ -78,7 +79,7 @@
import { ref, reactive, computed } from 'vue'; import { ref, reactive, computed } from 'vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import SzUserSelect from '/@/components/semri/userComponent/SzUserSelect.vue'; import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import { getAvatarColor } from '../types'; import { getAvatarColor } from '../types';
import { getUserList } from '/@/api/common/api'; import { getUserList } from '/@/api/common/api';
import { checkUserSecLevel } from '../utils/taskUtils'; import { checkUserSecLevel } from '../utils/taskUtils';