--添加测试模块
This commit is contained in:
216
src/views/system/Testfrom3/components/Form.vue
Normal file
216
src/views/system/Testfrom3/components/Form.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<SimpleForm
|
||||
ref="systemFormRef"
|
||||
:formProps="data.formDataProps"
|
||||
:formModel="{}"
|
||||
:isWorkFlow="props.fromPage!=FromPageType.MENU"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref,onBeforeMount,onMounted } from 'vue';
|
||||
import { formProps, formEventConfigs ,formConfig} from './config';
|
||||
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||
import { addTestfrom3, getTestfrom3, updateTestfrom3 } from '/@/api/system/Testfrom3';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormDataProps } from '/@/components/Designer/src/types';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { useFormConfig } from '/@/hooks/web/useFormConfig';
|
||||
import { FromPageType } from '/@/enums/workflowEnum';
|
||||
import { createFormEvent, getFormDataEvent, loadFormEvent, submitFormEvent,} from '/@/hooks/web/useFormEvent';
|
||||
import { changeWorkFlowForm, changeSchemaDisabled } from '/@/hooks/web/useWorkFlowForm';
|
||||
import { WorkFlowFormParams } from '/@/model/workflow/bpmnConfig';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { filterFormSchemaAuth } = usePermission();
|
||||
const { mergeFormSchemas,mergeFormEventConfigs } = useFormConfig();
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const RowKey = 'id';
|
||||
const emits = defineEmits(['changeUploadComponentIds','loadingCompleted', 'form-mounted']);
|
||||
const props = defineProps({
|
||||
fromPage: {
|
||||
type: Number,
|
||||
default: FromPageType.MENU,
|
||||
},
|
||||
});
|
||||
const systemFormRef = ref();
|
||||
const data: { formDataProps: FormDataProps } = reactive({
|
||||
formDataProps: {schemas:[]} as FormDataProps,
|
||||
});
|
||||
const state = reactive({
|
||||
formModel: {},
|
||||
});
|
||||
|
||||
let customFormEventConfigs=[];
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 合并渲染覆盖配置中的字段配置、表单事件配置
|
||||
await mergeCustomFormRenderConfig();
|
||||
|
||||
if (props.fromPage == FromPageType.MENU) {
|
||||
setMenuPermission();
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
} else if (props.fromPage == FromPageType.FLOW) {
|
||||
emits('loadingCompleted'); //告诉系统表单已经加载完毕
|
||||
// loadingCompleted后 工作流页面直接利用Ref调用setWorkFlowForm方法
|
||||
} else if (props.fromPage == FromPageType.PREVIEW) {
|
||||
// 预览 无需权限,表单事件也无需执行
|
||||
} else if (props.fromPage == FromPageType.DESKTOP) {
|
||||
// 桌面设计 表单事件需要执行
|
||||
emits('loadingCompleted'); //告诉系统表单已经加载完毕
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
}
|
||||
emits('form-mounted', formProps);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function mergeCustomFormRenderConfig() {
|
||||
let cloneProps=cloneDeep(formProps);
|
||||
let fEventConfigs=cloneDeep(formEventConfigs);
|
||||
if (formConfig.useCustomConfig) {
|
||||
if(props.fromPage !== FromPageType.FLOW){
|
||||
let formPath=currentRoute.value.query.formPath;
|
||||
//1.合并字段配置
|
||||
cloneProps.schemas=await mergeFormSchemas({formSchema:cloneProps.schemas!,formPath:formPath});
|
||||
//2.合并表单事件配置
|
||||
fEventConfigs=await mergeFormEventConfigs({formEventConfigs:fEventConfigs,formPath:formPath});
|
||||
}
|
||||
}
|
||||
data.formDataProps=cloneProps;
|
||||
customFormEventConfigs=fEventConfigs;
|
||||
}
|
||||
|
||||
// 根据菜单页面权限,设置表单属性(必填,禁用,显示)
|
||||
function setMenuPermission() {
|
||||
data.formDataProps.schemas = filterFormSchemaAuth(data.formDataProps.schemas!);
|
||||
}
|
||||
|
||||
// 校验form 通过返回表单数据
|
||||
async function validate() {
|
||||
let values = [];
|
||||
try {
|
||||
values = await systemFormRef.value?.validate();
|
||||
//添加隐藏组件
|
||||
if (data.formDataProps.hiddenComponent?.length) {
|
||||
data.formDataProps.hiddenComponent.forEach((component) => {
|
||||
values[component.bindField] = component.value;
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
return values;
|
||||
}
|
||||
// 根据行唯一ID查询行数据,并设置表单数据 【编辑】
|
||||
async function setFormDataFromId(rowId, skipUpdate) {
|
||||
try {
|
||||
const record = await getTestfrom3(rowId);
|
||||
if (skipUpdate) {
|
||||
return record;
|
||||
}
|
||||
setFieldsValue(record);
|
||||
state.formModel = record;
|
||||
await getFormDataEvent(customFormEventConfigs, state.formModel, systemFormRef.value, formProps.schemas); //表单事件:获取表单数据
|
||||
return record;
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
// 辅助设置表单数据
|
||||
function setFieldsValue(record) {
|
||||
systemFormRef.value.setFieldsValue(record);
|
||||
}
|
||||
// 重置表单数据
|
||||
async function resetFields() {
|
||||
await systemFormRef.value.resetFields();
|
||||
}
|
||||
// 设置表单数据全部为Disabled 【查看】
|
||||
async function setDisabledForm(isDisabled) {
|
||||
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas),isDisabled);
|
||||
}
|
||||
// 获取行键值
|
||||
function getRowKey() {
|
||||
return RowKey;
|
||||
}
|
||||
// 更新api表单数据
|
||||
async function update({ values, rowId }) {
|
||||
try {
|
||||
values[RowKey] = rowId;
|
||||
state.formModel = values;
|
||||
let saveVal = await updateTestfrom3(values);
|
||||
await submitFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:提交表单
|
||||
return saveVal;
|
||||
} catch (error) {}
|
||||
}
|
||||
// 新增api表单数据
|
||||
async function add(values) {
|
||||
try {
|
||||
state.formModel = values;
|
||||
let saveVal = await addTestfrom3(values);
|
||||
await submitFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:提交表单
|
||||
return saveVal;
|
||||
} catch (error) {}
|
||||
}
|
||||
// 根据工作流页面权限,设置表单属性(必填,禁用,显示)
|
||||
async function setWorkFlowForm(obj: WorkFlowFormParams) {
|
||||
try {
|
||||
const cloneProps=cloneDeep(formProps);
|
||||
customFormEventConfigs=cloneDeep(formEventConfigs);
|
||||
if (formConfig.useCustomConfig) {
|
||||
const parts = obj.formConfigKey.split('_');
|
||||
const formId=parts[1];
|
||||
cloneProps.schemas=await mergeFormSchemas({formSchema:cloneProps.schemas!,formId:formId});
|
||||
customFormEventConfigs=await mergeFormEventConfigs({formEventConfigs:customFormEventConfigs,formId:formId});
|
||||
}
|
||||
|
||||
let flowData = changeWorkFlowForm(cloneProps, obj);
|
||||
let { buildOptionJson, uploadComponentIds, formModels, isViewProcess } = flowData;
|
||||
data.formDataProps = buildOptionJson;
|
||||
emits('changeUploadComponentIds', uploadComponentIds); //工作流中必须保存上传组件id【附件汇总需要】
|
||||
if (isViewProcess) {
|
||||
setDisabledForm(); //查看
|
||||
}
|
||||
state.formModel = formModels;
|
||||
if(formModels[RowKey]) {
|
||||
setFormDataFromId(formModels[RowKey], false)
|
||||
} else {
|
||||
setFieldsValue(formModels)
|
||||
}
|
||||
} catch (error) {}
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
}
|
||||
defineExpose({
|
||||
setFieldsValue,
|
||||
resetFields,
|
||||
validate,
|
||||
add,
|
||||
update,
|
||||
setFormDataFromId,
|
||||
setDisabledForm,
|
||||
setMenuPermission,
|
||||
setWorkFlowForm,
|
||||
getRowKey,
|
||||
});
|
||||
</script>
|
||||
110
src/views/system/Testfrom3/components/Testfrom3Modal.vue
Normal file
110
src/views/system/Testfrom3/components/Testfrom3Modal.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" @cancel="handleClose" :paddingRight="15" :bodyStyle="{ minHeight: '400px !important' }">
|
||||
<ModalForm ref="formRef" :fromPage="FromPageType.MENU" />
|
||||
</BasicModal>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { formProps } from './config';
|
||||
import ModalForm from './Form.vue';
|
||||
import { FromPageType } from '/@/enums/workflowEnum';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const { notification } = useMessage();
|
||||
const formRef = ref();
|
||||
const state = reactive({
|
||||
formModel: {},
|
||||
isUpdate: true,
|
||||
isView: false,
|
||||
isCopy: false,
|
||||
rowId: '',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
state.isUpdate = !!data?.isUpdate;
|
||||
state.isView = !!data?.isView;
|
||||
state.isCopy = !!data?.isCopy;
|
||||
|
||||
setModalProps({
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
showCancelBtn: !state.isView,
|
||||
showOkBtn: !state.isView,
|
||||
canFullscreen: true,
|
||||
width: 900,
|
||||
});
|
||||
if (state.isUpdate || state.isView || state.isCopy) {
|
||||
state.rowId = data.id;
|
||||
if (state.isView) {
|
||||
await formRef.value.setDisabledForm();
|
||||
}
|
||||
await formRef.value.setFormDataFromId(state.rowId);
|
||||
} else {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (state.isView ? '查看' : !state.isUpdate ? '新增' : '编辑'));
|
||||
|
||||
async function saveModal() {
|
||||
let saveSuccess = false;
|
||||
try {
|
||||
const values = await formRef.value?.validate();
|
||||
//添加隐藏组件
|
||||
if (formProps.hiddenComponent?.length) {
|
||||
formProps.hiddenComponent.forEach((component) => {
|
||||
values[component.bindField] = component.value;
|
||||
});
|
||||
}
|
||||
if (values !== false) {
|
||||
try {
|
||||
if (!state.isUpdate || state.isCopy) {
|
||||
saveSuccess = await formRef.value.add(values);
|
||||
} else {
|
||||
saveSuccess = await formRef.value.update({ values, rowId: state.rowId });
|
||||
}
|
||||
return saveSuccess;
|
||||
} catch (error) {}
|
||||
}
|
||||
} catch (error) {
|
||||
return saveSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const saveSuccess = await saveModal();
|
||||
setModalProps({ confirmLoading: true });
|
||||
if (saveSuccess) {
|
||||
if (!state.isUpdate || state.isCopy) {
|
||||
//false 新增
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('新增成功!'),
|
||||
}); //提示消息
|
||||
} else {
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('修改成功!'),
|
||||
}); //提示消息
|
||||
}
|
||||
closeModal();
|
||||
formRef.value.resetFields();
|
||||
emit('success');
|
||||
}
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
</script>
|
||||
231
src/views/system/Testfrom3/components/config.ts
Normal file
231
src/views/system/Testfrom3/components/config.ts
Normal file
@ -0,0 +1,231 @@
|
||||
import { FormProps, FormSchema } from '/@/components/Form';
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
|
||||
export const formConfig = {
|
||||
useCustomConfig: false,
|
||||
};
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'danXingWenBen5518',
|
||||
label: '单行文本',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'danXingWenBen5841',
|
||||
label: '单行文本',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'jiShuZuJian6835',
|
||||
label: '计数组件',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'danXingWenBen5518',
|
||||
title: '单行文本',
|
||||
componentType: 'input',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'danXingWenBen5841',
|
||||
title: '单行文本',
|
||||
componentType: 'input',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'jiShuZuJian6835',
|
||||
title: '计数组件',
|
||||
componentType: 'number',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
];
|
||||
//表单事件
|
||||
export const formEventConfigs = {
|
||||
0: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#2774ff',
|
||||
text: '开始节点',
|
||||
icon: '#icon-kaishi',
|
||||
bgcColor: '#D8E5FF',
|
||||
isUserDefined: false,
|
||||
},
|
||||
{
|
||||
color: '#F6AB01',
|
||||
icon: '#icon-chushihua',
|
||||
text: '初始化表单',
|
||||
bgcColor: '#f9f5ea',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
1: [
|
||||
{
|
||||
color: '#B36EDB',
|
||||
icon: '#icon-shujufenxi',
|
||||
text: '获取表单数据',
|
||||
detail: '(新增无此操作)',
|
||||
bgcColor: '#F8F2FC',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
2: [
|
||||
{
|
||||
color: '#F8625C',
|
||||
icon: '#icon-jiazai',
|
||||
text: '加载表单',
|
||||
bgcColor: '#FFF1F1',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
3: [
|
||||
{
|
||||
color: '#6C6AE0',
|
||||
icon: '#icon-jsontijiao',
|
||||
text: '提交表单',
|
||||
bgcColor: '#F5F4FF',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
4: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#F8625C',
|
||||
text: '结束节点',
|
||||
icon: '#icon-jieshuzhiliao',
|
||||
bgcColor: '#FFD6D6',
|
||||
isLast: true,
|
||||
isUserDefined: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
export const formProps: FormProps = {
|
||||
labelCol: { span: 3, offset: 0 },
|
||||
labelAlign: 'right',
|
||||
layout: 'horizontal',
|
||||
size: 'default',
|
||||
schemas: [
|
||||
{
|
||||
key: '4251bdeb18574f2cb014ca33d650be52',
|
||||
field: 'danXingWenBen5518',
|
||||
label: '单行文本',
|
||||
type: 'input',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: '',
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
respNewRow: false,
|
||||
placeholder: '请输入单行文本',
|
||||
maxlength: null,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
addonBefore: '',
|
||||
addonAfter: '',
|
||||
disabled: false,
|
||||
allowClear: false,
|
||||
showLabel: true,
|
||||
required: false,
|
||||
rules: [],
|
||||
events: {},
|
||||
isSave: false,
|
||||
isShow: true,
|
||||
scan: false,
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
{
|
||||
key: '3cea59512d464fa3b5a81814999718f3',
|
||||
field: 'danXingWenBen5841',
|
||||
label: '单行文本',
|
||||
type: 'input',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: '',
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
respNewRow: false,
|
||||
placeholder: '请输入单行文本',
|
||||
maxlength: null,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
addonBefore: '',
|
||||
addonAfter: '',
|
||||
disabled: false,
|
||||
allowClear: false,
|
||||
showLabel: true,
|
||||
required: false,
|
||||
rules: [],
|
||||
events: {},
|
||||
isSave: false,
|
||||
isShow: true,
|
||||
scan: false,
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'c4aaaf58ffbc468bb3f6c4f013b0350b',
|
||||
field: 'jiShuZuJian6835',
|
||||
label: '计数组件',
|
||||
type: 'number',
|
||||
component: 'InputNumber',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: 0,
|
||||
componentProps: {
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
maxlength: null,
|
||||
disabled: false,
|
||||
showLabel: true,
|
||||
controls: true,
|
||||
required: false,
|
||||
subTotal: false,
|
||||
isShow: true,
|
||||
rules: [],
|
||||
events: {},
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
],
|
||||
showActionButtonGroup: false,
|
||||
buttonLocation: 'center',
|
||||
actionColOptions: { span: 24 },
|
||||
showResetButton: false,
|
||||
showSubmitButton: false,
|
||||
hiddenComponent: [],
|
||||
};
|
||||
47
src/views/system/Testfrom3/components/workflowPermission.ts
Normal file
47
src/views/system/Testfrom3/components/workflowPermission.ts
Normal file
@ -0,0 +1,47 @@
|
||||
export const permissionList = [
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '单行文本',
|
||||
fieldId: 'danXingWenBen5518',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'input',
|
||||
key: '4251bdeb18574f2cb014ca33d650be52',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '单行文本',
|
||||
fieldId: 'danXingWenBen5841',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'input',
|
||||
key: '3cea59512d464fa3b5a81814999718f3',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '计数组件',
|
||||
fieldId: 'jiShuZuJian6835',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'number',
|
||||
key: 'c4aaaf58ffbc468bb3f6c4f013b0350b',
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
469
src/views/system/Testfrom3/index.vue
Normal file
469
src/views/system/Testfrom3/index.vue
Normal file
@ -0,0 +1,469 @@
|
||||
<template>
|
||||
<PageWrapper dense fixedHeight contentFullHeight contentClass="flex">
|
||||
<BasicTable @register="registerTable" ref="tableRef" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" @row-dbClick="dbClickRow">
|
||||
|
||||
<template #toolbar>
|
||||
<template v-for="button in tableButtonConfig" :key="button.code">
|
||||
<a-button v-if="button.isDefault" :type="button.type" @click="buttonClick(button.code)">
|
||||
<template #icon><Icon :icon="button.icon" /></template>
|
||||
{{ button.name }}
|
||||
</a-button>
|
||||
<a-button v-else :type="button.type">
|
||||
<template #icon><Icon :icon="button.icon" /></template>
|
||||
{{ button.name }}
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<TableAction :actions="getActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<PrintPreview
|
||||
v-if="printData.visible"
|
||||
:id="printData.id"
|
||||
:request-params-configs="printData.requestParamsConfigs"
|
||||
:request-body-configs="printData.requestBodyConfigs"
|
||||
:request-header-configs="printData.requestHeaderConfigs"
|
||||
@close="printData.visible = false"
|
||||
/>
|
||||
<Testfrom3Modal @register="registerModal" @success="handleSuccess" />
|
||||
<ImportModal @register="registerImportModal" importUrl="/system/testfrom3/import" @success="handleImportSuccess"/>
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, createVNode,
|
||||
reactive
|
||||
} from 'vue';
|
||||
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { BasicTable, useTable, TableAction, ActionItem } from '/@/components/Table';
|
||||
import { getTestfrom3Page, deleteTestfrom3, exportTestfrom3} from '/@/api/system/Testfrom3';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { useFormConfig } from '/@/hooks/web/useFormConfig';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getTestfrom3 } from '/@/api/system/Testfrom3';
|
||||
import {generateTableJson } from '/@/utils/event/design';
|
||||
import printJS from 'print-js';
|
||||
|
||||
import { getPrintConfigInfo } from '/@/api/system/generator/print';
|
||||
import PrintPreview from '/@/views/generator/print/Preview.vue';
|
||||
import { PrintButton } from '/@/enums/printEnum';
|
||||
import { InputParamItem } from '/@/components/ApiConfig/src/interface';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import Testfrom3Modal from './components/Testfrom3Modal.vue';
|
||||
import { ImportModal } from '/@/components/Import';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import {formConfig, searchFormSchema, columns } from './components/config';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import useEventBus from '/@/hooks/event/useEventBus';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { bus, CREATE_FLOW, FLOW_PROCESSED, FORM_LIST_MODIFIED } = useEventBus();
|
||||
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
defineEmits(['register']);
|
||||
const { filterColumnAuth, filterButtonAuth } = usePermission();
|
||||
const { mergeColumns,mergeSearchFormSchema,mergeButtons } = useFormConfig();
|
||||
|
||||
const filterColumns = cloneDeep(filterColumnAuth(columns));
|
||||
const customConfigColums =ref(filterColumns);
|
||||
const customSearchFormSchema =ref(searchFormSchema);
|
||||
|
||||
const tableRef = ref();
|
||||
//所有按钮
|
||||
const buttons = ref([{"isUse":true,"name":"新增","code":"add","icon":"ant-design:plus-outlined","isDefault":true,"type":"primary"},{"isUse":true,"name":"编辑","code":"edit","icon":"ant-design:form-outlined","isDefault":true},{"isUse":true,"name":"刷新","code":"refresh","icon":"ant-design:reload-outlined","isDefault":true},{"isUse":true,"name":"查看","code":"view","icon":"ant-design:eye-outlined","isDefault":true},{"isUse":true,"name":"快速导入","code":"import","icon":"ant-design:import-outlined","isDefault":true},{"isUse":true,"name":"快速导出","code":"export","icon":"ant-design:export-outlined","isDefault":true},{"isUse":true,"name":"打印","code":"print","icon":"ant-design:printer-outlined","isDefault":true},{"isUse":true,"name":"模板打印","code":"templateprint","icon":"ant-design:printer-outlined","isDefault":true},{"isUse":true,"name":"删除","code":"delete","icon":"ant-design:delete-outlined","isDefault":true}]);
|
||||
//展示在列表内的按钮
|
||||
const actionButtons = ref<string[]>(['view', 'edit', 'copyData', 'delete', 'startwork','flowRecord']);
|
||||
const buttonConfigs = computed(()=>{
|
||||
return filterButtonAuth(buttons.value);
|
||||
})
|
||||
|
||||
const tableButtonConfig = computed(() => {
|
||||
return buttonConfigs.value?.filter((x) => !actionButtons.value.includes(x.code));
|
||||
});
|
||||
|
||||
const actionButtonConfig = computed(() => {
|
||||
return buttonConfigs.value?.filter((x) => actionButtons.value.includes(x.code));
|
||||
});
|
||||
|
||||
const btnEvent = {add : handleAdd,edit : handleEdit,refresh : handleRefresh,view : handleView,import : handleImport,export : handleExport,print : handlePrint,templateprint : handleTemplateprint,delete : handleDelete,}
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
const router = useRouter();
|
||||
const printMenuId = computed(() => currentRoute.value.meta.menuId as string);
|
||||
|
||||
const formIdComputedRef = ref();
|
||||
formIdComputedRef.value = currentRoute.value.meta.formId
|
||||
const schemaIdComputedRef = ref();
|
||||
schemaIdComputedRef.value = currentRoute.value.meta.schemaId
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedRowsData = ref<any[]>([]);
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
||||
|
||||
// 模板打印 入参参数
|
||||
let printData: {
|
||||
visible:boolean;
|
||||
id: string;
|
||||
code:string;
|
||||
requestParamsConfigs: Array<InputParamItem>;
|
||||
requestHeaderConfigs: Array<InputParamItem>;
|
||||
requestBodyConfigs: Array<InputParamItem>;
|
||||
|
||||
} = reactive({
|
||||
visible:false,
|
||||
id: '',
|
||||
code:'',
|
||||
requestParamsConfigs: [],
|
||||
requestHeaderConfigs: [],
|
||||
requestBodyConfigs: [],
|
||||
});
|
||||
|
||||
const formName='测试_极简模式';
|
||||
const [registerTable, { reload, getRawDataSource, }] = useTable({
|
||||
title: '' || (formName + '列表'),
|
||||
api: getTestfrom3Page,
|
||||
rowKey: 'id',
|
||||
columns: customConfigColums,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: customSearchFormSchema,
|
||||
fieldMapToTime: [],
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, FormId: formIdComputedRef.value, PK: 'id' };
|
||||
},
|
||||
afterFetch: (res) => {
|
||||
tableRef.value.setToolBarWidth();
|
||||
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 160,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
customRow,
|
||||
});
|
||||
|
||||
function dbClickRow(record) {
|
||||
if (!actionButtonConfig?.value.some(element => element.code == 'view')) {
|
||||
return;
|
||||
}
|
||||
const { processId, taskIds, schemaId } = record.workflowData || {};
|
||||
if (taskIds && taskIds.length) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaId + '/' + (processId || '') + '/approveFlow',
|
||||
query: {
|
||||
taskId: taskIds[0],
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
} else if (schemaId && !taskIds && processId) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaId + '/' + processId + '/approveFlow',
|
||||
query: {
|
||||
readonly: 1,
|
||||
taskId: '',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
router.push({
|
||||
path: '/form/Testfrom3/' + record.id + '/viewForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buttonClick(code) {
|
||||
if (code.includes(PrintButton.CODE)) {
|
||||
printData.code = code;
|
||||
}
|
||||
btnEvent[code]();
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
if (schemaIdComputedRef.value) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaIdComputedRef.value + '/0/createFlow'
|
||||
});
|
||||
} else {
|
||||
router.push({
|
||||
path: '/form/Testfrom3/0/createForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
|
||||
router.push({
|
||||
path: '/form/Testfrom3/' + record.id + '/updateForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleDelete(record: Recordable) {
|
||||
deleteList([record.id]);
|
||||
}
|
||||
|
||||
function deleteList(ids) {
|
||||
Modal.confirm({
|
||||
title: '提示信息',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: '是否确认删除?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
deleteTestfrom3(ids).then((_) => {
|
||||
handleSuccess();
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('删除成功!'),
|
||||
});
|
||||
});
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
|
||||
async function handlePrint() {
|
||||
const dataSource = Array.isArray(getRawDataSource())
|
||||
? getRawDataSource()
|
||||
: getRawDataSource().list;
|
||||
const json = generateTableJson(filterColumns, dataSource);
|
||||
const properties = filterColumns.map((item) => item.title);
|
||||
printJS({
|
||||
printable: json,
|
||||
properties: properties,
|
||||
type: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
// 模板打印
|
||||
async function handleTemplateprint() {
|
||||
if (!selectedKeys.value.length) {
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('请选择数据'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (selectedKeys.value.length > 1) {
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('只能选择一条数据进行操作'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
let id = selectedKeys.value[0];
|
||||
try {
|
||||
const record = await getTestfrom3(id);
|
||||
let res = await getPrintConfigInfo(printData.code, printMenuId.value);
|
||||
if(res.enabledMark==null){
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('当前功能未绑定打印模板,请绑定后再进行模板打印。'),
|
||||
});
|
||||
return ;
|
||||
}
|
||||
if(res.enabledMark==0){
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('找不到打印模板,请联系管理员。'),
|
||||
});
|
||||
return ;
|
||||
}
|
||||
printData.id = res.schemaId;
|
||||
if (res.apiConfig) {
|
||||
let json = JSON.parse(res.apiConfig);
|
||||
if (json.requestParamsConfigs && json.requestParamsConfigs.length > 0) {
|
||||
printData.requestParamsConfigs = json.requestParamsConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
if (json.requestHeaderConfigs && json.requestHeaderConfigs.length > 0) {
|
||||
printData.requestHeaderConfigs = json.requestHeaderConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
if (json.requestBodyConfigs && json.requestBodyConfigs.length > 0) {
|
||||
printData.requestBodyConfigs = json.requestBodyConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
printData.visible = true;
|
||||
}else{
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('当前功能未绑定打印模板,请绑定后再进行模板打印。'),
|
||||
});
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
function onSelectChange(selectedRowKeys: [], selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
selectedRowsData.value = selectedRows;
|
||||
}
|
||||
|
||||
function customRow(record: Recordable) {
|
||||
return {
|
||||
onClick: () => {
|
||||
let selectedRowKeys = [...selectedKeys.value];
|
||||
if (selectedRowKeys.indexOf(record.id) >= 0) {
|
||||
let index = selectedRowKeys.indexOf(record.id);
|
||||
selectedRowKeys.splice(index, 1);
|
||||
} else {
|
||||
selectedRowKeys.push(record.id);
|
||||
}
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
},
|
||||
};
|
||||
}
|
||||
function handleRefresh() {
|
||||
reload();
|
||||
}
|
||||
function handleSuccess() {
|
||||
|
||||
selectedKeys.value = [];
|
||||
selectedRowsData.value = [];
|
||||
reload();
|
||||
}
|
||||
|
||||
function handleView(record: Recordable) {
|
||||
|
||||
dbClickRow(record);
|
||||
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
const res = await exportTestfrom3({ isTemplate: false });
|
||||
downloadByData(
|
||||
res.data,
|
||||
'Testfrom3.xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
}
|
||||
|
||||
function handleImport() {
|
||||
openImportModal(true, {
|
||||
title: '快速导入',
|
||||
downLoadUrl:'/system/testfrom3/export',
|
||||
});
|
||||
}
|
||||
function handleImportSuccess(){
|
||||
reload()
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
if (schemaIdComputedRef.value) {
|
||||
bus.on(FLOW_PROCESSED, handleRefresh);
|
||||
bus.on(CREATE_FLOW, handleRefresh);
|
||||
} else {
|
||||
bus.on(FORM_LIST_MODIFIED, handleRefresh);
|
||||
}
|
||||
|
||||
// 合并渲染覆盖配置中的列表配置,包括展示字段配置、搜索字段配置、按钮配置
|
||||
mergeCustomListRenderConfig();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (schemaIdComputedRef.value) {
|
||||
bus.off(FLOW_PROCESSED, handleRefresh);
|
||||
bus.off(CREATE_FLOW, handleRefresh);
|
||||
} else {
|
||||
bus.off(FORM_LIST_MODIFIED, handleRefresh);
|
||||
}
|
||||
});
|
||||
function getActions(record: Recordable):ActionItem[] {
|
||||
|
||||
const actionsList: ActionItem[] = actionButtonConfig.value?.map((button) => {
|
||||
if (!record.workflowData?.processId) {
|
||||
return {
|
||||
icon: button?.icon,
|
||||
tooltip: button?.name,
|
||||
color: button.code === 'delete' ? 'error' : undefined,
|
||||
onClick: btnEvent[button.code].bind(null, record),
|
||||
};
|
||||
} else {
|
||||
if (button.code === 'view') {
|
||||
return {
|
||||
icon: button?.icon,
|
||||
tooltip: button?.name,
|
||||
onClick: btnEvent[button.code].bind(null, record),
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
return actionsList;
|
||||
}
|
||||
async function mergeCustomListRenderConfig(){
|
||||
if (formConfig.useCustomConfig) {
|
||||
let formId=currentRoute.value.meta.formId;
|
||||
//1.合并展示字段配置
|
||||
let cols= await mergeColumns(customConfigColums.value,formId);
|
||||
customConfigColums.value=cols;
|
||||
//2.合并搜索字段配置
|
||||
let sFormSchema= await mergeSearchFormSchema(customSearchFormSchema.value,formId);
|
||||
customSearchFormSchema.value=sFormSchema;
|
||||
//3.合并按钮配置
|
||||
let btns= await mergeButtons(buttons.value,formId);
|
||||
buttons.value=btns;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-table-selection-col) {
|
||||
width: 50px;
|
||||
}
|
||||
.show{
|
||||
display: flex;
|
||||
}
|
||||
.hide{
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
77
src/views/system/dataMigration/components/config.ts
Normal file
77
src/views/system/dataMigration/components/config.ts
Normal file
@ -0,0 +1,77 @@
|
||||
export const items=[
|
||||
/* {
|
||||
code:"租户",
|
||||
name:'租户',
|
||||
ref:"tenantRef",
|
||||
}, */
|
||||
{
|
||||
code:'角色',
|
||||
name:'角色',
|
||||
ref:"roleRef",
|
||||
},
|
||||
{
|
||||
code:'岗位',
|
||||
name:'岗位' ,
|
||||
ref:"postRef",
|
||||
},
|
||||
|
||||
{
|
||||
code:'用户组',
|
||||
name:'用户组',
|
||||
ref:"userGroupRef",
|
||||
},
|
||||
{
|
||||
code:'菜单',
|
||||
name:'菜单',
|
||||
ref:"menuRef",
|
||||
},
|
||||
|
||||
{
|
||||
code:'表单',
|
||||
name:'表单',
|
||||
ref:"formRef",
|
||||
},
|
||||
{
|
||||
code:'流程定义',
|
||||
name:'流程定义',
|
||||
ref:"workflowRef",
|
||||
},
|
||||
{
|
||||
code:'系统配置',
|
||||
name:'系统配置',
|
||||
ref:"systemConfigRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'数据字典',
|
||||
name:'数据字典',
|
||||
ref:"dictionaryRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'自动编码',
|
||||
name:'自动编码',
|
||||
ref:"codeRuleRef",
|
||||
},
|
||||
{
|
||||
code:'桌面设计',
|
||||
name:'桌面设计',
|
||||
ref:"desktopRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'角色-菜单授权',
|
||||
name:'权限:角色-菜单授权(菜单、按钮、列表、表单)',
|
||||
ref:"roleMenuAuthRef",
|
||||
},
|
||||
{
|
||||
code:'角色-自定义接口授权',
|
||||
name:'权限:角色-自定义接口授权',
|
||||
ref:"roleInterfaceAuthRef",
|
||||
},
|
||||
{
|
||||
code:'租户-菜单授权',
|
||||
name:'权限:租户-菜单授权',
|
||||
ref:"tenantMenuAuthRef",
|
||||
}
|
||||
]
|
||||
239
src/views/system/dataMigration/components/export/ExportModal.vue
Normal file
239
src/views/system/dataMigration/components/export/ExportModal.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="data-migration-modal" >
|
||||
<template #title>
|
||||
<a-steps :current="current">
|
||||
<a-step
|
||||
v-for="step in steps"
|
||||
:title="t(step.name)"
|
||||
/>
|
||||
</a-steps>
|
||||
<div class="btn-box">
|
||||
<a-button type="primary" @click="handleStepPrev" :disabled="current == 0">{{t('上一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleStepNext" :disabled="current>=steps.length-1">{{t('下一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleExport" :disabled="current<steps.length-1">{{t('导出')}}</a-button>
|
||||
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="step-container">
|
||||
<Tenant ref="tenantRef" v-show="showIf('租户')"/>
|
||||
<Role ref="roleRef" v-show="showIf('角色')"/>
|
||||
<Post ref="postRef" v-show="showIf('岗位')"/>
|
||||
<UserGroup ref="userGroupRef" v-show="showIf('用户组')"/>
|
||||
<Menu ref="menuRef" v-show="showIf('菜单')"/>
|
||||
<Form ref="formRef" v-show="showIf('表单')"/>
|
||||
<Workflow ref="workflowRef" v-show="showIf('流程定义')"/>
|
||||
<SystemConfig ref="systemConfigRef" v-show="showIf('系统配置')"/>
|
||||
<Dictionary ref="dictionaryRef" v-show="showIf('数据字典')"/>
|
||||
<CodeRule ref="codeRuleRef" v-show="showIf('自动编码')"/>
|
||||
<Desktop ref="desktopRef" v-show="showIf('桌面设计')"/>
|
||||
<!-- <span ref="roleMenuAuthRef" v-show="showIf('角色-菜单授权')" >角色-菜单授权 选择列表</span> -->
|
||||
<RoleMenuAuth ref="roleMenuAuthRef" v-show="showIf('角色-菜单授权')"/>
|
||||
<InterfaceAuth ref="roleInterfaceAuthRef" v-show="showIf('角色-自定义接口授权')"/>
|
||||
<TenantAuthorize ref="tenantMenuAuthRef" v-show="showIf('租户-菜单授权')"/>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref,unref,provide,Ref,onMounted} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import {isFunction } from 'lodash-es';
|
||||
import { isNumber } from '/@/utils/is';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { exportDatas,downloadDatas} from '/@/api/system/dataMigration';
|
||||
import Tenant from './components/Tenant.vue';
|
||||
import Role from './components/Role.vue';
|
||||
import Post from './components/Post.vue';
|
||||
import Menu from './components/Menu.vue';
|
||||
import Form from './components/Form.vue';
|
||||
import Workflow from './components/Workflow.vue';
|
||||
import SystemConfig from './components/SystemConfig.vue';
|
||||
import Dictionary from './components/Dictionary.vue';
|
||||
import CodeRule from './components/CodeRule.vue';
|
||||
import Desktop from './components/Desktop.vue';
|
||||
import UserGroup from './components/UserGroup.vue';
|
||||
import RoleMenuAuth from './components/RoleMenuAuth.vue';
|
||||
import InterfaceAuth from './components/InterfaceAuth.vue';
|
||||
import TenantAuthorize from './components/TenantAuthorize.vue';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
const current = ref(0);
|
||||
const steps=ref([]);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
steps.value=data.items;
|
||||
current.value=0;
|
||||
selectedModel.value={};
|
||||
initStep(0);
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
canFullscreen: false,
|
||||
defaultFullscreen: true,
|
||||
destroyOnClose: true,
|
||||
draggable: false,
|
||||
showOkBtn: false,
|
||||
showCancelBtn: false,
|
||||
footer: null,
|
||||
closable: false,
|
||||
});
|
||||
});
|
||||
|
||||
const tenantRef=ref();
|
||||
const roleRef=ref();
|
||||
const postRef= ref();
|
||||
const userGroupRef= ref();
|
||||
const menuRef = ref();
|
||||
const formRef=ref();
|
||||
const workflowRef=ref();
|
||||
const systemConfigRef= ref();
|
||||
const dictionaryRef= ref();
|
||||
const codeRuleRef= ref();
|
||||
const desktopRef= ref();
|
||||
const roleMenuAuthRef= ref();
|
||||
const roleInterfaceAuthRef= ref();
|
||||
const tenantMenuAuthRef= ref();
|
||||
|
||||
const refs = {
|
||||
tenantRef,
|
||||
roleRef,
|
||||
postRef,
|
||||
userGroupRef,
|
||||
menuRef,
|
||||
formRef,
|
||||
workflowRef,
|
||||
systemConfigRef,
|
||||
dictionaryRef,
|
||||
codeRuleRef,
|
||||
desktopRef,
|
||||
roleMenuAuthRef,
|
||||
roleInterfaceAuthRef,
|
||||
tenantMenuAuthRef
|
||||
};
|
||||
|
||||
const selectedModel: Ref<{
|
||||
menu?:{
|
||||
menuIds: number[],
|
||||
buttonIds: number[],
|
||||
columnIds:number[],
|
||||
formIds:number[]
|
||||
},
|
||||
tenant?:{
|
||||
tenantIds:number[]
|
||||
}
|
||||
}> = ref({});
|
||||
|
||||
provide('selectedModel', selectedModel);
|
||||
|
||||
function handleClose() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function showIf(itemType:string){
|
||||
let currentStep=getStep('current');
|
||||
if(currentStep==null){
|
||||
return false;
|
||||
}
|
||||
return currentStep.code==itemType;
|
||||
}
|
||||
|
||||
//上一步
|
||||
function handleStepPrev() {
|
||||
completeStep(current.value);
|
||||
current.value--;
|
||||
initStep(current.value);
|
||||
|
||||
}
|
||||
//下一步
|
||||
async function handleStepNext() {
|
||||
completeStep(current.value);
|
||||
current.value++;
|
||||
initStep(current.value);
|
||||
}
|
||||
|
||||
//导出
|
||||
async function handleExport(){
|
||||
completeStep(current.value);
|
||||
let result= await exportDatas({
|
||||
exportType:'exportSelected',
|
||||
...unref(selectedModel)
|
||||
});
|
||||
|
||||
if(result){
|
||||
const fileName='data-mg-'+dateUtil(new Date()).format('YYYY-MM-DD_HH_mm_ss');
|
||||
const res = await downloadDatas({uuid:result});
|
||||
downloadByData(
|
||||
res.data,
|
||||
fileName+".zip"
|
||||
);
|
||||
}
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function getStep(key:string|number){
|
||||
let stepList=unref(steps);
|
||||
if(!stepList||stepList.length==0){
|
||||
return null;
|
||||
}
|
||||
let step=null;
|
||||
if(key=='current'){
|
||||
step=unref(stepList[current.value]);
|
||||
}else if(key=='prev'){
|
||||
step=unref(stepList[current.value-1]);
|
||||
}else if(key=='next'){
|
||||
step=unref(stepList[current.value+1]);
|
||||
}else if(isNumber(key)&&key>=0&&key<stepList.length){
|
||||
step=unref(stepList[key]);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
function initStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.initStep&& stepRef.initStep();
|
||||
|
||||
};
|
||||
|
||||
function completeStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.completeStep&& stepRef.completeStep();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.data-migration-modal{
|
||||
.step-container {
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.ant-modal-header{
|
||||
height: 100px;
|
||||
}
|
||||
.ant-steps{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ant-steps-horizontal:not(.ant-steps-label-vertical) .ant-steps-item {
|
||||
padding-left:16px!important;
|
||||
margin-bottom: 10px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.btn-box {
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top:60px;
|
||||
:deep(.ant-btn) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getCodeList } from '/@/api/system/code';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: t('编码编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编码名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('当前流水号'),
|
||||
dataIndex: 'currentNumber',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('编码说明'),
|
||||
dataIndex: 'description',
|
||||
width: 180,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getCodeList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
currentNumber: item.currentNumber,
|
||||
description: item.description
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let codeRule = selectedModel.value.codeRule || (selectedModel.value.codeRule = {});
|
||||
codeRule.codeRuleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.codeRule=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getPageList } from '/@/api/desktop';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getPageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let deskTop = selectedModel.value.deskTop || (selectedModel.value.deskTop = {});
|
||||
deskTop.desktopIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.deskTop=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable
|
||||
@register="registerTableItem"
|
||||
@row-click="handleRowClick"
|
||||
class="w-2/5 xl:w-2/5 mr-2"
|
||||
>
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedItems[record.id]"/>
|
||||
</template> -->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedItems[record.id]"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<BasicTable
|
||||
@register="registerTableDetail"
|
||||
class="w-3/5 xl:w-3/5"
|
||||
:rowSelection="rowSelectionDetail"
|
||||
>
|
||||
</BasicTable>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref,inject,reactive, } from 'vue';
|
||||
import { BasicTable, useTable, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {
|
||||
getDicItemPageList,
|
||||
getDicDetailPageList
|
||||
} from '/@/api/system/dic';
|
||||
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const checkedItems=ref({});
|
||||
const checkedDetails=ref({});
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const searchFormSchemaItem: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('项目名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const itemColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('项目名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchemaDetail: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('字典名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const detailColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('字典名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('值'),
|
||||
dataIndex: 'value',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
align: 'left',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
// Row selection for detail table
|
||||
const selectedRowKeysDetail = ref<string[]>([]);
|
||||
const rowSelectionDetail = reactive<any> ({
|
||||
selectedRowKeys: selectedRowKeysDetail,
|
||||
onChange: (selectedRowKeys: string[]) => {
|
||||
selectedRowKeysDetail.value = selectedRowKeys;
|
||||
syncCheckedDetails();
|
||||
},
|
||||
onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {
|
||||
const ids = (changeRows || selectedRows || []).map((r: any) => String(r.id));
|
||||
if (selected) {
|
||||
selectedRowKeysDetail.value = Array.from(new Set([...selectedRowKeysDetail.value, ...ids]));
|
||||
} else {
|
||||
selectedRowKeysDetail.value = selectedRowKeysDetail.value.filter((k) => !ids.includes(k));
|
||||
}
|
||||
syncCheckedDetails();
|
||||
},
|
||||
});
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const selectItemId = ref('');
|
||||
|
||||
const [registerTableItem, { reload: itemReload }] = useTable({
|
||||
title: t('数据字典项'),
|
||||
api: getDicItemPageList,
|
||||
rowKey: 'id',
|
||||
columns: itemColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaItem,
|
||||
showResetButton: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTableDetail, { reload: detailReload }] = useTable({
|
||||
title: t('数据字典详情'),
|
||||
api: getDicDetailPageList,
|
||||
rowKey: 'id',
|
||||
columns: detailColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaDetail,
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, itemId: selectItemId.value };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
selectItemId.value = record.id;
|
||||
detailReload();
|
||||
}
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
selectItemId.value = rowKey[0];
|
||||
if(selectItemId.value){
|
||||
detailReload();
|
||||
}
|
||||
}
|
||||
|
||||
function syncCheckedDetails() {
|
||||
const map: Record<string, boolean> = {};
|
||||
selectedRowKeysDetail.value.forEach((key) => {
|
||||
map[key] = true;
|
||||
});
|
||||
checkedDetails.value = map;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let items=checkedItems.value;
|
||||
let details=checkedDetails.value;
|
||||
let checkItemIds = Object.keys(items).filter(key => items[key] === true);
|
||||
let checkDetailIds = Object.keys(details).filter(key => details[key] === true);
|
||||
|
||||
if((checkItemIds&&checkItemIds.length>0)||(checkDetailIds&&checkDetailIds.length>0)){
|
||||
let dictionary = selectedModel.value.dictionary || (selectedModel.value.dictionary = {});
|
||||
dictionary.itemIds=checkItemIds;
|
||||
dictionary.detailIds=checkDetailIds;
|
||||
}else{
|
||||
selectedModel.value.dictionary=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getFormTemplatePage} from '/@/api/form/design';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '表单id',
|
||||
key:'id',
|
||||
dataIndex:'id',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '表单名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '模板类型',
|
||||
dataIndex: '',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.formDesignType===0) {
|
||||
return '数据优先模板';
|
||||
}else if(record.formDesignType===1){
|
||||
return '界面优先模板';
|
||||
}else if(record.formDesignType===2){
|
||||
return '简易模板';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getFormTemplatePage({limit:1,size:99,type:0,category:''});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
formDesignType: item.formDesignType
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let formTemplate = selectedModel.value.formTemplate || (selectedModel.value.formTemplate = {});
|
||||
formTemplate.formTemplateIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.formTemplate=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable
|
||||
@register="registerTableRole"
|
||||
@row-click="handleRowClick"
|
||||
class="w-2/5 xl:w-2/5 mr-2"
|
||||
>
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedRoles[record.id]"/>
|
||||
</template> -->
|
||||
</BasicTable>
|
||||
<BasicTable @register="registerInterface" class="w-3/5 xl:w-3/5" ref="interfaceRef">
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedInterfaces[record.roleId+'_'+record.id]"/>
|
||||
</template> -->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedInterfaces[record.roleId+'_'+record.id]"
|
||||
@change="onParentCheckChange(record, $event)"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject,nextTick,unref } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {getRolePageList,getRoleInterfaceList} from '/@/api/system/role';
|
||||
import {getAllInterface } from '/@/api/system/interface';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const checkedRoles=ref({});
|
||||
const checkedInterfaces=ref({});
|
||||
const allInterfacesTree=ref([]);
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const searchFormSchemaRole: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('角色名称'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('角色编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const roleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const searchFormSchemaInterface: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('名称'),
|
||||
component: 'Input',
|
||||
}
|
||||
];
|
||||
|
||||
const interfaceColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('接口名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const selectRoleId = ref('');
|
||||
|
||||
const [registerTableRole, { reload: roleReload }] = useTable({
|
||||
title: t('角色'),
|
||||
api: getRolePageList,
|
||||
rowKey: 'id',
|
||||
columns: roleColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaRole,
|
||||
showResetButton: false,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange: onSelectChange,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerInterface, {reload: interfaceReload, expandAll, collapseAll }] = useTable({
|
||||
title: '自定义接口',
|
||||
isTreeTable: true,
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, roleId: selectRoleId.value };
|
||||
},
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaInterface,
|
||||
showResetButton: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
columns:interfaceColumns,
|
||||
api:getAuthInterface,
|
||||
rowKey: 'id',
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
if(selectRoleId.value===record.id){
|
||||
return;
|
||||
}
|
||||
selectRoleId.value = record.id;
|
||||
interfaceReload();
|
||||
}
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
if(selectRoleId.value== rowKey[0]){
|
||||
return;
|
||||
}
|
||||
|
||||
if(rowKey[0]){
|
||||
selectRoleId.value =rowKey[0] ;
|
||||
interfaceReload();
|
||||
}
|
||||
}
|
||||
|
||||
function setChildrenChecked(record, checked) {
|
||||
if (record.children && record.children.length > 0) {
|
||||
record.children.forEach(child => {
|
||||
checkedInterfaces.value[child.roleId + '_' + child.id] = checked;
|
||||
setChildrenChecked(child, checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onParentCheckChange(record, e) {
|
||||
const checked = e.target.checked;
|
||||
setChildrenChecked(record, checked);
|
||||
}
|
||||
|
||||
function getAuthTree(authTree,allTree,authList,roleId) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthTree(children, o.children, authList,roleId);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
node.roleId=roleId;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function getAuthInterface(params, mode){
|
||||
if(!params.roleId){
|
||||
return [];
|
||||
}
|
||||
let allTree=allInterfacesTree.value;
|
||||
if(!allTree||allTree.length==0){
|
||||
allTree=allInterfacesTree.value=await getAllInterface();
|
||||
}
|
||||
|
||||
let authList=await getRoleInterfaceList(params,mode);
|
||||
|
||||
let authTree=[];
|
||||
getAuthTree(authTree,allTree,authList,params.roleId);
|
||||
|
||||
expandAll();
|
||||
|
||||
return authTree;
|
||||
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let interfaces=checkedInterfaces.value;
|
||||
let checkIds = Object.keys(interfaces).filter(key => interfaces[key] === true);
|
||||
if(checkIds&&checkIds.length>0){
|
||||
let map={};
|
||||
for(let i=0;i<checkIds.length;i++){
|
||||
let splits=checkIds[i].split('_');
|
||||
let roleId=splits[0];
|
||||
let interfaceId=splits[1];
|
||||
|
||||
let interfaceList=map[roleId]||(map[roleId]=[]);
|
||||
interfaceList.push(interfaceId);
|
||||
}
|
||||
let interfaceAuth = selectedModel.value.interfaceAuth || (selectedModel.value.interfaceAuth = {});
|
||||
interfaceAuth.interfaceAuthIdsMap=map;
|
||||
}else{
|
||||
selectedModel.value.interfaceAuth=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<a-row :gutter="[16, 16]" class="h-full">
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('菜单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="menuData"
|
||||
checkable
|
||||
ref="menuRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('按钮')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="buttonSelectData"
|
||||
checkable
|
||||
ref="ButtonRef"
|
||||
@check="handleButtonSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('字段')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="columnSelectData"
|
||||
checkable
|
||||
ref="ColumnRef"
|
||||
@check="handleColumnSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('表单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="fieldSelectData"
|
||||
checkable
|
||||
ref="FieldRef"
|
||||
@check="handleFieldSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject} from 'vue';
|
||||
import { TreeItem,BasicTree,TreeActionType} from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { getMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { getMenuButtonList, getMenuColumnList, getMenuFieldList } from '/@/api/system/menuButton';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const menuRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ButtonRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ColumnRef = ref<Nullable<TreeActionType>>(null);
|
||||
const FieldRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
const menuData = ref<TreeItem[]>([]);
|
||||
const buttonData = ref<TreeItem[]>([]);
|
||||
const columnData = ref<TreeItem[]>([]);
|
||||
const fieldData = ref<TreeItem[]>([]);
|
||||
|
||||
const buttonSelectData = ref<TreeItem[]>([]);
|
||||
const columnSelectData = ref<TreeItem[]>([]);
|
||||
const fieldSelectData = ref<TreeItem[]>([]);
|
||||
|
||||
const menuKeys = ref<string[]>([]);
|
||||
const btnKeys = ref<string[]>([]);
|
||||
const colKeys = ref<string[]>([]);
|
||||
const fieldKeys = ref<string[]>([]);
|
||||
|
||||
const btnFilterKeys = ref<string[]>([]);
|
||||
const colFilterKeys = ref<string[]>([]);
|
||||
const fieldFilterKeys = ref<string[]>([]);
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
menuData.value = await getMenuSimpleTree();
|
||||
buttonData.value = await getMenuButtonList();
|
||||
columnData.value = await getMenuColumnList();
|
||||
fieldData.value = await getMenuFieldList();
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTreeSelect(keys, e) {
|
||||
//keys 所有全选中的key
|
||||
//e.halfCheckedKeys 所有半选中的
|
||||
|
||||
//如果没有半选 就证明全部全选 直接使用keys
|
||||
//如果有半选需要 使用keys + e.halfCheckedKeys
|
||||
|
||||
const menuSelect: string[] = [...e.halfCheckedKeys, ...keys] as String[];
|
||||
//选择菜单的时候。对应后面的button column field 只进行增量添加
|
||||
incrementTree(menuSelect);
|
||||
|
||||
menuKeys.value = menuSelect;
|
||||
}
|
||||
|
||||
|
||||
function incrementTree(addMenuSelect) {
|
||||
/* btnFilterKeys.value = [];
|
||||
colFilterKeys.value = [];
|
||||
fieldFilterKeys.value = []; */
|
||||
|
||||
// 确保未选中菜单的子节点不会丢失
|
||||
findMenuTree(buttonSelectData.value, menuData.value, Array.from(addMenuSelect), 'button');
|
||||
findMenuTree(columnSelectData.value, menuData.value, Array.from(addMenuSelect), 'column');
|
||||
findMenuTree(fieldSelectData.value, menuData.value, Array.from(addMenuSelect), 'field');
|
||||
|
||||
// 更新按钮、字段、表单的选中状态
|
||||
btnKeys.value = Array.from(new Set([...btnKeys.value, ...addMenuSelect]));
|
||||
colKeys.value = Array.from(new Set([...colKeys.value, ...addMenuSelect]));
|
||||
fieldKeys.value = Array.from(new Set([...fieldKeys.value, ...addMenuSelect]));
|
||||
|
||||
// 扩展树的展开状态
|
||||
unref(ButtonRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
unref(ColumnRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
unref(FieldRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
}
|
||||
|
||||
function findMenuTree(menuSelectTree, treeData, keys, type) {
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
let o = cloneDeep(treeData[i]);
|
||||
if (keys.includes(o.id)) {
|
||||
let hadData = false;
|
||||
let index = 0;
|
||||
for (let j = 0; j < menuSelectTree.length; j++) {
|
||||
if (menuSelectTree[j].id == o.id) {
|
||||
hadData = true;
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hadData) {
|
||||
o.children = [];
|
||||
menuSelectTree.push(o);
|
||||
} else {
|
||||
o = menuSelectTree[index];
|
||||
}
|
||||
if (treeData[i].children?.length > 0) {
|
||||
findMenuTree(o.children, treeData[i].children, keys, type);
|
||||
} else {
|
||||
let list =
|
||||
type == 'button'
|
||||
? buttonData.value
|
||||
: type == 'column'
|
||||
? columnData.value
|
||||
: fieldData.value;
|
||||
o.children = getAuthData(list, o.id, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthData(list, id, type) {
|
||||
let arr: TreeItem[] =list.filter((o) => {
|
||||
return o.menuId == id;
|
||||
});
|
||||
arr.forEach((o) => {
|
||||
o.parentId = o.menuId;
|
||||
if (type == 'button') {
|
||||
btnFilterKeys.value.push(o.id);
|
||||
} else if (type == 'column') {
|
||||
colFilterKeys.value.push(o.id);
|
||||
} else {
|
||||
if (o.children && o.children.length > 0) {
|
||||
o.children?.forEach((k) => {
|
||||
fieldFilterKeys.value.push(k.id);
|
||||
});
|
||||
}
|
||||
fieldFilterKeys.value.push(o.id);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function handleButtonSelect(keys, e) {
|
||||
btnKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleColumnSelect(keys, e) {
|
||||
colKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleFieldSelect(keys, e) {
|
||||
fieldKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let menuIds=menuKeys.value;
|
||||
let buttonIds=btnKeys.value.filter((o) =>{ return btnFilterKeys.value.includes(o)});
|
||||
let columnIds=colKeys.value.filter((o) => {return colFilterKeys.value.includes(o)});
|
||||
let formIds=fieldKeys.value.filter((o) => {return fieldFilterKeys.value.includes(o)});
|
||||
if((menuIds&&menuIds.length>0)||(buttonIds&&buttonIds.length>0)
|
||||
||(columnIds&&columnIds.length>0)||(formIds&&formIds.length>0)){
|
||||
let menu = selectedModel.value.menu || (selectedModel.value.menu = {});
|
||||
menu.menuIds=menuIds;
|
||||
menu.buttonIds=buttonIds;
|
||||
menu.columnIds=columnIds;
|
||||
menu.formIds=formIds;
|
||||
}else{
|
||||
selectedModel.value.menu=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less">
|
||||
.autherizeDialog {
|
||||
.ant-modal {
|
||||
top: 20px;
|
||||
height: calc(100% - 40px) !important;
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-modal-body {
|
||||
height: calc(100% - 120px) !important;
|
||||
|
||||
.scrollbar__view {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
max-height: none !important;
|
||||
|
||||
.ant-col {
|
||||
height: 100%;
|
||||
|
||||
.treebox {
|
||||
height: calc(100% - 42px);
|
||||
overflow: auto;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-right: solid 1px #ccc;
|
||||
|
||||
.vben-tree {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<div class="w-1/3 xl:w-1/4">
|
||||
<DeptTree @select="handleSelect" />
|
||||
</div>
|
||||
<div class="w-2/3 xl:w-3/4">
|
||||
<BasicTable @register="registerTable">
|
||||
</BasicTable>
|
||||
</div>
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, h, ref, inject, reactive, watch } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {getTreePostList} from '/@/api/system/post';
|
||||
import { SelectDepartment } from '/@/components/SelectOrganizational/index';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
|
||||
import DeptTree from '/@/views/system/user/components/DeptTree.vue';
|
||||
import { UserInfo } from '/#/store';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
const checkedPosts=ref<Record<string, boolean>>({});
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('岗位名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('岗位编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('状态'),
|
||||
dataIndex: 'enabledMark',
|
||||
width: 80,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
customRender: ({ record }) => {
|
||||
const status = record.enabledMark;
|
||||
const enable = ~~status === 1;
|
||||
const color = enable ? 'green' : 'red';
|
||||
const text = enable ? t('启用') : t('停用');
|
||||
return h(Tag, { color: color }, () => text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('岗位名称'),
|
||||
component: 'Input',
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('岗位编码'),
|
||||
component: 'Input',
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: t('状态'),
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: t('启用'), value: 1 },
|
||||
{ label: t('停用'), value: 0 },
|
||||
],
|
||||
},
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const selectDeptId = ref('');
|
||||
const postId = ref('');
|
||||
const selectedUserIds = ref<string[]>([]);
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
// --- 新增:用于树形表格多选(父选中 -> 子也选中) ---
|
||||
const selectedRowKeys = ref<string[]>([]);
|
||||
const rowSelection = reactive<any>({
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: selectedRowKeys.value,
|
||||
// onChange 来自 ant table,会在任何选择变化时触发
|
||||
onChange: (keys: (string|number)[]) => {
|
||||
selectedRowKeys.value = (keys || []).map(k => String(k));
|
||||
syncCheckedPosts();
|
||||
},
|
||||
// onSelect 单条选择(可以用于父级级联选中子级)
|
||||
onSelect: (record: any, selected: boolean) => {
|
||||
const data = getDataSource ? getDataSource() : [];
|
||||
const node = findNode(data, String(record.id));
|
||||
const ids = node ? collectIds(node) : [String(record.id)];
|
||||
if (selected) {
|
||||
selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...ids]));
|
||||
} else {
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter(k => !ids.includes(k));
|
||||
}
|
||||
// 把 reactive 对象的 selectedRowKeys 同步(ant table 读取该字段)
|
||||
rowSelection.selectedRowKeys = selectedRowKeys.value;
|
||||
syncCheckedPosts();
|
||||
},
|
||||
// 全选/取消全选
|
||||
onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {
|
||||
// changeRows 通常是当前页被切换选中状态的行,使用它来只影响当前页
|
||||
const pageIds = (changeRows || selectedRows || []).map((r: any) => String(r.id));
|
||||
if (selected) {
|
||||
// 仅将当前页的 id 加入已选集合(保留跨页已选)
|
||||
selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...pageIds]));
|
||||
} else {
|
||||
// 仅移除当前页的 id,不清空其它页的已选
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter((k) => !pageIds.includes(k));
|
||||
}
|
||||
rowSelection.selectedRowKeys = selectedRowKeys.value;
|
||||
syncCheckedPosts();
|
||||
},
|
||||
});
|
||||
|
||||
// helper: 在 tree data 中查找节点
|
||||
function findNode(list: any[], id: string): any | null {
|
||||
for (const n of list || []) {
|
||||
if (String(n.id) === id) return n;
|
||||
const found = findNode(n.children || [], id);
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// helper: 收集节点以及所有子孙 id(字符串形式)
|
||||
function collectIds(node: any, out: string[] = []) {
|
||||
out.push(String(node.id));
|
||||
(node.children || []).forEach((c: any) => collectIds(c, out));
|
||||
return out;
|
||||
}
|
||||
// helper: 返回所有节点 id(平展)
|
||||
function flattenAllIds(list: any[], out: string[] = []) {
|
||||
for (const n of list || []) {
|
||||
out.push(String(n.id));
|
||||
if (n.children && n.children.length) flattenAllIds(n.children, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function syncCheckedPosts() {
|
||||
const map: Record<string, boolean> = {};
|
||||
(selectedRowKeys.value || []).forEach(k => (map[String(k)] = true));
|
||||
checkedPosts.value = map;
|
||||
}
|
||||
|
||||
// --- 使用 useTable 时将 rowSelection 传入(支持父选中级联子级) ---
|
||||
const [registerTable, { reload, getDataSource }] = useTable({
|
||||
title: t('岗位列表'),
|
||||
api: getTreePostList,
|
||||
columns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
actionColOptions: { span: 16 },
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
//发送请求默认新增 左边树结构所选机构id
|
||||
return { ...params, deptId: selectDeptId.value };
|
||||
},
|
||||
rowKey: 'id',
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
},
|
||||
striped: false,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
showIndexColumn: false,
|
||||
defaultExpandAllRows: true,
|
||||
// 把我们准备好的 rowSelection 对象传入
|
||||
rowSelection,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleSelect(deptId = '') {
|
||||
selectDeptId.value = deptId;
|
||||
reload({ searchInfo: { deptId: deptId } });
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let posts=checkedPosts.value;
|
||||
let checkPostIds = Object.keys(posts).filter(key => posts[key] === true);
|
||||
|
||||
if(checkPostIds&&checkPostIds.length>0){
|
||||
let post = selectedModel.value.post || (selectedModel.value.post = {});
|
||||
post.postIds=checkPostIds;
|
||||
}else{
|
||||
selectedModel.value.post=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getRolePageList} from '/@/api/system/role';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getRolePageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let role = selectedModel.value.role || (selectedModel.value.role = {});
|
||||
role.roleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.role=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,423 @@
|
||||
<template>
|
||||
<div class="role-menu-auth">
|
||||
<a-row :gutter="[16, 16]" class="h-full">
|
||||
<a-col :span="8" >
|
||||
<BasicTable
|
||||
@register="registerTableRole"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('菜单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="menuAuthData"
|
||||
checkable
|
||||
ref="menuRef"
|
||||
@check="handleMenuSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('按钮')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="buttonAuthData"
|
||||
checkable
|
||||
ref="buttonRef"
|
||||
@check="handleButtonSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('字段')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="columnAuthData"
|
||||
checkable
|
||||
ref="columnRef"
|
||||
@check="handleColumnSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('表单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="fieldAuthData"
|
||||
checkable
|
||||
ref="fieldRef"
|
||||
@check="handleFieldSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject,nextTick} from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import { TreeItem,BasicTree,TreeActionType} from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {getRolePageList,getRoleAuth} from '/@/api/system/role';
|
||||
import { getMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { getMenuButtonList, getMenuColumnList, getMenuFieldList } from '/@/api/system/menuButton';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const menuRef = ref<Nullable<TreeActionType>>(null);
|
||||
const buttonRef = ref<Nullable<TreeActionType>>(null);
|
||||
const columnRef = ref<Nullable<TreeActionType>>(null);
|
||||
const fieldRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
const selectRoleId = ref('');
|
||||
|
||||
let menuData =[];
|
||||
let buttonData =[];
|
||||
let columnData = [];
|
||||
let fieldData =[];
|
||||
|
||||
const menuAuthData = ref<TreeItem[]>([]);
|
||||
const buttonAuthData = ref<TreeItem[]>([]);
|
||||
const columnAuthData = ref<TreeItem[]>([]);
|
||||
const fieldAuthData = ref<TreeItem[]>([]);
|
||||
|
||||
let selectedMenuData={};
|
||||
let selectedButtonData={};
|
||||
let selectedColumnData={};
|
||||
let selectedFieldData={};
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const roleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
menuData = await getMenuSimpleTree();
|
||||
buttonData = await getMenuButtonList();
|
||||
columnData = await getMenuColumnList();
|
||||
fieldData = await getMenuFieldList();
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const [registerTableRole, { reload: roleReload }] = useTable({
|
||||
title: t('角色'),
|
||||
api: getRolePageList,
|
||||
rowKey: 'id',
|
||||
columns: roleColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: [],
|
||||
showResetButton: false,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange: onSelectChange,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
pagination: { pageSize: 100 },
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
if(selectRoleId.value===record.id){
|
||||
return;
|
||||
}
|
||||
selectRoleId.value = record.id;
|
||||
reloadRoleAuth();
|
||||
}
|
||||
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
if(selectRoleId.value== rowKey[0]){
|
||||
return;
|
||||
}
|
||||
|
||||
if(rowKey[0]){
|
||||
selectRoleId.value =rowKey[0] ;
|
||||
reloadRoleAuth();
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthMenuTree(authTree,allTree,authList) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthMenuTree(children, o.children, authList);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
node.roleId=selectRoleId.value;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
}
|
||||
|
||||
setRoleObjectId(authTree);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAuthObjectTree(menuTree,authList,type) {
|
||||
let cloneMenuTree=cloneDeep(menuTree);
|
||||
for(let i = 0; i < authList.length; i++){
|
||||
let object=authList[i];
|
||||
object.type=type;
|
||||
let result=hangToMenuTree(object,cloneMenuTree,type);
|
||||
if(result!==true){
|
||||
cloneMenuTree.push(object);
|
||||
}
|
||||
}
|
||||
|
||||
let objectTree=[];
|
||||
fillToObjectTree(objectTree,cloneMenuTree,type);
|
||||
setRoleObjectId(objectTree);
|
||||
return objectTree;
|
||||
}
|
||||
|
||||
function hangToMenuTree(object,menuTree,type) {
|
||||
for(let i = 0; i< menuTree.length; i++){
|
||||
let menu=menuTree[i];
|
||||
if(menu.type&&menu.type===type){
|
||||
continue;
|
||||
}
|
||||
let flag=false;
|
||||
if(menu.id==object.menuId){
|
||||
let children = menu.children || (menu.children = []);
|
||||
children.push(object);
|
||||
flag=true;
|
||||
}else{
|
||||
if (menu.children?.length > 0) {
|
||||
flag=hangToMenuTree(object,menu.children,type);
|
||||
}
|
||||
}
|
||||
if(flag===true){
|
||||
menu.hasObjects=true;
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fillToObjectTree(objectTree,menuTree,type){
|
||||
for(let i = 0; i< menuTree.length; i++){
|
||||
let menu=menuTree[i];
|
||||
if(menu.type===type){
|
||||
objectTree.push(menu);
|
||||
}else if(menu.hasObjects===true){
|
||||
let children=[]
|
||||
if (menu.children?.length > 0) {
|
||||
fillToObjectTree(children,menu.children,type);
|
||||
}
|
||||
menu.children=children;
|
||||
objectTree.push(menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setRoleObjectId(objectTree){
|
||||
for(let i = 0; i< objectTree.length; i++){
|
||||
let object=objectTree[i];
|
||||
object.roleObjectId=selectRoleId.value+"_"+object.id;
|
||||
if (object.children?.length > 0) {
|
||||
setRoleObjectId(object.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadRoleAuth(){
|
||||
let roleId=selectRoleId.value;
|
||||
if(!roleId){
|
||||
return [];
|
||||
}
|
||||
const authList = await getRoleAuth(roleId);
|
||||
let authMenuIds= authList.menuIds || [];
|
||||
let authButtonIds= authList.buttonIds || [];
|
||||
let authColumnIds= authList.columnIds || [];
|
||||
let authFieldIds= authList.formIds || [];
|
||||
|
||||
let menuAuthTree=[];
|
||||
getAuthMenuTree(menuAuthTree,menuData,authMenuIds);
|
||||
menuAuthData.value=menuAuthTree;
|
||||
|
||||
let authButtonData=buttonData.filter((item)=>authButtonIds.includes(item.id));
|
||||
let buttonAuthTree=getAuthObjectTree(menuData,authButtonData,'button');
|
||||
buttonAuthData.value=buttonAuthTree;
|
||||
|
||||
let authColumnData=columnData.filter((item)=>authColumnIds.includes(item.id));
|
||||
let columnAuthTree=getAuthObjectTree(menuData,authColumnData,'column');
|
||||
columnAuthData.value=columnAuthTree;
|
||||
|
||||
let authFieldData=fieldData.filter((item)=>authFieldIds.includes(item.id));
|
||||
let fieldAuthTree=getAuthObjectTree(menuData,authFieldData,'field');
|
||||
fieldAuthData.value=fieldAuthTree;
|
||||
|
||||
nextTick(() => {
|
||||
unref(menuRef).setCheckedKeys(selectedMenuData[selectRoleId.value]);
|
||||
unref(buttonRef).setCheckedKeys(selectedButtonData[selectRoleId.value]);
|
||||
unref(columnRef).setCheckedKeys(selectedColumnData[selectRoleId.value]);
|
||||
unref(fieldRef).setCheckedKeys(selectedFieldData[selectRoleId.value]);
|
||||
unref(menuRef)?.expandAll(true);
|
||||
unref(buttonRef)?.expandAll(true);
|
||||
unref(columnRef)?.expandAll(true);
|
||||
unref(fieldRef)?.expandAll(true);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMenuSelect(keys, e){
|
||||
selectedMenuData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function handleButtonSelect(keys, e){
|
||||
selectedButtonData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function handleColumnSelect(keys, e){
|
||||
selectedColumnData[selectRoleId.value]=keys;
|
||||
}
|
||||
function handleFieldSelect(keys, e){
|
||||
selectedFieldData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let map={};
|
||||
for(let key of Object.keys(selectedMenuData)){
|
||||
let list=selectedMenuData[key];
|
||||
if(list&&list.length>0){
|
||||
let menuIdList=list.map((item)=>{ return item.split("_")[1];});
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...menuIdList);
|
||||
}
|
||||
}
|
||||
|
||||
for(let key of Object.keys(selectedButtonData)){
|
||||
let list=selectedButtonData[key];
|
||||
if(list&&list.length>0){
|
||||
let allbuttonList=buttonData.map((item)=>{return item.id});
|
||||
let buttonList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allbuttonList.includes(item);});
|
||||
if(buttonList&&buttonList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...buttonList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(let key of Object.keys(selectedColumnData)){
|
||||
let list=selectedColumnData[key];
|
||||
if(list&&list.length>0){
|
||||
let allColumnList=columnData.map((item)=>{return item.id});
|
||||
let columnList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allColumnList.includes(item);});
|
||||
if(columnList&&columnList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...columnList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(let key of Object.keys(selectedFieldData)){
|
||||
let list=selectedFieldData[key];
|
||||
if(list&&list.length>0){
|
||||
let allFieldList=fieldData.map((item)=>{return item.id});
|
||||
let fieldList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allFieldList.includes(item);});
|
||||
if(fieldList&&fieldList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...fieldList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Object.keys(map).length>0){
|
||||
let authorize = selectedModel.value.authorize || (selectedModel.value.authorize = {});
|
||||
authorize.authorizeMap=map;
|
||||
}else{
|
||||
selectedModel.value.authorize=null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-table-title){
|
||||
display:none!important;
|
||||
}
|
||||
|
||||
.treebox {
|
||||
height: calc(100% - 42px);
|
||||
overflow: auto;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-right: solid 1px #ccc;
|
||||
|
||||
.vben-tree {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getXjrSystemConfigPage} from '/@/api/system/systemConfig';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
key:'value',
|
||||
dataIndex:'value',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getXjrSystemConfigPage({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
value: item.value
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let systemConfig = selectedModel.value.systemConfig || (selectedModel.value.systemConfig = {});
|
||||
systemConfig.systemConfigIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.systemConfig=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getTenantPageList} from '/@/api/system/tenant';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '租户名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '租户编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getTenantPageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let tenant = selectedModel.value.tenant || (selectedModel.value.tenant = {});
|
||||
tenant.tenantIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenant=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div style="margin-left: 30%">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">
|
||||
{{ t('已授权菜单') }}
|
||||
</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="treeData"
|
||||
checkable
|
||||
ref="treeRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref,inject,onMounted,nextTick } from 'vue';
|
||||
import { getTenantMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { TreeItem, BasicTree, TreeActionType } from '/@/components/Tree';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getAuthorize } from '/@/api/system/tenant';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
|
||||
|
||||
const emits = defineEmits(['success', 'register']);
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const treeRef = ref<Nullable<TreeActionType>>(null);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
function getTree(tree) {
|
||||
return tree || null;
|
||||
}
|
||||
|
||||
function handleTreeSelect(keys, e) {
|
||||
selectedKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function getAuthTree(authTree,allTree,authList) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthTree(children, o.children, authList);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
|
||||
let allTree = await getTenantMenuSimpleTree();
|
||||
const authList = await getAuthorize(userInfo.value.tenantId);
|
||||
|
||||
let authTree=[];
|
||||
getAuthTree(authTree,allTree,authList);
|
||||
|
||||
treeData.value=authTree;
|
||||
|
||||
nextTick(() => {
|
||||
getTree(unref(treeRef)).expandAll(true);
|
||||
});
|
||||
|
||||
inited.value=true;
|
||||
}catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function builldSelectedTree(tree,selecteds,arr){
|
||||
if((!tree||tree.length==0)||(!selecteds||selecteds.length==0)){
|
||||
return;
|
||||
}
|
||||
|
||||
for(let i=0;i<tree.length;i++){
|
||||
let treeNode=tree[i];
|
||||
if(selecteds.includes(treeNode.id)){
|
||||
arr.push(treeNode);
|
||||
|
||||
let children=treeNode.children;
|
||||
let childArr=[];
|
||||
builldSelectedTree(children,selecteds,childArr);
|
||||
treeNode.children=childArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let cloneTreeData= cloneDeep(treeData.value);
|
||||
let arr=[];
|
||||
builldSelectedTree(cloneTreeData,selectedKeys.value,arr);
|
||||
if(arr.length>0){
|
||||
let tenantAuthorize = selectedModel.value.tenantAuthorize || (selectedModel.value.tenantAuthorize = {});
|
||||
tenantAuthorize.menuTree=arr;
|
||||
tenantAuthorize.menuIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenantAuthorize=null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getXjrGroupPage} from '/@/api/system/group';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '用户组名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '用户组编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getXjrGroupPage({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let userGroup = selectedModel.value.userGroup || (selectedModel.value.userGroup = {});
|
||||
userGroup.userGroupIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.userGroup=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getDesignPage } from '/@/api/workflow/design';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '流程编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '流程名称',
|
||||
key:'name',
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getDesignPage({limit:1,size:99,isAuth:false});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let workflowSchema = selectedModel.value.workflowSchema || (selectedModel.value.workflowSchema = {});
|
||||
workflowSchema.workflowSchemaIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.workflowSchema=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="export">
|
||||
<div class="l-export" ><span @click.stop="openConfirmDialog">导出所选项目{{configType}}</span></div>
|
||||
<div class="l-export" >
|
||||
<span @click.stop="beginExport('exportSelected')" class="action-btn">选择性导出</span>
|
||||
<!-- <span @click.stop="beginExport('exportAll')" class="action-btn">导出全部</span> -->
|
||||
</div>
|
||||
<div class="list-wrapper">
|
||||
<a-list size="small" bordered :data-source="state.options">
|
||||
<template #renderItem="{ item }">
|
||||
@ -18,155 +21,109 @@
|
||||
</a-list>
|
||||
</div>
|
||||
</div>
|
||||
<ExportModal
|
||||
v-if="state.isShowExportModal"
|
||||
@register="registerExportModal"
|
||||
@close="handleClose()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref,watch,reactive,h} from 'vue';
|
||||
import { ref,unref,watch,reactive,h} from 'vue';
|
||||
import {useMessage} from "/@/hooks/web/useMessage";
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { exportDatas,downloadDatas} from '/@/api/system/dataMigration';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import ExportModal from './ExportModal.vue';
|
||||
import { items } from '../config.ts';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
const state = reactive({
|
||||
indeterminate: false,
|
||||
checkAll: false,
|
||||
options:[
|
||||
{
|
||||
checked:false,
|
||||
code:"租户",
|
||||
name:'租户'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'角色',
|
||||
name:'角色'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'岗位',
|
||||
name:'岗位'
|
||||
},
|
||||
|
||||
{
|
||||
checked:false,
|
||||
code:'用户组',
|
||||
name:'用户组'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'菜单',
|
||||
name:'菜单'
|
||||
},
|
||||
|
||||
{
|
||||
checked:false,
|
||||
code:'表单',
|
||||
name:'表单'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'流程定义',
|
||||
name:'流程定义'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'系统变量',
|
||||
name:'系统变量'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'数据字典',
|
||||
name:'数据字典'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'桌面配置',
|
||||
name:'桌面配置'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'角色-菜单授权',
|
||||
name:'权限:角色-菜单授权(菜单、按钮、列表、表单)'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'角色-自定义接口授权',
|
||||
name:'权限:角色-自定义接口授权'
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'租户-菜单授权',
|
||||
name:'权限:租户-菜单授权'
|
||||
}
|
||||
]
|
||||
isShowExportModal: true,
|
||||
options:cloneDeep(items)
|
||||
});
|
||||
|
||||
|
||||
function onCheckAllChange(e: any){
|
||||
if(e.target.checked){
|
||||
|
||||
watch(
|
||||
() => state.options,
|
||||
val => {
|
||||
const checkedList=val.filter((item)=>item.checked);
|
||||
if(checkedList.length>0){
|
||||
if(checkedList.length< val.length){
|
||||
state.indeterminate=true;
|
||||
}else{
|
||||
state.checkAll=true;
|
||||
state.indeterminate=false;
|
||||
}
|
||||
|
||||
}else{
|
||||
state.indeterminate=false;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function onCheckAllChange(e: any){
|
||||
if(e.target.checked){
|
||||
state.options.forEach((item)=>{
|
||||
item.checked=true;
|
||||
});
|
||||
}else{
|
||||
}else{
|
||||
state.options.forEach((item)=>{
|
||||
item.checked=false;
|
||||
});
|
||||
}
|
||||
state.indeterminate=false;
|
||||
}
|
||||
}
|
||||
state.indeterminate=false;
|
||||
}
|
||||
|
||||
const [registerExportModal, { openModal: openExportModal }] = useModal();
|
||||
|
||||
function handleClose() {
|
||||
state.isShowExportModal = false;
|
||||
}
|
||||
|
||||
async function beginExport(type){
|
||||
const { notification} = useMessage();
|
||||
const checkedList=state.options.filter((item)=>item.checked);
|
||||
if(checkedList.length==0){
|
||||
notification.warning({
|
||||
message: '提示',
|
||||
description: '未选择任何项目'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(type=='exportSelected'){
|
||||
const checkedList=state.options.filter((item)=>item.checked);
|
||||
openExportModal(true, {
|
||||
items:cloneDeep(unref(checkedList))
|
||||
});
|
||||
}else if(type=='exportAll'){
|
||||
const {createConfirm} = useMessage();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: () => h('span', t('温馨提示')),
|
||||
content: () => t('确定导出所选项目中的所有数据吗?'),
|
||||
width:'500px',
|
||||
onOk: async () => {
|
||||
exportAll();
|
||||
},
|
||||
okText: () => t('确认'),
|
||||
cancelText: () => t('取消'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watch(
|
||||
() => state.options,
|
||||
val => {
|
||||
const checkedList=val.filter((item)=>item.checked);
|
||||
if(checkedList.length>0){
|
||||
if(checkedList.length< val.length){
|
||||
state.indeterminate=true;
|
||||
}else{
|
||||
state.checkAll=true;
|
||||
state.indeterminate=false;
|
||||
}
|
||||
|
||||
}else{
|
||||
state.indeterminate=false;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function openConfirmDialog(){
|
||||
const { notification,createConfirm} = useMessage();
|
||||
const checkedList=state.options.filter((item)=>item.checked);
|
||||
if(checkedList.length==0){
|
||||
notification.warning({
|
||||
message: '提示',
|
||||
description: '未选择任何项目'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: () => h('span', t('温馨提示')),
|
||||
content: () => t('确定导出所选项目吗?'),
|
||||
width:'500px',
|
||||
onOk: async () => {
|
||||
handleExport();
|
||||
},
|
||||
okText: () => t('确认'),
|
||||
cancelText: () => t('取消'),
|
||||
});
|
||||
}
|
||||
|
||||
async function handleExport(){
|
||||
async function exportAll(){
|
||||
try {
|
||||
const checkedCodeList=state.options.filter((item)=>item.checked).map(item => item.code);
|
||||
let currentTime=dateUtil(new Date()).format('YYYY-MM-DD_HH_mm_ss');
|
||||
let result= await exportDatas({
|
||||
exportType:'exportAll',
|
||||
items:checkedCodeList
|
||||
});
|
||||
|
||||
@ -223,8 +180,10 @@
|
||||
border:0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.action-btn{
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<span @click.stop="open">
|
||||
<slot></slot>
|
||||
<a-modal
|
||||
v-model:visible="data.visible"
|
||||
:title="t(`导入数据[${importType=='overrideMode'?'覆盖模式':'新增模式'}]`)"
|
||||
:maskClosable="false"
|
||||
@ok="doUpload"
|
||||
@cancel="close"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="upload-box">
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
class="upload-box"
|
||||
name="file"
|
||||
accept=".json,.zip"
|
||||
:max-count="1"
|
||||
:before-upload="beforeUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<img :src="BgImg" />
|
||||
<div class="a-upload__text">{{ t('将文件拖到此处,或') }}<em>{{ t('点击上传') }}</em></div>
|
||||
</a-upload>
|
||||
<div v-if="VITE_GLOB_UPLOAD_ALERT_TIP?.trim()" style="color: red; margin-top: 8px;">
|
||||
{{VITE_GLOB_UPLOAD_ALERT_TIP}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive,ref} from 'vue';
|
||||
import BgImg from '../../assets/sysconfig_import.png';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
const { VITE_GLOB_UPLOAD_ALERT_TIP } = getAppEnvConfig();
|
||||
const props = defineProps({
|
||||
importType: {
|
||||
type:String,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const globSetting = useGlobSetting();
|
||||
const { notification } = useMessage();
|
||||
|
||||
const data: {
|
||||
visible: boolean;
|
||||
} = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const fileList=ref<File[]>([]);
|
||||
|
||||
async function open() {
|
||||
data.visible = true;
|
||||
}
|
||||
function close() {
|
||||
data.visible = false;
|
||||
fileList.value=[];
|
||||
}
|
||||
|
||||
|
||||
function handleRemove(file:File){
|
||||
const index = fileList.value.indexOf(file);
|
||||
const newFileList = fileList.value.slice();
|
||||
newFileList.splice(index, 1);
|
||||
fileList.value = newFileList;
|
||||
};
|
||||
|
||||
function beforeUpload(file:File){
|
||||
fileList.value = [...(fileList.value || []), file];
|
||||
return false;
|
||||
};
|
||||
|
||||
async function doUpload(){
|
||||
if(fileList==undefined||fileList.value.length==0){
|
||||
notification.error({
|
||||
message: '提示',
|
||||
description: t('请选择文件'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const files=[];
|
||||
fileList.value.forEach(file => {
|
||||
files.push(file.originFileObj);
|
||||
});
|
||||
|
||||
const url="/system/dataMigration/importFullDatas";
|
||||
defHttp.uploadFile(
|
||||
{
|
||||
baseURL: globSetting.apiUrl,
|
||||
url:url,
|
||||
method: 'POST',
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
file: files,
|
||||
data:{
|
||||
importType:props.importType
|
||||
}
|
||||
}
|
||||
).then((data) => {
|
||||
notification.success({
|
||||
message: '提示',
|
||||
description: t('导入中...请稍后确认导入结果!'),
|
||||
});
|
||||
close();
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
}).finally(()=>{
|
||||
// close();
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.tenant-box{
|
||||
height:100px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.upload-box {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.a-upload-dragger {
|
||||
width: 615px;
|
||||
height: 370px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.a-upload__text {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
right: 100px;
|
||||
font-weight: bold;
|
||||
color: #1d2027;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
color: #4f83fd;
|
||||
}
|
||||
</style>
|
||||
271
src/views/system/dataMigration/components/import/ImportModal.vue
Normal file
271
src/views/system/dataMigration/components/import/ImportModal.vue
Normal file
@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="data-migration-modal">
|
||||
<template #title>
|
||||
<a-steps :current="current">
|
||||
<a-step
|
||||
v-for="step in steps"
|
||||
:title="t(step.name)"
|
||||
/>
|
||||
</a-steps>
|
||||
<div class="btn-box">
|
||||
<a-button type="primary" @click="handleStepPrev" :disabled="current == 0">{{t('上一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleStepNext" :disabled="current>=steps.length-1">{{t('下一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleImport" :disabled="current<steps.length-1">{{t('导入')}}</a-button>
|
||||
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="step-container">
|
||||
<Tenant ref="tenantRef" v-show="showIf('租户')"/>
|
||||
<Role ref="roleRef" v-show="showIf('角色')"/>
|
||||
<Post ref="postRef" v-show="showIf('岗位')"/>
|
||||
<UserGroup ref="userGroupRef" v-show="showIf('用户组')"/>
|
||||
<Menu ref="menuRef" v-show="showIf('菜单')"/>
|
||||
<Form ref="formRef" v-show="showIf('表单')"/>
|
||||
<Workflow ref="workflowRef" v-show="showIf('流程定义')"/>
|
||||
<SystemConfig ref="systemConfigRef" v-show="showIf('系统配置')"/>
|
||||
<Dictionary ref="dictionaryRef" v-show="showIf('数据字典')"/>
|
||||
<CodeRule ref="codeRuleRef" v-show="showIf('自动编码')"/>
|
||||
<Desktop ref="desktopRef" v-show="showIf('桌面设计')"/>
|
||||
<RoleMenuAuth ref="roleMenuAuthRef" v-show="showIf('角色-菜单授权')"/>
|
||||
<InterfaceAuth ref="roleInterfaceAuthRef" v-show="showIf('角色-自定义接口授权')"/>
|
||||
<TenantAuthorize ref="tenantMenuAuthRef" v-show="showIf('租户-菜单授权')"/>
|
||||
<ImportTypeSelect ref="importTypeSelectRef" v-show="showIf('导入模式选择')"/>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref,unref,provide,Ref,onMounted} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import {isFunction } from 'lodash-es';
|
||||
import { isNumber } from '/@/utils/is';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { importDatas} from '/@/api/system/dataMigration';
|
||||
import Tenant from './components/Tenant.vue';
|
||||
import Role from './components/Role.vue';
|
||||
import Post from './components/Post.vue';
|
||||
import Menu from './components/Menu.vue';
|
||||
import Form from './components/Form.vue';
|
||||
import Workflow from './components/Workflow.vue';
|
||||
import SystemConfig from './components/SystemConfig.vue';
|
||||
import Dictionary from './components/Dictionary.vue';
|
||||
import Desktop from './components/Desktop.vue';
|
||||
import UserGroup from './components/UserGroup.vue';
|
||||
import RoleMenuAuth from './components/RoleMenuAuth.vue';
|
||||
import InterfaceAuth from './components/InterfaceAuth.vue';
|
||||
import TenantAuthorize from './components/TenantAuthorize.vue';
|
||||
import ImportTypeSelect from './components/ImportTypeSelect.vue';
|
||||
import CodeRule from './components/CodeRule.vue';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notification } = useMessage();
|
||||
const current = ref(0);
|
||||
const steps=ref([]);
|
||||
let dirName=ref('');
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
let items=data.items;
|
||||
items.push({
|
||||
code:'导入模式选择',
|
||||
name:'导入模式选择',
|
||||
ref:"importTypeSelectRef",
|
||||
});
|
||||
steps.value=items;
|
||||
dirName.value=data.dirName;
|
||||
current.value=0;
|
||||
selectedModel.value={};
|
||||
initStep(0);
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
canFullscreen: false,
|
||||
defaultFullscreen: true,
|
||||
destroyOnClose: true,
|
||||
draggable: false,
|
||||
showOkBtn: false,
|
||||
showCancelBtn: false,
|
||||
footer: null,
|
||||
closable: false,
|
||||
});
|
||||
});
|
||||
|
||||
const tenantRef=ref();
|
||||
const roleRef=ref();
|
||||
const postRef= ref();
|
||||
const userGroupRef= ref();
|
||||
const menuRef = ref();
|
||||
const formRef=ref();
|
||||
const workflowRef=ref();
|
||||
const systemConfigRef= ref();
|
||||
const dictionaryRef= ref();
|
||||
const desktopRef= ref();
|
||||
const roleMenuAuthRef= ref();
|
||||
const roleInterfaceAuthRef= ref();
|
||||
const tenantMenuAuthRef= ref();
|
||||
const importTypeSelectRef=ref();
|
||||
const codeRuleRef=ref();
|
||||
|
||||
const refs = {
|
||||
tenantRef,
|
||||
roleRef,
|
||||
postRef,
|
||||
userGroupRef,
|
||||
menuRef,
|
||||
formRef,
|
||||
workflowRef,
|
||||
systemConfigRef,
|
||||
dictionaryRef,
|
||||
desktopRef,
|
||||
roleMenuAuthRef,
|
||||
roleInterfaceAuthRef,
|
||||
tenantMenuAuthRef,
|
||||
importTypeSelectRef,
|
||||
codeRuleRef,
|
||||
};
|
||||
|
||||
const selectedModel: Ref<{
|
||||
menu?:{
|
||||
menuIds: number[],
|
||||
buttonIds: number[],
|
||||
columnIds:number[],
|
||||
formIds:number[]
|
||||
},
|
||||
tenant?:{
|
||||
tenantIds:number[]
|
||||
}
|
||||
}> = ref({});
|
||||
|
||||
provide('selectedModel', selectedModel);
|
||||
provide('dirName', dirName);
|
||||
|
||||
function handleClose() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function showIf(itemType:string){
|
||||
let currentStep=getStep('current');
|
||||
if(currentStep==null){
|
||||
return false;
|
||||
}
|
||||
return currentStep.code==itemType;
|
||||
}
|
||||
|
||||
//上一步
|
||||
function handleStepPrev() {
|
||||
completeStep(current.value);
|
||||
current.value--;
|
||||
initStep(current.value);
|
||||
|
||||
}
|
||||
//下一步
|
||||
async function handleStepNext() {
|
||||
completeStep(current.value);
|
||||
current.value++;
|
||||
initStep(current.value);
|
||||
}
|
||||
|
||||
//导出
|
||||
async function handleImport(){
|
||||
completeStep(current.value);
|
||||
|
||||
let validateResult=false;
|
||||
let selectedModelVal=selectedModel.value;
|
||||
for(let key of Object.keys(selectedModelVal)){
|
||||
let item=selectedModelVal[key];
|
||||
if(item!=null){
|
||||
validateResult=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!validateResult){
|
||||
notification.warning({
|
||||
message: '提示',
|
||||
description: '没有选择任何项目'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await importDatas({
|
||||
dirName:dirName.value,
|
||||
data:{
|
||||
...unref(selectedModel)
|
||||
},
|
||||
});
|
||||
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('导入完成!'),
|
||||
});
|
||||
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function getStep(key:string|number){
|
||||
let stepList=unref(steps);
|
||||
if(!stepList||stepList.length==0){
|
||||
return null;
|
||||
}
|
||||
let step=null;
|
||||
if(key=='current'){
|
||||
step=unref(stepList[current.value]);
|
||||
}else if(key=='prev'){
|
||||
step=unref(stepList[current.value-1]);
|
||||
}else if(key=='next'){
|
||||
step=unref(stepList[current.value+1]);
|
||||
}else if(isNumber(key)&&key>=0&&key<stepList.length){
|
||||
step=unref(stepList[key]);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
function initStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.initStep&& stepRef.initStep();
|
||||
|
||||
};
|
||||
|
||||
function completeStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.completeStep&& stepRef.completeStep();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.data-migration-modal{
|
||||
.step-container {
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.ant-modal-header{
|
||||
height: 100px;
|
||||
}
|
||||
.ant-steps{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ant-steps-horizontal:not(.ant-steps-label-vertical) .ant-steps-item {
|
||||
padding-left:16px!important;
|
||||
margin-bottom: 10px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.btn-box {
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top:60px;
|
||||
:deep(.ant-btn) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<span @click.stop="open">
|
||||
<slot></slot>
|
||||
<a-modal
|
||||
v-model:visible="data.visible"
|
||||
:title="t('导入数据')"
|
||||
:maskClosable="false"
|
||||
@ok="doUpload"
|
||||
@cancel="close"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="upload-box">
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
class="upload-box"
|
||||
name="file"
|
||||
accept=".json,.zip"
|
||||
:max-count="1"
|
||||
:before-upload="beforeUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<img :src="BgImg" />
|
||||
<div class="a-upload__text">{{ t('将文件拖到此处,或') }}<em>{{ t('点击上传') }}</em></div>
|
||||
</a-upload>
|
||||
<div v-if="VITE_GLOB_UPLOAD_ALERT_TIP?.trim()" style="color: red; margin-top: 8px;">
|
||||
{{VITE_GLOB_UPLOAD_ALERT_TIP}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive,ref} from 'vue';
|
||||
import BgImg from '../../assets/sysconfig_import.png';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
|
||||
const emits=defineEmits(['fileUploaded']);
|
||||
|
||||
const { VITE_GLOB_UPLOAD_ALERT_TIP } = getAppEnvConfig();
|
||||
|
||||
const props = defineProps({
|
||||
importType: {
|
||||
type:String,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const globSetting = useGlobSetting();
|
||||
const { notification } = useMessage();
|
||||
|
||||
const data: {
|
||||
visible: boolean;
|
||||
} = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const fileList=ref<File[]>([]);
|
||||
|
||||
async function open() {
|
||||
data.visible = true;
|
||||
}
|
||||
function close() {
|
||||
data.visible = false;
|
||||
fileList.value=[];
|
||||
}
|
||||
|
||||
|
||||
function handleRemove(file:File){
|
||||
const index = fileList.value.indexOf(file);
|
||||
const newFileList = fileList.value.slice();
|
||||
newFileList.splice(index, 1);
|
||||
fileList.value = newFileList;
|
||||
};
|
||||
|
||||
function beforeUpload(file:File){
|
||||
fileList.value = [...(fileList.value || []), file];
|
||||
return false;
|
||||
};
|
||||
|
||||
async function doUpload(){
|
||||
if(fileList==undefined||fileList.value.length==0){
|
||||
notification.error({
|
||||
message: '提示',
|
||||
description: t('请选择文件'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const files=[];
|
||||
fileList.value.forEach(file => {
|
||||
files.push(file.originFileObj);
|
||||
});
|
||||
|
||||
const url="/system/dataMigration/analyseDatas";
|
||||
defHttp.uploadFile(
|
||||
{
|
||||
baseURL: globSetting.apiUrl,
|
||||
url:url,
|
||||
method: 'POST',
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
file: files
|
||||
}
|
||||
).then((data) => {
|
||||
emits('fileUploaded', data);
|
||||
close();
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
}).finally(()=>{
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.upload-box {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.a-upload-dragger {
|
||||
width: 615px;
|
||||
height: 370px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.a-upload__text {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
right: 100px;
|
||||
font-weight: bold;
|
||||
color: #1d2027;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
color: #4f83fd;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: t('编码编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编码名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('当前流水号'),
|
||||
dataIndex: 'currentNumber',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('创建用户'),
|
||||
dataIndex: 'createUserName',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('创建时间'),
|
||||
dataIndex: 'createDate',
|
||||
width: 180,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编码说明'),
|
||||
dataIndex: 'description',
|
||||
width: 180,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'自动编码',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
currentNumber: item.currentNumber,
|
||||
createUserName: item.createUserName,
|
||||
createDate: item.createDate,
|
||||
description: item.description
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let codeRule = selectedModel.value.codeRule || (selectedModel.value.codeRule = {});
|
||||
codeRule.itemName='自动编码';
|
||||
codeRule.importType=codeRule.importType?codeRule.importType:'overrideMode';
|
||||
codeRule.codeRuleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.codeRule=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'桌面设计',dirName:dirName.value});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let deskTop = selectedModel.value.deskTop || (selectedModel.value.deskTop = {});
|
||||
deskTop.itemName='桌面设计';
|
||||
deskTop.importType=deskTop.importType?deskTop.importType:'overrideMode';
|
||||
deskTop.desktopIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.deskTop=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable
|
||||
@register="registerTableItem"
|
||||
@row-click="handleRowClick"
|
||||
class="w-2/5 xl:w-2/5 mr-2"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedItems[record.id]"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<BasicTable @register="registerTableDetail" class="w-3/5 xl:w-3/5">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedDetails[record.id]"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject,nextTick } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {
|
||||
getDicItemPageList,
|
||||
getDicDetailPageList
|
||||
} from '/@/api/system/dic';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const checkedItems=ref({});
|
||||
const checkedDetails=ref({});
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
let data=null;
|
||||
|
||||
const searchFormSchemaItem: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('项目名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const itemColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('项目名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchemaDetail: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('字典名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const detailColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('字典名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('值'),
|
||||
dataIndex: 'value',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('所属项目'),
|
||||
dataIndex: 'itemName',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
align: 'left',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const selectItemId = ref('');
|
||||
|
||||
const [registerTableItem, { reload: itemReload }] = useTable({
|
||||
title: t('数据字典项'),
|
||||
api: loadDictionaryItem,
|
||||
rowKey: 'id',
|
||||
columns: itemColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaItem,
|
||||
showResetButton: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTableDetail, { reload: detailReload }] = useTable({
|
||||
title: t('数据字典详情'),
|
||||
api: loadDictionaryDetails,
|
||||
rowKey: 'id',
|
||||
columns: detailColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaDetail,
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, itemId: selectItemId.value };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
selectItemId.value = record.id;
|
||||
detailReload();
|
||||
}
|
||||
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
selectItemId.value = rowKey[0];
|
||||
detailReload();
|
||||
}
|
||||
|
||||
async function loadDictionaryItem(params){
|
||||
if(!inited.value){
|
||||
return [];
|
||||
}
|
||||
return data?.dicItems||[];
|
||||
}
|
||||
|
||||
async function loadDictionaryDetails(params){
|
||||
if(!inited.value){
|
||||
return [];
|
||||
}
|
||||
let itemId=params?.itemId;
|
||||
let details=data?.dicDetails||[];
|
||||
|
||||
let rtList=[];
|
||||
if(itemId){
|
||||
rtList=details.filter((detail)=>{return detail.itemId===itemId});
|
||||
}else{
|
||||
rtList=details;
|
||||
}
|
||||
|
||||
let items=data?.dicItems||[];
|
||||
const itemMap = items.reduce((acc, item) => {
|
||||
acc[item.id] = item.name;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
for(let i=0;i<rtList.length;i++){
|
||||
let detail=rtList[i];
|
||||
detail.itemName=itemMap[detail.itemId]||'--';
|
||||
}
|
||||
|
||||
return rtList;
|
||||
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({ itemName:'数据字典',dirName:dirName.value});
|
||||
data=rtObject.data;
|
||||
inited.value=true;
|
||||
|
||||
//默认选择全部
|
||||
let dicItems=data?.dicItems||[];
|
||||
dicItems.forEach(item=>{
|
||||
checkedItems.value[item.id]=true;
|
||||
});
|
||||
|
||||
let dicDetails=data?.dicDetails||[];
|
||||
dicDetails.forEach(item=>{
|
||||
checkedDetails.value[item.id]=true;
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
itemReload();
|
||||
loadDictionaryDetails();
|
||||
});
|
||||
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let items=checkedItems.value;
|
||||
let details=checkedDetails.value;
|
||||
let checkItemIds = Object.keys(items).filter(key => items[key] === true);
|
||||
let checkDetailIds = Object.keys(details).filter(key => details[key] === true);
|
||||
|
||||
if((checkItemIds&&checkItemIds.length>0)||(checkDetailIds&&checkDetailIds.length>0)){
|
||||
let dictionary = selectedModel.value.dictionary || (selectedModel.value.dictionary = {});
|
||||
dictionary.itemName='数据字典';
|
||||
dictionary.importType=dictionary.importType?dictionary.importType:'overrideMode';
|
||||
|
||||
dictionary.itemIds=checkItemIds;
|
||||
dictionary.detailIds=checkDetailIds;
|
||||
}else{
|
||||
selectedModel.value.dictionary=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getFormTemplatePage} from '/@/api/form/design';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([,
|
||||
{
|
||||
title: '表单ID',
|
||||
key:'id',
|
||||
dataIndex:'id',
|
||||
align: 'left',
|
||||
width:'40%',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '表单名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'表单',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
id: item.id,
|
||||
name: item.name
|
||||
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let formTemplate = selectedModel.value.formTemplate || (selectedModel.value.formTemplate = {});
|
||||
formTemplate.itemName='表单模板';
|
||||
formTemplate.importType=formTemplate.importType?formTemplate.importType:'overrideMode';
|
||||
formTemplate.formTemplateIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.formTemplate=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<div class="import-type-select">
|
||||
<a-table :dataSource="tableConfig.dataSource" :columns="tableConfig.columns" :pagination="false" >
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'importType'">
|
||||
<a-radio-group v-if="isForceOverrideMode(record.key)" v-model:value="record.importType">
|
||||
<a-radio value="overrideMode">覆盖模式</a-radio>
|
||||
</a-radio-group>
|
||||
<a-radio-group v-else v-model:value="record.importType">
|
||||
<a-radio value="addMode">新增模式</a-radio>
|
||||
<a-radio value="overrideMode">覆盖模式</a-radio>
|
||||
|
||||
</a-radio-group>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref,inject,onMounted,nextTick } from 'vue';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
|
||||
const inited=ref(false);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const tableConfig=ref({
|
||||
dataSource:[],
|
||||
columns:[
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'sortNum',
|
||||
key: 'sortNum',
|
||||
width:'100px'
|
||||
},
|
||||
{
|
||||
title: '项目名',
|
||||
dataIndex: 'itemName',
|
||||
key: 'itemName',
|
||||
width:'200px'
|
||||
},
|
||||
{
|
||||
title: '导入模式',
|
||||
dataIndex: 'importType',
|
||||
key: 'importType',
|
||||
width:'400px'
|
||||
},
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
function isForceOverrideMode(itemKey){
|
||||
let arr=['menu','interfaceAuth','authorize','formTemplate'];
|
||||
if(arr.includes(itemKey)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
tableConfig.value.dataSource=[];
|
||||
let arr=[];
|
||||
let selectedModelVal=selectedModel.value;
|
||||
let sortNum=0;
|
||||
for(let key of Object.keys(selectedModelVal)){
|
||||
let item=selectedModelVal[key];
|
||||
if(item){
|
||||
arr.push(
|
||||
{
|
||||
key:key,
|
||||
sortNum:++sortNum,
|
||||
itemName:item.itemName,
|
||||
importType:item.importType
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
tableConfig.value.dataSource=arr;
|
||||
inited.value=true;
|
||||
}catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function completeStep(){
|
||||
let dataSource=tableConfig.value.dataSource;
|
||||
if(dataSource&&dataSource.length>0){
|
||||
const map = dataSource.reduce((map, item) => {
|
||||
map.set(item.key, item);
|
||||
return map;
|
||||
}, new Map());
|
||||
|
||||
console.log(map);
|
||||
|
||||
let selectedModelVal=selectedModel.value;
|
||||
for(let key of Object.keys(selectedModelVal)){
|
||||
let item=selectedModelVal[key];
|
||||
let i=map.get(key);
|
||||
if(i){
|
||||
item.importType=i.importType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.import-type-select{
|
||||
width:100%;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
align-items:center;
|
||||
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr > td){
|
||||
padding-left: 8px!important;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
// import {getTenantPageList} from '/@/api/system/tenant';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色id',
|
||||
key:'roleId',
|
||||
dataIndex:'roleId',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key:'roleName',
|
||||
dataIndex:'roleName',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '接口id',
|
||||
key:'interfaceId',
|
||||
dataIndex:'interfaceId',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '接口名称',
|
||||
key:'interfaceName',
|
||||
dataIndex:'interfaceName',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '接口类型',
|
||||
dataIndex: 'groupId',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
const groupId = record.groupId;
|
||||
if(groupId){
|
||||
return '接口'
|
||||
} else {
|
||||
return '目录';
|
||||
}
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'角色-自定义接口授权',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
roleId: item.roleId,
|
||||
roleName: item.roleName,
|
||||
interfaceId: item.interfaceId,
|
||||
interfaceName: item.interfaceName,
|
||||
groupId: item.groupId
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let interfaceAuth = selectedModel.value.interfaceAuth || (selectedModel.value.interfaceAuth = {});
|
||||
interfaceAuth.itemName='角色-自定义接口授权';
|
||||
interfaceAuth.importType='overrideMode';
|
||||
interfaceAuth.interfaceAuthIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.interfaceAuth=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<a-row :gutter="[16, 16]" class="h-full">
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('菜单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="menuData"
|
||||
checkable
|
||||
ref="menuRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('按钮')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="buttonData"
|
||||
checkable
|
||||
ref="ButtonRef"
|
||||
@check="handleButtonSelect"
|
||||
:fieldNames="{ title: 'name', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('字段')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="columnData"
|
||||
checkable
|
||||
ref="ColumnRef"
|
||||
@check="handleColumnSelect"
|
||||
:fieldNames="{ title: 'name', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('表单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="fieldData"
|
||||
checkable
|
||||
ref="FieldRef"
|
||||
@check="handleFieldSelect"
|
||||
:fieldNames="{ title: 'name', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject,nextTick} from 'vue';
|
||||
import { TreeItem,BasicTree,TreeActionType} from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { getMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { getMenuButtonList, getMenuColumnList, getMenuFieldList } from '/@/api/system/menuButton';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const MenuRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ButtonRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ColumnRef = ref<Nullable<TreeActionType>>(null);
|
||||
const FieldRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
const menuData = ref<TreeItem[]>([]);
|
||||
const buttonData = ref<TreeItem[]>([]);
|
||||
const columnData = ref<TreeItem[]>([]);
|
||||
const fieldData = ref<TreeItem[]>([]);
|
||||
|
||||
let menuSelectData =[];
|
||||
let buttonSelectData =[];
|
||||
let columnSelectData =[];
|
||||
let fieldSelectData =[];
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
let rtObject= await getUploadedData({itemName:'菜单',dirName:dirName.value});
|
||||
let data=rtObject.data;
|
||||
|
||||
menuData.value=data.menus;
|
||||
|
||||
let allMenus=data.allMenus;
|
||||
|
||||
buttonData.value=buildObjectTree(allMenus,data.menuButtons);
|
||||
columnData.value=buildObjectTree(allMenus,data.menuColumns);
|
||||
fieldData.value=buildObjectTree(allMenus,data.menuForms);
|
||||
nextTick(() => {
|
||||
unref(MenuRef)?.expandAll(true);
|
||||
unref(ButtonRef)?.expandAll(true);
|
||||
unref(ColumnRef)?.expandAll(true);
|
||||
unref(FieldRef)?.expandAll(true);
|
||||
});
|
||||
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildObjectTree(menus,objects){
|
||||
if(!objects||objects.length==0){
|
||||
return [];
|
||||
}
|
||||
let cloneMenus=cloneDeep(menus);
|
||||
let rootMenus=cloneMenus.filter(i => i.parentId === '0');
|
||||
let arr=[];
|
||||
let bObjects=[];
|
||||
buildObjectChildren(rootMenus,cloneMenus,objects,bObjects);
|
||||
for(let i=0;i<rootMenus.length;i++){
|
||||
let rootMenu=rootMenus[i];
|
||||
if(rootMenu.flag===true){
|
||||
arr.push(rootMenu);
|
||||
}
|
||||
}
|
||||
let otherObjects=objects.filter(i=>!bObjects.includes(i));
|
||||
arr.push(...otherObjects);
|
||||
return arr;
|
||||
}
|
||||
|
||||
function buildObjectChildren(menus,allMenus,objects,bObjects){
|
||||
let rt=false;
|
||||
for(let i=0;i<menus.length;i++){
|
||||
let menu=menus[i];
|
||||
let childMenus=allMenus.filter(i => i.parentId === menu.id);
|
||||
let objectArr=[];
|
||||
menu.flag=false;
|
||||
for(let j=0;j<objects.length;j++){
|
||||
let object=objects[j];
|
||||
if(object.menuId===menu.id){
|
||||
objectArr.push(object);
|
||||
menu.flag=true;
|
||||
rt=true;
|
||||
}
|
||||
}
|
||||
menu.children=childMenus?childMenus:[];
|
||||
if(childMenus&&childMenus.length>0){
|
||||
let result=buildObjectChildren(menu.children,allMenus,objects,bObjects);
|
||||
if(result===true){
|
||||
menu.flag=true;
|
||||
rt=true;
|
||||
}
|
||||
}
|
||||
let filteredChildren=menu.children.filter(i => i.flag === true);
|
||||
menu.children=[...filteredChildren,...objectArr];
|
||||
bObjects.push(...objectArr);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
function handleTreeSelect(keys, e) {
|
||||
menuSelectData= [...e.halfCheckedKeys, ...keys];
|
||||
|
||||
}
|
||||
|
||||
function handleButtonSelect(keys, e) {
|
||||
buttonSelectData = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleColumnSelect(keys, e) {
|
||||
columnSelectData = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleFieldSelect(keys, e) {
|
||||
fieldSelectData = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
|
||||
if((menuSelectData.length>0)||(buttonSelectData.length>0)
|
||||
||(columnSelectData.length>0)||(fieldSelectData.length>0)){
|
||||
let menu = selectedModel.value.menu || (selectedModel.value.menu = {});
|
||||
menu.itemName='菜单';
|
||||
menu.importType='overrideMode';
|
||||
|
||||
menu.menuIds=menuSelectData;
|
||||
menu.buttonIds=buttonSelectData;
|
||||
menu.columnIds=columnSelectData;
|
||||
menu.formIds=fieldSelectData;
|
||||
}else{
|
||||
selectedModel.value.menu=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less">
|
||||
.autherizeDialog {
|
||||
.ant-modal {
|
||||
top: 20px;
|
||||
height: calc(100% - 40px) !important;
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-modal-body {
|
||||
height: calc(100% - 120px) !important;
|
||||
|
||||
.scrollbar__view {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
max-height: none !important;
|
||||
|
||||
.ant-col {
|
||||
height: 100%;
|
||||
|
||||
.treebox {
|
||||
height: calc(100% - 42px);
|
||||
overflow: auto;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-right: solid 1px #ccc;
|
||||
|
||||
.vben-tree {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject,h} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
import {getFormTemplatePage} from '/@/api/form/design';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '部门名称',
|
||||
key:'deptName',
|
||||
dataIndex:'deptName',
|
||||
align: 'left',
|
||||
width:'40%',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '岗位名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
width:'40%',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '岗位编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('状态'),
|
||||
dataIndex: 'enabledMark',
|
||||
width: 80,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
customRender: ({ record }) => {
|
||||
const status = record.enabledMark;
|
||||
const enable = ~~status === 1;
|
||||
const color = enable ? 'green' : 'red';
|
||||
const text = enable ? t('启用') : t('停用');
|
||||
return h(Tag, { color: color }, () => text);
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'岗位',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark:item.enabledMark,
|
||||
deptName: item.deptName,
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let post = selectedModel.value.post || (selectedModel.value.post = {});
|
||||
post.itemName='职位';
|
||||
post.importType='overrideMode';
|
||||
post.postIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.post=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getRolePageList} from '/@/api/system/role';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'角色',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let role = selectedModel.value.role || (selectedModel.value.role = {});
|
||||
role.itemName='角色';
|
||||
role.importType=role.importType?role.importType:'overrideMode';
|
||||
role.roleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.role=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色id',
|
||||
key:'roleId',
|
||||
dataIndex:'roleId',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key:'roleName',
|
||||
dataIndex:'roleName',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
key:'menuName',
|
||||
dataIndex: 'menuName',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '对象id',
|
||||
key:'objectId',
|
||||
dataIndex: 'objectId',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '对象名称',
|
||||
key:'objectName',
|
||||
dataIndex: 'objectName',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '对象类型',
|
||||
dataIndex: 'authorizeType',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
const authorizeType = record.authorizeType;
|
||||
if(authorizeType===0){
|
||||
return '菜单'
|
||||
}else if(authorizeType===1){
|
||||
return '按钮'
|
||||
}else if(authorizeType===2){
|
||||
return '列表字段'
|
||||
}else if(authorizeType===3){
|
||||
return '表单字段'
|
||||
}else{
|
||||
return '';
|
||||
}
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'角色-菜单授权',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
roleId: item.roleId,
|
||||
roleName: item.roleName,
|
||||
menuName: item.menuName,
|
||||
objectId: item.objectId,
|
||||
objectName: item.objectName,
|
||||
authorizeType:item.authorizeType
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let authorize = selectedModel.value.authorize || (selectedModel.value.authorize = {});
|
||||
authorize.itemName='角色-菜单授权';
|
||||
authorize.importType='overrideMode';
|
||||
authorize.authorizeIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.authorize=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getXjrSystemConfigPage} from '/@/api/system/systemConfig';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
key:'value',
|
||||
dataIndex:'value',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'系统配置',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
value: item.value
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let systemConfig = selectedModel.value.systemConfig || (selectedModel.value.systemConfig = {});
|
||||
systemConfig.itemName='系统配置';
|
||||
systemConfig.importType=systemConfig.importType?systemConfig.importType:'overrideMode';
|
||||
systemConfig.systemConfigIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.systemConfig=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
// import {getTenantPageList} from '/@/api/system/tenant';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '租户名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '租户编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'租户',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let tenant = selectedModel.value.tenant || (selectedModel.value.tenant = {});
|
||||
tenant.itemName='租户';
|
||||
tenant.importType='overrideMode';
|
||||
tenant.tenantIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenant=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div style="margin-left: 30%">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">
|
||||
{{ t('租户菜单授权') }}
|
||||
</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="treeData"
|
||||
checkable
|
||||
ref="treeRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject,nextTick} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { TreeItem, BasicTree, TreeActionType } from '/@/components/Tree';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const { t } = useI18n();
|
||||
const treeRef = ref<Nullable<TreeActionType>>(null);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function getTree(tree) {
|
||||
return tree || null;
|
||||
}
|
||||
function handleTreeSelect(keys, e) {
|
||||
selectedKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'租户-菜单授权',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
treeData.value=resList;
|
||||
|
||||
nextTick(() => {
|
||||
getTree(unref(treeRef)).expandAll(true);
|
||||
});
|
||||
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let tenantAuthorize = selectedModel.value.tenantAuthorize || (selectedModel.value.tenantAuthorize = {});
|
||||
tenantAuthorize.itemName='租户-菜单授权';
|
||||
tenantAuthorize.importType=tenantAuthorize.importType?tenantAuthorize.importType:'overrideMode';
|
||||
tenantAuthorize.tenantAuthorizeIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenantAuthorize=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '用户组名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '用户组编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'用户组',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let userGroup = selectedModel.value.userGroup || (selectedModel.value.userGroup = {});
|
||||
userGroup.itemName='用户组';
|
||||
userGroup.importType='overrideMode';
|
||||
userGroup.userGroupIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.userGroup=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getUploadedData} from '/@/api/system/dataMigration';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '流程编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '流程名称',
|
||||
key:'name',
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
const dirName= inject('dirName');
|
||||
|
||||
function onSelectChange(selectedRowKeys, selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getUploadedData({itemName:'流程定义',dirName:dirName.value});
|
||||
let resList=rtObject.data;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code
|
||||
}
|
||||
);
|
||||
selectedKeys.value.push(item.id);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let workflowSchema = selectedModel.value.workflowSchema || (selectedModel.value.workflowSchema = {});
|
||||
workflowSchema.itemName='流程定义';
|
||||
workflowSchema.importType=workflowSchema.importType?workflowSchema.importType:'overrideMode';
|
||||
workflowSchema.workflowSchemaIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.workflowSchema=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<div class="import">
|
||||
<div class="import-log"><span @click.stop="showLogsDialog">查看导入日志</span></div>
|
||||
<!-- <div class="import-log"><span @click.stop="showLogsDialog">查看导入日志</span></div> -->
|
||||
<div class="import-type-wrapper">
|
||||
<ImportSystemConfig importType="tenantMode" v-if="getAppEnvConfig().VITE_GLOB_TENANT_ENABLED">
|
||||
<span class="item-action">租户模式导入</span>
|
||||
</ImportSystemConfig>
|
||||
<ImportSystemConfig importType="overrideMode">
|
||||
<!-- <FullImportSelectFile importType="addMode" v-if="getAppEnvConfig().VITE_GLOB_TENANT_ENABLED">
|
||||
<span class="item-action">新增模式导入</span>
|
||||
</FullImportSelectFile>
|
||||
<FullImportSelectFile importType="overrideMode">
|
||||
<span class="item-action">覆盖模式导入</span>
|
||||
</ImportSystemConfig>
|
||||
</FullImportSelectFile> -->
|
||||
<PickingImportSelectFile @fileUploaded="openImportWindow">
|
||||
<span class="item-action">选择文件</span>
|
||||
</PickingImportSelectFile>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal
|
||||
@ -21,11 +24,11 @@
|
||||
<template v-for="item in logs" :key="item.index">
|
||||
<div class="log">
|
||||
<span class="col-item">日志时间:<span class="content">{{item.time}}</span></span>
|
||||
<span class="col-item">导入模式:<span class="content">{{item.importType=='overrideMode'?'覆盖模式':'租户模式'}}</span></span>
|
||||
<span class="col-item">导入模式:<span class="content">{{item.importType=='overrideMode'?'覆盖模式':'新增模式'}}</span></span>
|
||||
<span class="col-item">执行结果:
|
||||
<span :class="{'success':item.result=='success','fail':item.result!=='success'}">{{item.result=='success'?'成功':'失败'}}</span>
|
||||
</span>
|
||||
<span class="col-item" v-if="item.importType=='tenantMode'">租户编码:<span class="content">{{item.tenantCode}}</span></span>
|
||||
<!--<span class="col-item" v-if="item.importType=='tenantMode'">租户编码:<span class="content">{{item.tenantCode}}</span></span>-->
|
||||
<span class="col-item">
|
||||
<span class="content view-log-detail" v-if="item.result!=='success'" @click.stop="viewLogDetails(item.fileName)" >查看日志详情 </span>
|
||||
</span>
|
||||
@ -44,17 +47,40 @@
|
||||
<a-textarea v-model:value="logDetails" :style="{whiteSpace:'pre',overflowX: 'auto',color:'red'}" :rows="20" />
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<ImportModal
|
||||
v-if="state.isShowImportModal"
|
||||
@register="registerImportModal"
|
||||
@close="handleClose()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive,ref} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import ImportSystemConfig from './ImportSystemConfig.vue';
|
||||
import { getLogList,getLogDetails} from '/@/api/system/dataMigration';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {getAppEnvConfig} from "/@/utils/env";
|
||||
import FullImportSelectFile from './FullImportSelectFile.vue';
|
||||
import PickingImportSelectFile from './PickingImportSelectFile.vue';
|
||||
import { getLogList,getLogDetails} from '/@/api/system/dataMigration';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import ImportModal from './ImportModal.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { items } from '../config.ts';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const state = reactive({
|
||||
isShowImportModal: true,
|
||||
options:cloneDeep(items)
|
||||
});
|
||||
|
||||
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
||||
|
||||
const logsDialog: {
|
||||
visible: boolean;
|
||||
} = reactive({
|
||||
@ -70,6 +96,21 @@
|
||||
const logs=ref([]);
|
||||
const logDetails=ref([]);
|
||||
|
||||
function openImportWindow(data){
|
||||
let items=state.options.filter((item)=>{return data.items.includes(item.code)});
|
||||
if(items&&items.length>0){
|
||||
openImportModal(true, {
|
||||
items:cloneDeep(items),
|
||||
dirName:data.dirName
|
||||
});
|
||||
}else{
|
||||
notification.error({
|
||||
message: '提示',
|
||||
description: '没有从文件中识别到可导入项目,请确认文件内容是否正确'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function showLogsDialog(){
|
||||
logs.value=[];
|
||||
logsDialog.visible=true;
|
||||
@ -83,6 +124,7 @@
|
||||
logDetailsDialog.visible=true;
|
||||
logDetails.value=logContent;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@ -125,6 +167,9 @@
|
||||
padding:6px 8px;
|
||||
border-radius:8px;
|
||||
color:rgba(0, 0, 0, 0.8);
|
||||
display: block;
|
||||
min-width:100px;
|
||||
text-align: center;
|
||||
}
|
||||
.item-action:hover {
|
||||
cursor: pointer;
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
@finish="onFinish"
|
||||
@finish-failed="onFinishFailed"
|
||||
>
|
||||
<a-form-item :label="t('多端登录')" name="mulLogin">
|
||||
<!-- <a-form-item :label="t('多端登录')" name="mulLogin">
|
||||
<div class="flex">
|
||||
<div
|
||||
v-for="item in loginType"
|
||||
@ -41,9 +41,9 @@
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
</a-form-item> -->
|
||||
|
||||
<a-form-item :label="t('同端互斥')" name="mutualExclusion">
|
||||
<!-- <a-form-item :label="t('同端互斥')" name="mutualExclusion">
|
||||
<a-switch
|
||||
v-model:checked="formState.mutualExclusion"
|
||||
:checkedValue="1"
|
||||
@ -67,7 +67,7 @@
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
</a-form-item> -->
|
||||
|
||||
<a-form-item :label="t('只登出当前会话')" name="checkLogoutCurrentSession">
|
||||
<a-switch
|
||||
@ -95,6 +95,27 @@
|
||||
v-model:checked="formState.withoutLogin"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
@change="handleWithoutChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="withoutLoginTime">
|
||||
<template #label>
|
||||
<span style="display: inline-flex; align-items: center;">
|
||||
{{ t('登录有效时长') }}
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
<p style="max-width: 336px">单位为小时,大于等于零的整数,0或者空使用配置文件配置, 七天免登优先于免登时长配置</p>
|
||||
</template>
|
||||
<QuestionCircleFilled style="font-size: 16px; color: #ccc; margin-left: 6px;" />
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:value="formState.withoutLoginTime"
|
||||
:placeholder="t('登录有效时长')"
|
||||
style="width: 50%"
|
||||
@change="handleWithoutTimeChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('密码策略')" name="passwordStrategy">
|
||||
@ -232,6 +253,29 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="lockPageReLogin">
|
||||
<template #label>
|
||||
<span style="display: inline-flex; align-items: center;">
|
||||
{{ t('登录失效后回到锁屏页') }}
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
<p style="max-width: 336px">
|
||||
锁屏重登:用户信息超时,开启锁屏重登后,跳转到锁屏界面,解锁后可以继续提交当前页面
|
||||
</p>
|
||||
</template>
|
||||
<QuestionCircleFilled
|
||||
style="font-size: 16px; color: #ccc; margin-left: 6px;"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-switch
|
||||
v-model:checked="formState.lockPageReLogin"
|
||||
:checkedValue="true"
|
||||
:unCheckedValue="false"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ offset: 6, span: 24 }">
|
||||
<a-button type="primary" html-type="submit" class="ml-4">{{ t('提交') }}</a-button>
|
||||
</a-form-item>
|
||||
@ -266,6 +310,8 @@
|
||||
checkErrorLoginBlockIpCount?: number | null;
|
||||
checkErrorLoginBlockIpRestoreTime?: number | null;
|
||||
checkLogoutCurrentSession?: boolean;
|
||||
withoutLoginTime?: number;
|
||||
lockPageReLogin?: boolean;
|
||||
}
|
||||
|
||||
let formState = ref<FormState>({});
|
||||
@ -288,7 +334,9 @@
|
||||
checkErrorLoginBlockIp: res.checkErrorLoginBlockIp,
|
||||
checkErrorLoginBlockIpCount: res.checkErrorLoginBlockIpCount,
|
||||
checkErrorLoginBlockIpRestoreTime: res.checkErrorLoginBlockIpRestoreTime,
|
||||
checkLogoutCurrentSession: res.checkLogoutCurrentSession
|
||||
checkLogoutCurrentSession: res.checkLogoutCurrentSession,
|
||||
withoutLoginTime: res.withoutLoginTime,
|
||||
lockPageReLogin: res.lockPageReLogin,
|
||||
};
|
||||
id.value = res.id;
|
||||
});
|
||||
@ -358,6 +406,16 @@
|
||||
content: h('div', {}, [h('p', '修改同端互斥状态需重启后端!')]),
|
||||
});
|
||||
};
|
||||
function handleWithoutChange() {
|
||||
if (formState.value.withoutLogin == 1) {
|
||||
formState.value.withoutLoginTime = 0;
|
||||
}
|
||||
};
|
||||
function handleWithoutTimeChange() {
|
||||
if (formState.value.withoutLoginTime && formState.value.withoutLoginTime > 0) {
|
||||
formState.value.withoutLogin = 0;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
.gouBox {
|
||||
|
||||
139
src/views/system/tenant/components/ImportModal.vue
Normal file
139
src/views/system/tenant/components/ImportModal.vue
Normal file
@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<span @click.stop="open">
|
||||
<slot></slot>
|
||||
<a-modal
|
||||
v-model:visible="data.visible"
|
||||
:title="t(`导入租户`)"
|
||||
:maskClosable="false"
|
||||
@ok="doUpload"
|
||||
@cancel="close"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="upload-box">
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
class="upload-box"
|
||||
name="file"
|
||||
accept=".json,.zip"
|
||||
:max-count="1"
|
||||
:before-upload="beforeUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div class="a-upload__text">{{ t('将文件拖到此处,或') }}<em>{{ t('点击上传') }}</em></div>
|
||||
</a-upload>
|
||||
<div v-if="VITE_GLOB_UPLOAD_ALERT_TIP?.trim()" style="color: red; margin-top: 8px;">
|
||||
{{VITE_GLOB_UPLOAD_ALERT_TIP}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive,ref} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
|
||||
const { VITE_GLOB_UPLOAD_ALERT_TIP } = getAppEnvConfig();
|
||||
const props = defineProps({
|
||||
importType: {
|
||||
type:String,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const globSetting = useGlobSetting();
|
||||
const { notification } = useMessage();
|
||||
|
||||
const data: {
|
||||
visible: boolean;
|
||||
} = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const fileList=ref<File[]>([]);
|
||||
|
||||
async function open() {
|
||||
data.visible = true;
|
||||
}
|
||||
function close() {
|
||||
data.visible = false;
|
||||
fileList.value=[];
|
||||
}
|
||||
|
||||
|
||||
function handleRemove(file:File){
|
||||
const index = fileList.value.indexOf(file);
|
||||
const newFileList = fileList.value.slice();
|
||||
newFileList.splice(index, 1);
|
||||
fileList.value = newFileList;
|
||||
};
|
||||
|
||||
function beforeUpload(file:File){
|
||||
fileList.value = [...(fileList.value || []), file];
|
||||
return false;
|
||||
};
|
||||
|
||||
async function doUpload(){
|
||||
if(fileList==undefined||fileList.value.length==0){
|
||||
notification.error({
|
||||
message: '提示',
|
||||
description: t('请选择文件'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const files=[];
|
||||
fileList.value.forEach(file => {
|
||||
files.push(file.originFileObj);
|
||||
});
|
||||
|
||||
const url="/system/tenant/import";
|
||||
defHttp.uploadFile(
|
||||
{
|
||||
baseURL: globSetting.apiUrl,
|
||||
url:url,
|
||||
method: 'POST',
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
file: files,
|
||||
}
|
||||
).then((data) => {
|
||||
notification.success({
|
||||
message: '提示',
|
||||
description: t('导入成功!'),
|
||||
});
|
||||
close();
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
}).finally(()=>{
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
||||
.upload-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 120px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.a-upload__text {
|
||||
bottom: 100px;
|
||||
right: 100px;
|
||||
font-weight: bold;
|
||||
color: #1d2027;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
color: #4f83fd;
|
||||
}
|
||||
</style>
|
||||
@ -8,6 +8,14 @@
|
||||
<a-button type="primary" @click="handleAuth">
|
||||
{{ t('功能授权') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleExport">
|
||||
{{ t('导出租户') }}
|
||||
</a-button>
|
||||
<a-button type="primary">
|
||||
<ImportModal importType="overrideMode">
|
||||
{{ t('导入租户') }}
|
||||
</ImportModal>
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
@ -43,10 +51,14 @@
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import TenantDrawer from './components/TenantDrawer.vue';
|
||||
import AuthModal from './components/AuthModal.vue';
|
||||
import ImportModal from './components/ImportModal.vue';
|
||||
import { Switch } from 'ant-design-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { deleteTenant, getTenantPageList, updateTenantStatus } from '/@/api/system/tenant';
|
||||
import { deleteTenant, getTenantPageList, updateTenantStatus,exportTenant } from '/@/api/system/tenant';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
@ -144,7 +156,7 @@
|
||||
const { notification } = useMessage();
|
||||
const [registerDrawer, { openModal }] = useModal();
|
||||
const [registerModal, { openModal: openAuthModal }] = useModal();
|
||||
const [registerTable, { reload, getSelectRowKeys }] = useTable({
|
||||
const [registerTable, { reload, getSelectRowKeys,getSelectRows }] = useTable({
|
||||
title: t('租户列表'),
|
||||
api: getTenantPageList,
|
||||
rowKey: 'id',
|
||||
@ -199,6 +211,23 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function handleExport(){
|
||||
if (getSelectRows().length === 0) {
|
||||
notification.warning({
|
||||
message: t('警告'),
|
||||
description: t('必须选中一行!'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
let selectedRow=getSelectRows()[0];
|
||||
let fileName="tenant_"+selectedRow.code+'_'+dateUtil(new Date()).format('YYYY-MM-DD_HH_mm_ss');
|
||||
const res = await exportTenant({id:selectedRow.id});
|
||||
downloadByData(
|
||||
res.data,
|
||||
fileName+".json"
|
||||
);
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
|
||||
288
src/views/system/user/components/RoleModalV2.vue
Normal file
288
src/views/system/user/components/RoleModalV2.vue
Normal 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为角色id,value为角色对象
|
||||
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>
|
||||
@ -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 新增
|
||||
|
||||
Reference in New Issue
Block a user