--添加测试模块

This commit is contained in:
2025-10-13 11:53:54 +08:00
parent c3c93fe308
commit e1326c7ce8
146 changed files with 11171 additions and 807 deletions

View File

@ -0,0 +1,288 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:width="800"
title="新增角色"
:fixedHeight="true"
@ok="handleSubmit"
>
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
<a-tab-pane key="1" tab="全部">
<a-table
:columns="columns"
:dataSource="roleData"
:rowSelection="rowSelectionAll"
:pagination="pagination"
:loading="loading"
rowKey="id"
size="middle"
:scroll="{ y: 400 }"
@change="handleTableChange"
/>
</a-tab-pane>
<a-tab-pane key="2" tab="已选" force-render>
<a-table
:columns="columns"
:dataSource="selectedList"
:rowSelection="rowSelectionSelected"
rowKey="id"
size="middle"
:scroll="{ y: 400 }"
/>
</a-tab-pane>
<template #rightExtra>
<div class="left-extra">
<a-input
v-model:value="searchText"
placeholder="请输入查询关键字"
@keypress.enter="handleSearch"
style="width: 200px"
>
<template #prefix>
<SearchOutlined style="color: #ccc" />
</template>
</a-input>
<span class="left-text">已选中{{ selectedRowKeys.length }}</span>
</div>
<a-button type="primary" danger :icon="h(DeleteOutlined)" @click="handleClear">
清空
</a-button>
</template>
</a-tabs>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, h, computed, nextTick } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { getRolePageList } from '/@/api/system/role';
import { useI18n } from '/@/hooks/web/useI18n';
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons-vue';
import { cloneDeep } from 'lodash-es';
const { t } = useI18n();
// 表格列配置
const columns = [
{
title: t('角色名称'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('角色编码'),
dataIndex: 'code',
key: 'code',
align: 'center',
},
];
// 响应式数据
const activeKey = ref('1');
const roleData = ref<any[]>([]);
const selectedList = ref<any[]>([]);
const selectedRowKeys = ref<string[]>([]);
const searchText = ref('');
const loading = ref(false);
const total = ref(0);
const currentPage = ref(1);
const pageSize = ref(15);
// 使用Map存储所有已选择的角色数据key为角色idvalue为角色对象
const selectedMap = ref<Map<string, any>>(new Map());
// 分页配置
const pagination = computed(() => ({
total: total.value,
current: currentPage.value,
pageSize: pageSize.value,
showQuickJumper: true,
showTotal: (total: number) => `${total}`,
}));
// 定义事件
const emits = defineEmits(['success']);
// 模态框注册
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false });
// 初始化选择状态
const roles = data.roles || [];
selectedMap.value.clear();
roles.forEach(role => {
selectedMap.value.set(role.id, role);
});
// 更新选择状态
updateSelectionState();
await loadRoleData();
});
// 获取角色数据
const loadRoleData = async (params = {}) => {
loading.value = true;
try {
const res = await getRolePageList({
limit: currentPage.value,
size: pageSize.value,
name: searchText.value,
...params,
});
roleData.value = res.records || res.list || res;
total.value = res.total || res.length || 0;
} catch (error) {
console.error('获取角色列表失败:', error);
} finally {
loading.value = false;
}
};
// 表格翻页事件处理
const handleTableChange = (pagination: any) => {
currentPage.value = pagination.current;
pageSize.value = pagination.pageSize;
loadRoleData();
};
// 更新选择状态
const updateSelectionState = () => {
selectedRowKeys.value = Array.from(selectedMap.value.keys());
selectedList.value = Array.from(selectedMap.value.values());
};
// 全部表格的行选择配置
const rowSelectionAll = computed(() => ({
type: 'checkbox',
selectedRowKeys: selectedRowKeys.value,
onChange: (keys: string[], selectedRows: any[]) => {
// 处理单个选择/取消选择
const currentPageIds = roleData.value.map(item => item.id);
// 移除当前页所有已选择的项
currentPageIds.forEach(id => {
selectedMap.value.delete(id);
});
// 添加新选择的项
selectedRows.forEach(row => {
selectedMap.value.set(row.id, row);
});
updateSelectionState();
},
onSelect: (record: any, selected: boolean) => {
if (selected) {
selectedMap.value.set(record.id, record);
} else {
selectedMap.value.delete(record.id);
}
updateSelectionState();
},
onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {
if (selected) {
selectedRows.forEach(row => {
selectedMap.value.set(row.id, row);
});
} else {
changeRows.forEach(row => {
selectedMap.value.delete(row.id);
});
}
updateSelectionState();
},
}));
// 已选表格的行选择配置
const rowSelectionSelected = computed(() => ({
type: 'checkbox',
selectedRowKeys: selectedList.value.map(item => item.id),
onChange: (keys: string[], selectedRows: any[]) => {
// 清空所有选择
selectedMap.value.clear();
// 添加新选择的项
selectedRows.forEach(row => {
selectedMap.value.set(row.id, row);
});
updateSelectionState();
},
}));
// 标签页切换
const handleTabChange = (key: string) => {
activeKey.value = key;
// 已选标签页不需要额外操作selectedList已经是最新的
};
// 搜索
const handleSearch = () => {
currentPage.value = 1; // 搜索时重置到第一页
loadRoleData({ name: searchText.value });
};
// 清空选择
const handleClear = () => {
selectedMap.value.clear();
updateSelectionState();
};
// 提交
const handleSubmit = () => {
emits('success', Array.from(selectedMap.value.values()));
closeModal();
};
</script>
<style lang="less" scoped>
.left-extra {
display: flex;
align-items: center;
gap: 10px;
.left-text {
color: #5e95ff;
font-weight: 500;
}
}
:deep(.ant-tabs-extra-content) {
display: flex;
align-items: center;
justify-content: space-between;
width: 80%;
margin-left: 60px;
.left-extra {
display: flex;
align-items: center;
.left-text {
width: 90px;
margin-left: 10px;
color: #5e95ff;
}
}
}
:deep(.ant-input-affix-wrapper) {
border-radius: 10px;
background: #f8f8f8;
border: none;
}
:deep(.ant-input) {
background: #f8f8f8;
}
:deep(.ant-table) {
.ant-table-thead > tr > th {
background: #fafafa;
font-weight: 600;
}
}
</style>

