diff --git a/src/api/system/dept/index.js b/src/api/system/dept/index.js
new file mode 100644
index 0000000..148f644
--- /dev/null
+++ b/src/api/system/dept/index.js
@@ -0,0 +1,60 @@
+import { request } from '@/utils/request'
+
+export function getDeptList() {
+ return request({
+ url: 'sys/dept/list',
+ method: 'get'
+ })
+}
+
+export function moveDeptList(data) {
+ return request({
+ url: 'sys/dept/move',
+ method: 'post',
+ data
+ })
+}
+
+export function deleteDept(data) {
+ return request({
+ url: 'sys/dept/delete',
+ method: 'post',
+ data
+ }, {
+ successMsg: '删除成功'
+ })
+}
+
+
+export function updateDept(data) {
+ return request({
+ url: 'sys/dept/update',
+ method: 'post',
+ data,
+ });
+}
+
+export function createDept(data) {
+ return request({
+ url: 'sys/dept/add',
+ method: 'post',
+ data
+ })
+}
+
+
+export function getDeptInfo(params) {
+ return request({
+ url: 'sys/dept/info',
+ method: 'get',
+ params
+ })
+}
+
+export function transferDept(data) {
+ return request({
+ url: 'sys/dept/transfer',
+ method: 'post',
+ data
+ })
+}
diff --git a/src/hooks/useModal/index.js b/src/hooks/useModal/index.js
index b1c6ea4..9162760 100644
--- a/src/hooks/useModal/index.js
+++ b/src/hooks/useModal/index.js
@@ -1 +1,9 @@
-export default {}
+import { installUseModal } from './useModal'
+
+// 导出
+export { useFormModal } from './useFromModal'
+export { useModal } from './useFormModal'
+export const install = (app) => {
+ installUseModal(app)
+}
+export default install
diff --git a/src/hooks/useModal/modal.jsx b/src/hooks/useModal/modal.jsx
new file mode 100644
index 0000000..18abcc0
--- /dev/null
+++ b/src/hooks/useModal/modal.jsx
@@ -0,0 +1,8 @@
+import { installUseModal, useModal } from './useModal'
+// export { useFormModal } from './useFromModal'
+export const install = (app) => {
+ installUseModal(app)
+}
+
+
+export default install
diff --git a/src/hooks/useModal/useFormModal.jsx b/src/hooks/useModal/useFormModal.jsx
new file mode 100644
index 0000000..4411fa5
--- /dev/null
+++ b/src/hooks/useModal/useFormModal.jsx
@@ -0,0 +1,43 @@
+import { omit } from 'lodash-es'
+import { nextTick, ref, unref } from "vue"
+import SchemaForm from '@/components/core/schema-form'
+
+export function useFormModal() {
+ const [ModalRender] = useModal()
+
+ const showModal = async ({modalProps, formProps}) => {
+ const formRef = ref()
+ const onCancel = (e) => {
+ formRef.value?.resetFields()
+ modalProps?.onCancel?.(e)
+ }
+
+ await ModalRender.show({
+ destroyOnClose: true,
+ ...omit(modalProps, ['onFinish', 'onFail']),
+ onCancel,
+ content: () => {
+ const _formProps = Object.assign({}, { showActionButtonGroup: false}, formProps)
+
+ return
+ },
+ onOk: async () => {
+ let values;
+ try {
+ values = await formRef.value?.validate();
+ await modalProps?.onFinish?.(values);
+ formRef.value?.resetFields()
+ } catch(error) {
+ modalProps?.onFail?.(values)
+ return Promise.reject(error)
+ }
+ }
+ })
+
+ await nextTick()
+
+ return [unref(formRef)]
+ }
+
+ return [showModal, ModalRender]
+}
diff --git a/src/hooks/useModal/useModal.jsx b/src/hooks/useModal/useModal.jsx
new file mode 100644
index 0000000..aecc29d
--- /dev/null
+++ b/src/hooks/useModal/useModal.jsx
@@ -0,0 +1,68 @@
+import { createVNode, nextTick,getCurrentInstance, render } from "vue";
+import { MyModal } from './modal'
+let _app;
+
+export const useModal = () => {
+ let _modalInstallce;
+ const modalRef = ref();
+ const appContent = _app?._conotent || getCurrentInstance()?.appContent; // TODO: 这里的作用
+ const isAppChild = ref(false);
+
+ const getModalInstance = async () => {
+ await nextTick()
+ if(isAppChild.value && modalRef.value) {
+ return modalRef.value
+ }
+
+ if(_modalInstallce) {
+ return _modalInstallce
+ }
+
+ const container = document.createElement('div')
+ const vnode = createVNode(MyModal)
+ vnode.appContext = appContent
+ render(vnode, container)
+
+ _modalInstallce = vnode.component;
+ _modalInstallce.props.closeModal = hide;
+ return _modalInstallce;
+ }
+
+ const setProps = async (_props) => {
+ const instance = await getModalInstance()
+ if(Object.is(instance, modalRef.value)) {
+ instance?.exposed?.setProps?.(_props)
+ } else {
+ instance?.exposed?.setProps?.(_props)
+ }
+ }
+
+ const hide = () => {
+ setProps({visible: false})
+ }
+
+ const show = async (props) => {
+ setProps({
+ ...props,
+ closeModal: hide,
+ visible: true
+ })
+ }
+
+ await nextTick()
+
+ const ModalRender = (props, { attrs, slots}) => {
+ isAppChild.value = true
+ return
+ }
+
+ ModalRender.show = show
+ ModalRender.hide = hide
+ ModalRender.setProps = setProps
+
+ return [ModalRender, modalRef]
+}
+
+export const installUseModal = (app) => {
+ _app = app
+}
diff --git a/src/utils/index.js b/src/utils/index.js
index 9391abd..4a1687a 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -15,6 +15,83 @@ export function uniqueSlash(path) {
}
-export function formatMenu2Tree() {
+/**
+ * 渲染菜单至树形控件
+ * @param {Array} menus 所有菜单
+ * @param {Number | null} parentId 父级菜单ID
+ * @param {number[]|string[]} keyPath ID路径
+ */
+
+export function formatMenu2Tree(menus, parentId, keyPath = []) {
+ return menus
+ .filter((item) => item.parentId === parentId)
+ .map((item) => {
+ const _keyPath = keyPath.concat(parentId || []);
+ const arr = formatMenu2Tree(menus, item.id, _keyPath);
+ return Object.assign(item, {
+ keyPath: _keyPath,
+ title: item.name,
+ key: item.id,
+ value: item.id,
+ formData: item,
+ children: arr.length ? arr : null,
+ });
+ });
}
+
+/*
+ * 渲染部门至树形控件
+ * @param {Array} depts 所有部门
+ * @param {Number | null} parentId 父级部门ID
+ * @param {number[]|string[]} keyPath ID路径
+ */
+export const formatDept2Tree = (
+ depts,
+ parentId,
+ keyPath = [],
+) => {
+ return depts
+ .filter((item) => item.parentId === parentId)
+ .map((item) => {
+ const _keyPath = keyPath.concat(parentId || []);
+ const arr = formatDept2Tree(depts, item.id, _keyPath);
+ return Object.assign(item, {
+ keyPath: _keyPath,
+ title: item.name,
+ key: item.id,
+ value: item.id,
+ formData: item,
+ children: arr.length ? arr : null,
+ });
+ });
+};
+
+
+
+/**
+ * 在树中根据ID找child
+ * @param {string|number} id
+ * @param {any[]} treeData 树形数据
+ * @param {string} keyName 指定ID的属性名,默认是id
+ * @param {string} children 指定children的属性名,默认是children
+ */
+
+export const findChildById = (
+ id,
+ treeData = [],
+ keyName = 'id',
+ children = 'children',
+) => {
+ return treeData.reduce((prev, curr) => {
+ if (curr[keyName] === id) {
+ return curr;
+ }
+ if (prev) {
+ return prev;
+ }
+ if (curr[children]?.length) {
+ return findChildById(id, curr[children], keyName, children);
+ }
+ }, undefined);
+};
diff --git a/src/views/system/permission/user/index.bat.vue b/src/views/system/permission/user/index.bat.vue
new file mode 100644
index 0000000..7b71132
--- /dev/null
+++ b/src/views/system/permission/user/index.bat.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/system/permission/user/index.vue b/src/views/system/permission/user/index.vue
index 7b71132..814a242 100644
--- a/src/views/system/permission/user/index.vue
+++ b/src/views/system/permission/user/index.vue
@@ -1,5 +1,6 @@