<script lang="ts" setup>
|
import type { TreeActionType } from '@jnpf/ui';
|
import type { BasicColumn } from '@jnpf/ui/vxeTable';
|
import type { FormInstance } from 'ant-design-vue';
|
|
import { computed, nextTick, reactive, ref, toRefs, unref, watch } from 'vue';
|
|
import { BasicLeftTree } from '@jnpf/ui';
|
import { ModalClose } from '@jnpf/ui/modal';
|
|
import { Modal as AModal, Form } from 'ant-design-vue';
|
import { cloneDeep, pick } from 'lodash-es';
|
|
import { getAuthSystemList } from '#/api/system/system';
|
import { getFlowEngineListByIds, getFlowSelector } from '#/api/workFlow/template';
|
import { $t } from '#/locales';
|
import { useBaseStore } from '#/store';
|
|
interface State {
|
list: any[];
|
listQuery: any;
|
loading: boolean;
|
total: number;
|
selectedRowKeys: any[];
|
selectedRows: any[];
|
innerValue: string | string[] | undefined;
|
options: any[];
|
visible: boolean;
|
treeLoading: boolean;
|
treeData: any[];
|
selectRow: any;
|
}
|
|
defineOptions({ inheritAttrs: false });
|
const props = defineProps({
|
value: { default: [] },
|
popupTitle: { type: String, default: '选择流程' },
|
placeholder: { type: String, default: '请选择' },
|
disabled: { type: Boolean, default: false },
|
allowClear: { type: Boolean, default: true },
|
multiple: { type: Boolean, default: true },
|
size: { type: String, default: 'default' },
|
entrustType: { type: Number, default: 0 },
|
isGlobal: { type: Boolean, default: true },
|
});
|
const emit = defineEmits(['update:value', 'change']);
|
const state = reactive<State>({
|
list: [],
|
listQuery: {
|
keyword: '',
|
currentPage: 1,
|
pageSize: 20,
|
},
|
loading: false,
|
total: 0,
|
selectedRowKeys: [],
|
selectedRows: [],
|
innerValue: undefined,
|
options: [],
|
visible: false,
|
treeLoading: false,
|
treeData: [],
|
selectRow: null,
|
});
|
const { innerValue, options, visible, treeLoading, treeData, selectRow } = toRefs(state);
|
const formItemContext = Form.useInjectFormItemContext();
|
const baseStore = useBaseStore();
|
const columns: BasicColumn[] = [
|
{ title: '流程名称', dataIndex: 'fullName' },
|
{ title: '流程编码', dataIndex: 'enCode' },
|
];
|
const searchInfo = reactive({
|
category: '',
|
systemId: '',
|
isDelegate: 1,
|
isAuthority: 1,
|
delegateUser: '',
|
});
|
const leftTreeRef = ref<Nullable<TreeActionType>>(null);
|
const formElRef = ref<FormInstance>();
|
const tableElRef = ref<any>(null);
|
const scrollHeight = window.innerHeight * 0.7 - 52 - 39 - 56;
|
|
const getSelectBindValue = computed(() => {
|
return {
|
...pick(props, ['disabled', 'size', 'allowClear', 'placeholder']),
|
fieldNames: { label: 'fullName', value: 'id' },
|
open: false,
|
mode: props.multiple ? 'multiple' : '',
|
showSearch: false,
|
showArrow: true,
|
};
|
});
|
const getRowSelection = computed<any>(() => ({
|
type: props.multiple ? 'checkbox' : 'radio',
|
selectedRowKeys: state.selectedRowKeys,
|
onChange: setSelectedRowKeys,
|
}));
|
const getTableBindValues = computed(() => {
|
return {
|
columns,
|
rowSelection: unref(getRowSelection),
|
customRow,
|
loading: state.loading,
|
size: 'small',
|
rowKey: 'id',
|
scroll: {
|
y: scrollHeight,
|
},
|
pagination: {
|
current: state.listQuery.currentPage,
|
pageSize: state.listQuery.pageSize,
|
size: 'small',
|
defaultPageSize: state.listQuery.pageSize,
|
showTotal: (total) => $t('component.table.total', { total }),
|
showSizeChanger: true,
|
pageSizeOptions: ['20', '50', '80', '100'],
|
showQuickJumper: true,
|
total: state.total,
|
},
|
class: 'jnpf-basic-table',
|
};
|
});
|
|
watch(
|
() => props.value,
|
() => {
|
setValue();
|
},
|
{ immediate: true },
|
);
|
|
function customRow(record: Recordable) {
|
return {
|
onClick: (e: Event) => {
|
e?.stopPropagation();
|
const tr: HTMLElement = (e as MouseEvent).composedPath?.().find((dom: any) => dom.tagName === 'TR') as HTMLElement;
|
if (!tr) return;
|
const isCheckbox = unref(getTableBindValues).rowSelection.type === 'checkbox';
|
const key = record[unref(getTableBindValues).rowKey];
|
if (isCheckbox) {
|
// 找到Checkbox,检查是否为disabled
|
const checkBox = tr.querySelector('input[type=checkbox]');
|
if (!checkBox || checkBox.hasAttribute('disabled')) return;
|
if (state.selectedRowKeys.includes(key)) {
|
const keyIndex = state.selectedRowKeys.indexOf(key);
|
state.selectedRowKeys.splice(keyIndex, 1);
|
state.selectedRows = state.selectedRows.filter((o) => o[unref(getTableBindValues).rowKey] !== key);
|
} else {
|
state.selectedRowKeys.push(key);
|
state.selectedRows.push(record);
|
}
|
} else {
|
// 找到radio,检查是否为disabled
|
const radio = tr.querySelector('input[type=radio]');
|
if (!radio || radio.hasAttribute('disabled')) return;
|
state.selectedRowKeys = [key];
|
state.selectedRows = [record];
|
}
|
},
|
};
|
}
|
function setValue() {
|
if (!props.value || !props.value.length) {
|
state.innerValue = [];
|
state.options = [];
|
state.selectRow = null;
|
state.selectedRowKeys = [];
|
state.selectedRows = [];
|
return;
|
}
|
const ids = props.value as any[];
|
getFlowEngineListByIds(ids).then((res) => {
|
const selectedList: any[] = res.data;
|
const innerIds = selectedList.map((o) => o.id);
|
state.innerValue = innerIds;
|
state.options = cloneDeep(selectedList);
|
state.selectRow = cloneDeep(selectedList);
|
state.selectedRowKeys = innerIds;
|
state.selectedRows = cloneDeep(selectedList);
|
});
|
}
|
function onChange(val, option) {
|
if (!val || !val.length) {
|
state.options = [];
|
emit('update:value', []);
|
emit('change', '', []);
|
state.selectedRowKeys = [];
|
state.selectedRows = [];
|
} else {
|
state.options = option;
|
emit('update:value', val);
|
emit('change', '', state.options);
|
}
|
formItemContext.onFieldChange();
|
}
|
async function openSelectModal() {
|
if (props.disabled) return;
|
state.visible = true;
|
getCategoryData();
|
}
|
async function getCategoryData() {
|
const firstItem = { id: '', encode: 'all', fullName: '全部流程' };
|
if (props.isGlobal) {
|
state.treeLoading = true;
|
const res = await getAuthSystemList();
|
if (!res) return (state.treeLoading = false);
|
state.treeData = [firstItem, ...(res.data || [])];
|
state.treeLoading = false;
|
searchInfo.category = '';
|
searchInfo.systemId = state.treeData[0].id;
|
} else {
|
const res = (await baseStore.getDictionaryData('businessType')) as any[];
|
state.treeData = [firstItem, ...res];
|
searchInfo.category = state.treeData[0].id;
|
searchInfo.systemId = '';
|
}
|
searchInfo.isAuthority = props.entrustType === 0 ? 1 : 0;
|
const leftTree = unref(leftTreeRef);
|
leftTree?.setSelectedKeys([searchInfo.category]);
|
state.loading = true;
|
handleReset();
|
const tableEl = tableElRef.value?.$el;
|
const bodyEl = tableEl.querySelector('.ant-table-body');
|
bodyEl!.style.height = `${scrollHeight}px`;
|
}
|
function initData() {
|
state.loading = true;
|
const query = {
|
...state.listQuery,
|
...unref(searchInfo),
|
isLaunch: 1,
|
};
|
getFlowSelector(query)
|
.then((res) => {
|
state.list = res.data.list || [];
|
state.total = res.data.pagination.total;
|
state.loading = false;
|
})
|
.catch(() => {
|
state.loading = false;
|
});
|
}
|
function handleTreeSelect(id) {
|
if (props.isGlobal) {
|
if (searchInfo.systemId === id) return;
|
searchInfo.systemId = id;
|
} else {
|
if (searchInfo.category === id) return;
|
searchInfo.category = id;
|
}
|
nextTick(() => initData());
|
}
|
function handleCancel() {
|
state.visible = false;
|
}
|
function getForm() {
|
const form = unref(formElRef);
|
if (!form) {
|
throw new Error('form is null!');
|
}
|
return form;
|
}
|
function handleSearch() {
|
state.listQuery.currentPage = 1;
|
state.listQuery.pageSize = 20;
|
updateSelectRow();
|
nextTick(() => initData());
|
}
|
function handleReset() {
|
getForm().resetFields();
|
state.listQuery.keyword = '';
|
handleSearch();
|
}
|
function setSelectedRowKeys(selectedRowKeys, selectedRows) {
|
state.selectedRowKeys = selectedRowKeys;
|
state.selectedRows = selectedRows;
|
}
|
function handleTableChange(pagination) {
|
state.listQuery.currentPage = pagination.current;
|
state.listQuery.pageSize = pagination.pageSize;
|
updateSelectRow();
|
nextTick(() => initData());
|
}
|
function updateSelectRow() {
|
if (!state.selectRow) state.selectRow = [];
|
const newSelectRow = state.selectedRows;
|
for (const item of newSelectRow) {
|
if (!state.selectRow.some((o) => o.id === item.id)) state.selectRow.push(item);
|
}
|
state.selectRow = state.selectRow.filter((o) => !(state.list.some((l) => l.id === o.id) && !newSelectRow.some((l) => l.id === o.id)));
|
}
|
function handleSubmit() {
|
updateSelectRow();
|
state.options = state.selectRow;
|
state.innerValue = unref(selectRow).map((o) => o.id);
|
emit('update:value', unref(innerValue));
|
emit('change', unref(innerValue), unref(selectRow));
|
formItemContext.onFieldChange();
|
handleCancel();
|
}
|
</script>
|
|
<template>
|
<div class="common-container">
|
<a-select v-model:value="innerValue" v-bind="getSelectBindValue" :options="options" @change="onChange" @click="openSelectModal" />
|
<AModal
|
v-model:open="visible"
|
:title="popupTitle"
|
:width="1000"
|
class="common-container-modal"
|
@ok="handleSubmit"
|
@cancel="handleCancel"
|
:mask-closable="false">
|
<template #closeIcon>
|
<ModalClose :can-fullscreen="false" @cancel="handleCancel" />
|
</template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-left">
|
<BasicLeftTree ref="leftTreeRef" :show-search="false" :tree-data="treeData" :loading="treeLoading" @select="handleTreeSelect" />
|
</div>
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-content">
|
<div class="jnpf-common-search-box jnpf-common-search-box-modal">
|
<a-form :colon="false" label-align="right" :model="state.listQuery" ref="formElRef">
|
<a-row :gutter="10">
|
<a-col :span="8">
|
<a-form-item :label="$t('common.keyword')" name="keyword">
|
<a-input v-model:value="state.listQuery.keyword" :placeholder="$t('common.enterKeyword')" allow-clear @press-enter="handleSearch" />
|
</a-form-item>
|
</a-col>
|
<a-col :span="8">
|
<a-form-item label=" ">
|
<a-button type="primary" class="mr-2" @click="handleSearch">{{ $t('common.queryText') }}</a-button>
|
<a-button @click="handleReset">{{ $t('common.resetText') }}</a-button>
|
</a-form-item>
|
</a-col>
|
</a-row>
|
</a-form>
|
</div>
|
<a-table :data-source="state.list" v-bind="getTableBindValues" @change="handleTableChange" ref="tableElRef" />
|
</div>
|
</div>
|
</div>
|
</AModal>
|
</div>
|
</template>
|