Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
xieyonghong 2023-03-24 13:51:09 +08:00
commit 570950c4e3
9 changed files with 514 additions and 369 deletions

View File

@ -34,8 +34,8 @@
)
: record.type === 'boolean'
? [
{ label: '是', value: true },
{ label: '否', value: false },
{ label: record?.dataType?.trueText, value: record?.dataType?.trueValue },
{ label: record?.dataType?.falseText, value: record?.dataType?.falseValue },
]
: undefined
"

View File

@ -15,7 +15,7 @@
</j-select>
</j-form-item>
<j-form-item label="选择产品" v-bind="validateInfos.copy" v-if="formModel.type === 'copy'">
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label"></j-select>
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label" filterable></j-select>
</j-form-item>
<j-form-item label="物模型类型" v-bind="validateInfos.metadata" v-if="type === 'device' || formModel.type === 'import'">
<j-select v-model:value="formModel.metadata">
@ -209,19 +209,20 @@ const handleImport = async () => {
const res = await convertMetadata('from', 'alink', data.import).catch(err => err)
if (res.status === 200) {
const metadata = operateLimits(res.result)
let result;
if (props?.type === 'device') {
await saveMetadata(id as string, metadata).catch(err => err)
// instanceStore.setCurrent(JSON.parse(metadata || '{}'))
result = await saveMetadata(id as string, metadata).catch(err => err)
} else {
await modify(id as string, { metadata: metadata }).catch(err => err)
// productStore.setCurrent(JSON.parse(metadata || '{}'))
result = await modify(id as string, { metadata: metadata }).catch(err => err)
}
if (result.success) {
message.success('导入成功')
}
loading.value = false
// MetadataAction.insert(JSON.parse(metadata || '{}'));
message.success('导入成功')
} else {
loading.value = false
message.error('发生错误!')
// message.error('!')
return
}
if (props?.type === 'device') {
instanceStore.refresh(id as string)

View File

@ -1,126 +1,173 @@
<template>
<div class="mangement-container">
<div class="left">
<j-input-search
v-model:value="leftData.searchValue"
placeholder="请输入"
style="margin-bottom: 24px"
/>
<!-- 使用v-if用于解决异步加载数据后不展开的问题 -->
<j-tree
v-if="leftData.treeData.length > 0"
showLine
defaultExpandAll
:tree-data="leftData.treeData"
v-model:selectedKeys="leftData.selectedKeys"
@select="leftData.onSelect"
>
<template #title="{ dataRef }">
<div
v-if="dataRef.root"
:style="`
justify-content: space-between;
display: flex;
align-items: center;
`"
>
<span>
<page-container>
<div class="manager-container">
<div class="left">
<j-input-search
v-model:value="leftData.searchValue"
placeholder="请输入"
style="margin-bottom: 24px"
/>
<!-- 使用v-if用于解决异步加载数据后不展开的问题 -->
<j-tree
v-if="leftData.treeData.length > 0"
showLine
defaultExpandAll
:tree-data="leftData.treeData"
v-model:selectedKeys="leftData.selectedKeys"
@select="onSelect"
>
<template #title="{ dataRef }">
<div
v-if="dataRef.root"
style="
justify-content: space-between;
display: flex;
align-items: center;
width: 200px;
"
>
<span>
{{ dataRef.title }}
</span>
<AIcon
type="PlusOutlined"
style="color: #1d39c4"
@click="addTable"
/>
</div>
<span v-else>
{{ dataRef.title }}
</span>
<AIcon
type="PlusOutlined"
style="color: #1d39c4"
@click="leftData.addTable"
/>
</div>
<span v-else>
{{ dataRef.title }}
</span>
</template>
</j-tree>
</div>
<div class="right">
<div class="btns">
<j-button type="primary" @click="table.clickSave"
>保存</j-button
>
</template>
</j-tree>
</div>
<j-pro-table
ref="tableRef"
:columns="table.columns"
model="TABLE"
:dataSource="table.data"
>
<template #name="slotProps">
<j-input
:disabled="slotProps.scale !== undefined"
v-model:value="slotProps.name"
placeholder="请输入名称"
:maxlength="64"
/>
</template>
<template #type="slotProps">
<j-input
v-model:value="slotProps.type"
placeholder="请输入类型"
:maxlength="64"
/>
</template>
<template #length="slotProps">
<j-input-number
v-model:value="slotProps.length"
:min="0"
:max="99999"
/>
</template>
<template #precision="slotProps">
<j-input-number
v-model:value="slotProps.precision"
:min="0"
:max="99999"
/>
</template>
<template #notnull="slotProps">
<j-radio-group
v-model:value="slotProps.notnull"
button-style="solid"
<div class="right">
<div class="btns">
<j-button type="primary" @click="clickSave">保存</j-button>
</div>
<j-form ref="formRef" :model="table">
<j-table
:columns="columns"
:dataSource="table.data"
:pagination="false"
:scroll="{ y: 500 }"
>
<j-radio-button :value="true"></j-radio-button>
<j-radio-button :value="false"></j-radio-button>
</j-radio-group>
</template>
<template #comment="slotProps">
<j-input
v-model:value="slotProps.comment"
placeholder="请输入说明"
/>
</template>
<template #action="slotProps">
<PermissionButton
:hasPermission="`{permission}:delete`"
type="link"
:tooltip="{ title: '删除' }"
:popConfirm="{
title: `确认删除`,
onConfirm: () => table.clickDel(slotProps),
}"
:disabled="slotProps.status"
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</template>
</j-pro-table>
<j-botton class="add-row" @click="table.addRow">
<AIcon type="PlusOutlined" /> 新增行
</j-botton>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'name'">
<j-form-item
:name="['data', index, 'name']"
:rules="[
{
max: 64,
message: '最多可输入64个字符',
},
{
required: true,
message: '请输入名称',
},
]"
>
<j-input
:disabled="record.scale !== undefined"
v-model:value="record.name"
placeholder="请输入名称"
/>
</j-form-item>
</template>
<template v-else-if="column.key === 'type'">
<j-form-item
:name="['data', index, 'type']"
:rules="[
{
max: 64,
message: '最多可输入64个字符',
},
{
required: true,
message: '请输入类型',
},
]"
>
<j-input
v-model:value="record.type"
placeholder="请输入类型"
/>
</j-form-item>
</template>
<template v-else-if="column.key === 'length'">
<j-form-item :name="['data', index, 'length']">
<j-input-number
v-model:value="record.length"
:min="0"
:max="99999"
style="width: 100%"
/>
</j-form-item>
</template>
<template v-else-if="column.key === 'precision'">
<j-form-item
:name="['data', index, 'precision']"
>
<j-input-number
v-model:value="record.precision"
:min="0"
:max="99999"
style="width: 100%"
/>
</j-form-item>
</template>
<template v-else-if="column.key === 'notnull'">
<j-form-item
:name="['data', index, 'notnull']"
>
<j-radio-group
v-model:value="record.notnull"
button-style="solid"
>
<j-radio-button :value="true"
></j-radio-button
>
<j-radio-button :value="false"
></j-radio-button
>
</j-radio-group>
</j-form-item>
</template>
<template v-else-if="column.key === 'comment'">
<j-form-item
:name="['data', index, 'notnull']"
>
<j-input
v-model:value="record.comment"
placeholder="请输入说明"
/>
</j-form-item>
</template>
<template v-else-if="column.key === 'action'">
<PermissionButton
hasPermission="system/DataSource:delete"
type="link"
:tooltip="{ title: '删除' }"
:danger="true"
:popConfirm="{
title: `确认删除`,
onConfirm: () => clickDel(record),
}"
:disabled="record.status"
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</template>
</template>
</j-table>
</j-form>
<j-button class="add-row" @click="addRow">
<AIcon type="PlusOutlined" /> 新增行
</j-button>
</div>
</div>
</div>
<div class="dialogs">
<j-modal
v-model:visible="dialog.visible"
title="新增"
@ok="dialog.handleOk"
>
<j-modal v-model:visible="dialog.visible" title="新增" @ok="handleOk">
<j-form :model="dialog.form" ref="addFormRef">
<j-form-item
label="名称"
@ -129,22 +176,22 @@
{
required: true,
message: '请输入名称',
trigger: 'change',
},
{
max: 64,
message: '最多可输入64个字符',
trigger: 'change',
trigger: 'blur',
},
{
pattern: /^[0-9].*$/,
message: '不能以数字开头',
// pattern: /^[0-9].*$/,
// message: '',
trigger: 'change',
validator: checkName,
},
{
pattern: /^\w+$/,
message: '名称只能由数字、字母、下划线、中划线组成',
trigger: 'change',
trigger: 'blur',
},
]"
>
@ -155,7 +202,7 @@
</j-form-item>
</j-form>
</j-modal>
</div>
</page-container>
</template>
<script setup lang="ts" name="Management">
@ -166,248 +213,290 @@ import {
saveTable_api,
delSaveRow_api,
} from '@/api/system/dataSource';
import { onlyMessage } from '@/utils/comm';
import { FormInstance, message } from 'ant-design-vue';
import { DataNode } from 'ant-design-vue/lib/tree';
import _ from 'lodash';
import { cloneDeep } from 'lodash';
import type { dbColumnType, dictItemType, sourceItemType } from '../typing';
const id = useRoute().query.id as string;
const columns = [
{
title: '列名',
dataIndex: 'name',
key: 'name',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
},
{
title: '长度',
dataIndex: 'length',
key: 'length',
},
{
title: '精度',
dataIndex: 'precision',
key: 'precision',
},
{
title: '不能为空',
dataIndex: 'notnull',
key: 'notnull',
},
{
title: '说明',
dataIndex: 'comment',
key: 'comment',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
},
];
const formRef = ref();
const getInfo = (_id: string) => {
getDataSourceInfo_api(_id).then((resp: any) => {
info.data = resp.result;
});
};
const info = reactive({
data: {} as sourceItemType,
init: () => {
id &&
getDataSourceInfo_api(id).then((resp: any) => {
info.data = resp.result;
});
},
});
const leftData = reactive({
searchValue: '',
sourceTree: [] as dictItemType[],
treeData: [] as DataNode[],
treeData: [] as any[],
selectedKeys: [] as string[],
oldKey: '',
init: () => {
leftData.getTree();
watch(
[
() => leftData.searchValue,
() => leftData.sourceTree,
() => info.data,
],
(n) => {
if (leftData.sourceTree.length < 1 || !info.data.shareConfig)
return;
let filterArr = [];
if (leftData.searchValue) {
filterArr = leftData.sourceTree.filter((item) =>
item.name.includes(n[0]),
);
} else filterArr = leftData.sourceTree;
leftData.treeData = [
{
title: info.data.shareConfig.schema,
key: info.data.shareConfig.schema,
root: true,
children: filterArr.map((item) => ({
title: item.name,
key: item.name,
})),
},
];
leftData.selectedKeys = [filterArr[0].name];
leftData.onSelect([filterArr[0].name]);
},
{},
);
},
getTree: () => {
rdbTree_api(id)
.then((resp: any) => {
leftData.sourceTree = resp.result;
})
.catch(() => {});
},
onSelect: (selectedKeys: string[], e?: any) => {
if (e?.node?.root) {
leftData.selectedKeys = [leftData.oldKey];
return;
}
leftData.oldKey = selectedKeys[0];
const key = selectedKeys[0];
table.getTabelData(key);
},
addTable: (e: Event) => {
e.stopPropagation();
},
});
const table = reactive({
columns: [
{
title: '列名',
dataIndex: 'name',
key: 'name',
scopedSlots: true,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
scopedSlots: true,
},
{
title: '长度',
dataIndex: 'length',
key: 'length',
scopedSlots: true,
},
{
title: '精度',
dataIndex: 'precision',
key: 'precision',
scopedSlots: true,
},
{
title: '不能为空',
dataIndex: 'notnull',
key: 'notnull',
scopedSlots: true,
},
{
title: '说明',
dataIndex: 'comment',
key: 'comment',
scopedSlots: true,
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
},
],
data: [] as dbColumnType[],
getTabelData: (key: string) => {
const queryTables = (key: string) => {
if (key) {
rdbTables_api(id, key).then((resp: any) => {
table.data = resp.result.columns.map(
(item: object, index: number) => ({ ...item, index }),
(item: object, index: number) => ({
...item,
index,
}),
);
});
},
addRow: () => {
const initData: dbColumnType = {
precision: 0,
length: 0,
notnull: false,
type: '',
comment: '',
name: '',
};
table.data.push(initData);
},
clickSave: () => {
const columns = cloneDeep(table.data);
columns.forEach((item) => delete item.index);
const params = {
name: leftData.selectedKeys[0],
columns,
};
saveTable_api(id, params).then((resp) => {
}
};
const handleSearch = (refresh?: boolean) => {
rdbTree_api(id)
.then((resp: any) => {
if (resp.status === 200) {
message.success('操作成功');
table.getTabelData(params.name);
leftData.sourceTree = resp.result;
if (refresh) {
leftData.selectedKeys = [resp.result[0]?.name];
queryTables(resp.result[0]?.name);
} else {
queryTables(leftData.selectedKeys[0]);
}
}
});
})
.catch(() => {});
};
const onSelect = (selectedKeys: string[], e?: any) => {
if (e?.node?.root) {
leftData.selectedKeys = [leftData.oldKey];
return;
}
if (!selectedKeys[0]) {
return;
}
leftData.oldKey = selectedKeys[0];
const key = selectedKeys[0];
queryTables(key);
};
const addTable = (e: Event) => {
e?.stopPropagation();
dialog.visible = true;
};
watch(
() => id,
(newId) => {
if (newId) {
getInfo(newId);
handleSearch(true);
}
},
clickDel: (row: any) => {
if (row.scale !== undefined) {
delSaveRow_api(id, leftData.selectedKeys[0], [row.name]).then(
(resp: any) => {
if (resp.status === 200) table.data.splice(row.index, 1);
},
);
} else table.data.splice(row.index, 1);
{
immediate: true,
},
);
const table = reactive({
data: [] as dbColumnType[],
});
const addRow = () => {
const initData: dbColumnType = {
precision: 0,
length: 0,
notnull: false,
type: '',
comment: '',
name: '',
};
table.data.push(initData);
};
const clickDel = (row: any) => {
if (row.scale !== undefined) {
delSaveRow_api(id, leftData.selectedKeys[0], [row.name]).then(
(resp: any) => {
if (resp.status === 200) table.data.splice(row.index, 1);
},
);
} else table.data.splice(row.index, 1);
};
const clickSave = () => {
formRef.value.validate().then((_data: any) => {
console.log(_data)
// const columns = cloneDeep(table.data);
// columns.forEach((item) => delete item.index);
// if (!columns.length) {
// onlyMessage('', 'error');
// return;
// }
// const params = {
// name: leftData.selectedKeys[0],
// columns,
// };
// saveTable_api(id, params).then((resp) => {
// if (resp.status === 200) {
// message.success('');
// queryTables(params.name);
// }
// });
});
};
const addFormRef = ref<FormInstance>();
const dialog = reactive({
visible: false,
form: {
name: '',
},
handleOk: () => {
addFormRef.value &&
addFormRef.value.validate().then(() => {
const name = dialog.form.name;
leftData.sourceTree.unshift({
id: name,
name,
});
leftData.oldKey = name;
leftData.selectedKeys = [name];
table.data = [];
});
},
});
init();
function init() {
info.init();
leftData.init();
}
const handleOk = () => {
addFormRef.value &&
addFormRef.value.validate().then(() => {
const name = dialog.form.name;
leftData.sourceTree.unshift({
id: name,
name,
});
leftData.oldKey = name;
leftData.selectedKeys = [name];
table.data = [];
dialog.visible = false;
addFormRef.value?.resetFields();
});
};
watch(
[() => leftData.searchValue, () => leftData.sourceTree],
([m, n]) => {
if (!!m) {
const list = n.filter((item) => {
return item.name.includes(m);
});
leftData.treeData = [
{
title: info.data.shareConfig.schema,
key: info.data.shareConfig.schema,
root: true,
children: list.map((item) => ({
title: item.name,
key: item.name,
})),
},
];
if (!_.map(list, 'name').includes(leftData.selectedKeys[0])) {
leftData.selectedKeys = [list[0]?.name];
queryTables(list[0]?.name);
}
} else {
leftData.treeData = [
{
title: info.data.shareConfig.schema,
key: info.data.shareConfig.schema,
root: true,
children: leftData.sourceTree.map((item) => ({
title: item.name,
key: item.name,
})),
},
];
}
},
{ deep: true },
);
const checkName = (_: any, value: any) =>
new Promise((resolve, reject) => {
if (value) {
const first = value.slice(0, 1);
if (typeof Number(first) === 'number' && !isNaN(Number(first))) {
reject('不能以数字开头');
} else {
resolve('');
}
}
});
</script>
<style lang="less" scoped>
.mangement-container {
margin: 24px;
.manager-container {
padding: 24px;
background-color: #fff;
display: flex;
min-height: 500px;
.left {
flex-basis: 280px;
padding-right: 24px;
padding: 0 24px;
box-sizing: border-box;
:deep(.ant-tree-treenode) {
width: 100%;
.ant-tree-switcher-noop {
display: none;
}
.ant-tree-node-content-wrapper {
width: 100%;
.ant-tree-title {
width: 100%;
}
}
&:first-child .ant-tree-node-selected {
background-color: transparent;
}
}
}
.right {
width: calc(100% - 280px);
box-sizing: border-box;
border-left: 1px solid #f0f0f0;
padding-left: 24px;
.btns {
display: flex;
justify-content: right;
padding: 0px 24px;
}
.add-row {
display: block;
text-align: center;
width: 100%;
margin: 24px 0;
cursor: pointer;
}
.ant-form-item {
margin-bottom: 0;
}
}
}
</style>

View File

@ -6,7 +6,7 @@
:title="dialogTitle"
:confirmLoading="loading"
@ok="confirm"
@cancel="emits('update:visible', false)"
@cancel="emits('cancel')"
>
<j-form ref="formRef" :model="form.data" layout="vertical">
<j-row :gutter="24">
@ -51,7 +51,7 @@
message: '请输入URL',
trigger: 'change',
},
{ validator: form.checkUrl, trigger: 'blur' },
{ validator: checkUrl, trigger: 'blur' },
]"
>
<j-input
@ -193,9 +193,8 @@ import { FormInstance, message } from 'ant-design-vue';
import { Rule } from 'ant-design-vue/lib/form';
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
const emits = defineEmits(['confirm', 'update:visible']);
const emits = defineEmits(['confirm', 'cancel']);
const props = defineProps<{
visible: boolean;
data: sourceItemType;
}>();
//
@ -203,19 +202,25 @@ const dialogTitle = computed(() =>
props.data.id ? '编辑数据源' : '新增数据源',
);
const loading = ref(false);
const confirm = () => {
loading.value = true;
formRef.value
?.validate()
.then(() => form.submit())
.then((resp: any) => {
if (resp.status === 200) {
message.success('操作成功');
emits('confirm');
emits('update:visible', false);
}
})
.finally(() => (loading.value = false));
const checkUrl = (_rule: Rule, value: string): Promise<any> => {
if (!value) return Promise.reject('请输入URL');
const arr = value.split(':');
if (arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') {
return Promise.resolve();
} else {
return Promise.reject('请输入正确的URL');
}
};
const getTypeOption = () => {
getDataTypeDict_api().then((resp: any) => {
const result = resp.result as dictItemType[];
form.typeOptions = result.map((item) => ({
label: item.name,
value: item.id,
}));
});
};
const formRef = ref<FormInstance>();
@ -223,30 +228,38 @@ const form = reactive({
data: {
...props.data,
} as sourceItemType,
typeOptions: [] as optionItemType[],
checkUrl: (_rule: Rule, value: string): Promise<any> => {
if (!value) return Promise.reject('请输入URL');
const arr = value.split(':');
if (arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') {
return Promise.resolve();
} else {
return Promise.reject('请输入正确的URL');
}
},
getTypeOption: () => {
getDataTypeDict_api().then((resp: any) => {
const result = resp.result as dictItemType[];
form.typeOptions = result.map((item) => ({
label: item.name,
value: item.id,
}));
});
},
submit: () => {
return saveDataSource_api(form.data);
},
});
form.getTypeOption();
watch(
() => props.data,
(newValue) => {
form.data = {...newValue, shareConfig: { ...newValue?.shareConfig }}
},
{
immediate: true,
deep: true
},
);
onMounted(() => {
getTypeOption();
})
const confirm = () => {
loading.value = true;
formRef.value
?.validate()
.then(async (_data: any) => {
const resp = await saveDataSource_api({ ...props.data, ..._data });
if (resp.status === 200) {
message.success('操作成功');
emits('confirm');
formRef.value?.resetFields()
}
})
.finally(() => {
loading.value = false;
});
};
</script>

View File

@ -14,8 +14,13 @@
model="TABLE"
:params="queryParams"
:defaultParams="{
pageSize: 10,
sorts: [{ name: 'createTime', order: 'desc' }],
}"
:pagination="{
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
}"
>
<template #headerTitle>
<PermissionButton
@ -31,7 +36,7 @@
:status="slotProps.state?.value"
:text="slotProps.state?.text"
:statusNames="{
enabled: 'success',
enabled: 'processing',
disabled: 'error',
}"
>
@ -116,6 +121,7 @@
? '请先禁用,再删除'
: '删除',
}"
:danger="true"
:popConfirm="{
title: `确认删除`,
onConfirm: () => table.clickDel(slotProps),
@ -130,7 +136,7 @@
<EditDialog
v-if="dialog.visible"
v-model:visible="dialog.visible"
@cancel="table.cancel"
:data="dialog.selectItem"
@confirm="table.refresh"
/>
@ -211,7 +217,7 @@ const columns = [
value: 'enabled',
},
{
label: '禁用',
label: '禁用',
value: 'disabled',
},
],
@ -277,7 +283,13 @@ const table = {
//
refresh: () => {
tableRef.value.reload();
dialog.visible = false
dialog.selectItem = {}
},
cancel: () => {
dialog.visible = false
dialog.selectItem = {}
}
};
table.getTypeOption();

View File

@ -8,12 +8,13 @@
visible
@cancel="emits('update:visible', false)"
>
<a-alert
message="只能分配有'共享'权限的资产数据"
type="warning"
show-icon
/>
<div style="margin-top: 5px;">
<div class="alert-info">
<j-space>
<AIcon type="ExclamationCircleOutlined" />
<span>只能分配有'共享'权限的资产数据</span>
</j-space>
</div>
<div style="margin-top: 5px">
<span>资产权限</span>
<j-checkbox-group
v-model:value="form.permission"
@ -67,4 +68,11 @@ const options = computed(() => {
});
</script>
<style scoped></style>
<style scoped lang="less">
.alert-info {
background: #f3f3f3;
border-radius: 2px;
padding: 6px;
color: rgba(0, 0, 0, 0.55);
}
</style>

View File

@ -83,7 +83,7 @@
<div
class="pager"
v-if="
requestBody.params.paramsTable.length &&
requestBody.params.paramsTable.length > 10 &&
requestBody.pageSize
"
>

View File

@ -83,10 +83,27 @@ const rowSelection = {
selectedRowKeys: ref<string[]>([]),
};
const save = async () => {
const keys = props.selectedRowKeys;
// fix: #bug10828
// id
const currenTableKeys = props.tableData.map((m: any) => m.id);
// id
const currentSelectedKeys = rowSelection.selectedRowKeys.value;
// , id
const oldSelectedKeys = currenTableKeys.filter((key) =>
props.sourceKeys.includes(key),
);
const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key));
const addKeys = keys.filter((key) => !props.sourceKeys.includes(key));
// const keys = props.selectedRowKeys;
// const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key));
// const addKeys = keys.filter((key) => !props.sourceKeys.includes(key));
//
const removeKeys = oldSelectedKeys.filter(
(key) => !currentSelectedKeys.includes(key),
);
//
const addKeys = currentSelectedKeys.filter(
(key) => !oldSelectedKeys.includes(key),
);
if (props.mode === 'api') {
// api

View File

@ -117,9 +117,12 @@
show-search
style="width: 100%"
placeholder="请选择组织"
multiple
:tree-data="form.departmentOptions"
:fieldNames="{ label: 'name', value: 'id' }"
multiple
:filterTreeNode="
(v: string, node: any) => filterSelectNode(v, node, 'name')
"
>
<template #title="{ name }">
{{ name }}
@ -195,6 +198,7 @@ import { Rule } from 'ant-design-vue/es/form';
import { DefaultOptionType } from 'ant-design-vue/es/vc-tree-select/TreeSelect';
import { AxiosResponse } from 'axios';
import { passwordRegEx } from '@/utils/validate';
import { filterSelectNode } from '@/utils/comm';
const deptPermission = 'system/Department';
const rolePermission = 'system/Role';
@ -250,7 +254,8 @@ const form = reactive({
if (!value) return reject('请输入密码');
else if (value.length > 64) return reject('最多可输入64个字符');
else if (value.length < 8) return reject('密码不能少于8位');
else if (!passwordRegEx(value)) return reject('密码必须包含大小写英文和数字');
else if (!passwordRegEx(value))
return reject('密码必须包含大小写英文和数字');
validateField_api('password', value).then((resp: any) => {
resp.result.passed
? resolve('')