View File

@ -107,26 +107,43 @@
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDepartmentTree } from '/@/api/system/department';
import { addUser, getUser, updateUser } from '/@/api/system/user';
import { addUser, getUser, updateUser, getUserNamePrefix } from '/@/api/system/user';
import Icon from '/@/components/Icon/index';
import { testPwdState } from '/@/utils/event/design';
import { PlusOutlined } from '@ant-design/icons-vue';
import RoleModal from './RoleModal.vue';
import RoleModal from './RoleModalV2.vue';
import PostModal from './PostModal.vue';
import OrganizationModal from './OrganizationModal.vue';
import { debounce } from 'lodash-es';
const { t } = useI18n();
const accountFormSchema: FormSchema[] = [
{
field: 'userName',
label: '账号',
component: 'Input',
required: true,
colProps: { span: 12 },
componentProps: {
placeholder: '请输入账号',
{
field: 'userName',
label: '账号',
component: 'Input',
colProps: { span: 12 },
rules: [
{
required: true,
validator: async (_, value) => {
let values = getFieldsValue1();
let prefix = await getUserNamePrefix();
if (!value) {
return Promise.reject('请输入账号');
}
if (values?.isSelfBuild === 'Y' && !value.startsWith(prefix)) {
return Promise.reject('自建用户账号必须带有前缀:' + prefix);
}
return Promise.resolve();
},
},
],
required: true,
componentProps: {
placeholder: '请输入账号',
},
},
},
{
field: 'name',
label: '用户姓名',
@ -285,6 +302,51 @@
unCheckedValue: 'N',
}
},
{
field: 'isSelfBuild',
label: '自建用户',
component: 'Switch',
colProps: { span: 12 },
helpComponentProps: { maxWidth: '400px' },
componentProps: ({ formModel, formActionType }) => {
return {
checkedValue: 'Y',
unCheckedValue: 'N',
onChange: debounce((e: ChangeEvent) => {
//fieldInt组件会根据fieldString的组件值 变化 +2 //参考view/code/demo2/data/index.ts
if (formModel.isSelfBuild === 'N') {
formModel.isPushMQ = 'N';
}
}, 500),
}
}
},
{
field: 'isPushMQ',
label: '推送mq',
component: 'Switch',
colProps: { span: 12 },
helpMessage: '开启推送mq系统会将自建用户推送到移动办公',
helpComponentProps: { maxWidth: '400px' },
componentProps: {
checkedValue: 'Y',
unCheckedValue: 'N',
},
},
{
field: 'validityTime',
label: '有效期',
colProps: {
span: 12,
},
component: 'RangePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
style: { width: '100%' },
getPopupContainer: () => document.body,
}
},
{
label: t('备注'),
field: 'remark',
@ -536,6 +598,7 @@
registerForm1,
{
setFieldsValue: setFieldsValue1,
getFieldsValue: getFieldsValue1,
updateSchema: updateSchema1,
resetFields: resetFields1,
validate: validate1,
@ -624,6 +687,16 @@
rowId.value = data.id;
const record = await getUser(data.id);
const departmentIds = record.departmentIds?.split(',')||[];
let validityTime = []
if (record.validityStartTime) {
validityTime[0] = record.validityStartTime;
}
if (record.validityEndTime) {
validityTime[1] = record.validityEndTime;
}
if (validityTime.length > 0) {
record.validityTime = validityTime;
}
setFieldsValue1({
...record,
departmentIds,
@ -640,6 +713,15 @@
setTableData2(postDatasource.value);
setTableData3(orgDatasource.value);
setTableData4(deptDatasource.value);
} else {
//新增
setFieldsValue1({
isSync: 'N',
isMain: 'N',
isSelfBuild: 'N',
isPushMQ: 'N',
});
getUserNamePrefixName();
}
const treeData = await getDepartmentTree();
@ -702,6 +784,19 @@
orgs: deptDatasource.value,
});
};
async function getUserNamePrefixName() {
let userNamePrefix = await getUserNamePrefix();
setFieldsValue1({
userName: userNamePrefix
});
// updateSchema1([{
// field: 'userName',
// defaultValue: userNamePrefix?.value,
// componentProps: { addonBefore: userNamePrefix?.value }
// }])
}
const handleDelete = (index, type, record) => {
// 映射不同类型对应的数据源
const dataSources = {
@ -765,7 +860,10 @@
departments
};
setModalProps({ confirmLoading: true });
if (data.validityTime) {
data.validityStartTime = data.validityTime[0];
data.validityEndTime = data.validityTime[1];
}
// TODO custom api
if (!unref(isUpdate)) {
//false 新增