feat: 对接系统配置中的组织架构90%

This commit is contained in:
Zzz1zz
2025-06-28 00:10:30 +08:00
parent c5c61f0573
commit f6e4159889
16 changed files with 901 additions and 49 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# 开发环境配置
VITE_APP_TITLE=租赁管理系统
VITE_API_BASE_URL=http://localhost:9500/api/
VITE_API_BASE_URL=https://go.kola.work/api
VITE_APP_MODE=development
# 调试相关
+16 -2
View File
@@ -1,6 +1,6 @@
import axios from 'axios';
import type { RouteRecordNormalized } from 'vue-router';
import { UserState } from '@/store/modules/user/types';
// import type { RouteRecordNormalized } from 'vue-router';
// import { UserState } from '@/store/modules/user/types';
export interface ProjectRecord {
id: string;
@@ -12,3 +12,17 @@ export interface ProjectParams extends Partial<ProjectRecord> {
current: number;
pageSize: number;
}
// 全部项目列表
export function getAllProjectList() {
return axios.get<any>('/project/getAllProjectList');
}
// 项目信息
export function getProjectDetail(id: string) {
return axios.get<any>(`/project/getProject?id=${id}`);
}
// 项目列表(分页)
export function queryPolicyList(params: ProjectParams) {
return axios.post<any>('/project/getProjectList', { params });
}
+103
View File
@@ -0,0 +1,103 @@
import axios from 'axios';
import { HttpResponse } from './interceptor';
export interface DepartmentFormParams {
parent_id: number | null;
name: string;
leaderId?: number;
remark?: string;
id?: number;
}
export interface DepartmentIdData {
departmentId: number | string;
}
type DepartmentFormRes = HttpResponse<DepartmentIdData>;
export interface DepartmentItem {
id: number;
systemTenantId: number;
name: string;
parentId: number;
UserId: number;
level: number;
path: string;
remark: string;
isDelete: number;
createdAt: string;
updatedAt: string;
sort: number;
children?: DepartmentItem[]; // 递归结构
isShowEditIcon?: boolean; // 是否展示编辑icon
username?: string;
parentName?: string;
}
export type DepartmentList = DepartmentItem[];
export type DepartmentListRes = HttpResponse<{
departmentList: DepartmentItem[];
}>;
export interface DepartmentDetailData {
department: DepartmentItem;
}
export type DepartmentDetailRes = HttpResponse<DepartmentDetailData>;
// 创建部门
export function createDepartment(
params: DepartmentFormParams
): Promise<DepartmentFormRes> {
return axios.post('/department/createDepartment', params);
}
// 获取部门列表
export function getDepartmentList(): Promise<DepartmentListRes> {
return axios.get('/department/getDepartmentList');
}
// 更新(编辑)部门
export function updateDepartment(
params: DepartmentFormParams
): Promise<DepartmentFormRes> {
return axios.post('/department/updateDepartment', params);
}
export function getDepartmentDetail(id: number): Promise<DepartmentDetailRes> {
return axios.get(`/department/getDepartment?id=${id}`);
}
export interface UserItem {
id: number; // 用户id
username: string; // 用户名称
realname: string; // 姓名
email: string; // 邮箱
tel: string; // 手机号
status: 1 | 2; // 状态:1 启用,2 禁用
role_id: number; // 角色id
department_id: number; // 部门id
role_name: string; // 角色名称
dept_name: string; // 部门名称
lastLoginTime: string; // 最后登录时间
}
export type UserList = UserItem[];
export type UserListRes = HttpResponse<UserList>;
// 后台-用户列表(除主管外所有用户)
export function getAllUserList(params): Promise<UserListRes> {
return axios.post('/user/getAllUserList', params);
}
// 部门-向上移动
export function departmentSetUp(id: number): Promise<HttpResponse> {
return axios.get(`/department/setUp?id=${id}`);
}
// 部门-向下移动
export function departmentSetDown(id: number): Promise<HttpResponse> {
return axios.get(`/department/setDown?id=${id}`);
}
// 删除部门
export function delDepartment(id: number): Promise<HttpResponse> {
return axios.get(`/department/delDepartment?id=${id}`);
}
// // 项目信息
// export function getProjectDetail(id: string) {
// return axios.get<any>(`/project/getProject?id=${id}`);
// }
// // 项目列表(分页)
// export function queryPolicyList(params: ProjectParams) {
// return axios.post<any>('/project/getProjectList', { params });
// }
+112 -6
View File
@@ -13,36 +13,131 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
// reset.less - 通用 CSS Reset 初始化样式
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
main,
menu,
nav,
section,
summary {
display: block;
}
body {
font-size: 14px;
line-height: 1.5;
text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
}
ul,
ol {
list-style: none;
}
a {
color: inherit;
text-decoration: none;
background-color: transparent;
&:hover {
text-decoration: underline;
}
}
img,
video {
max-width: 100%;
height: auto;
vertical-align: middle;
border: 0;
}
button,
input,
select,
textarea {
font: inherit;
color: inherit;
background: none;
border: none;
outline: none;
appearance: none;
}
button {
cursor: pointer;
background: none;
border: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
input[type='search'] {
appearance: none;
}
// 常用隐藏类
.hide {
display: none !important;
}
// 通用字体加粗
.bold {
font-weight: bold;
}
.echarts-tooltip-diy {
background: linear-gradient(
304.17deg,
rgba(253, 254, 255, 0.6) -6.04%,
rgba(244, 247, 252, 0.6) 85.2%
rgb(253 254 255 / 60%) -6.04%,
rgb(244 247 252 / 60%) 85.2%
) !important;
border: none !important;
backdrop-filter: blur(10px) !important;
/* Note: backdrop-filter has minimal browser support */
border-radius: 6px !important;
.content-panel {
display: flex;
justify-content: space-between;
padding: 0 9px;
background: rgba(255, 255, 255, 0.8);
background: rgb(255 255 255 / 80%);
width: 164px;
height: 32px;
line-height: 32px;
box-shadow: 6px 0px 20px rgba(34, 87, 188, 0.1);
box-shadow: 6px 0 20px rgb(34 87 188 / 10%);
border-radius: 4px;
margin-bottom: 4px;
}
.tooltip-title {
margin: 0 0 10px 0;
margin: 0 0 10px;
}
p {
margin: 0;
}
.tooltip-title,
.tooltip-value {
font-size: 13px;
@@ -53,6 +148,7 @@ body {
color: #1d2129;
font-weight: bold;
}
.tooltip-item-icon {
display: inline-block;
margin-right: 8px;
@@ -65,13 +161,15 @@ body {
.general-card {
border-radius: 4px;
border: none;
& > .arco-card-header {
height: auto;
padding: 20px;
border: none;
}
& > .arco-card-body {
padding: 0 20px 20px 20px;
padding: 0 20px 20px;
}
}
@@ -87,18 +185,26 @@ body {
height: 6px;
border-radius: 50%;
background-color: rgb(var(--blue-6));
&.pass {
background-color: rgb(var(--green-6));
}
}
}
.flex_center_between {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title_between {
display: flex;
align-items: center;
justify-content: space-between;
}
.full_height {
/* 56为面包屑高度 58为头部layout高度 20为内容padding */
min-height: calc(100vh - 56px - 58px - 20px);
}
+1 -1
View File
@@ -116,7 +116,7 @@
const paddingStyle = computed(() => {
const paddingLeft =
renderMenu.value && !hideMenu.value
? { paddingLeft: `${menuWidth.value + 10}px` }
? { paddingLeft: `${menuWidth.value + 18}px` }
: {};
const paddingTop = navbar.value ? { paddingTop: navbarHeight } : {};
return { ...paddingLeft, ...paddingTop };
+37 -9
View File
@@ -5,18 +5,13 @@ import localeWorkplace from '@/views/dashboard/workplace/locale/zh-CN';
import localeMonitor from '@/views/dashboard/monitor/locale/zh-CN';
import locale403 from '@/views/exception/403/locale/zh-CN';
import locale404 from '@/views/exception/404/locale/zh-CN';
import locale500 from '@/views/exception/500/locale/zh-CN';
import localeSettings from './zh-CN/settings';
import localeBusiness from '@/views/business/locale/zh-CN';
import localeFinance from '@/views/finance/locale/zh-CN';
import localeProperty from '@/views/property/locale/zh-CN';
import localeSetting from '@/views/setting/locale/zh-CN';
import localeSettings from './zh-CN/settings';
export default {
'menu.dashboard': '仪表盘',
@@ -35,14 +30,49 @@ export default {
'navbar.docs': '文档中心',
'navbar.action.locale': '切换为中文',
// 翻译
'menu.business': '业务',
'menu.finance': '财务',
'menu.property': '物业',
'menu.setting': '设置',
'search.placeholder': '请输入内容',
'table.filter': '筛选',
'table.export': '导出',
'watch-detail': '查看详情',
'menu.business.project': '项目管理',
'menu.business.space': '空间管理',
'menu.business.house-view': '房源剖视',
'menu.business.house-list': '房源列表',
'menu.business.contract': '合同管理',
'menu.business.contract-list': '合同列表',
'menu.business.contract-file': '合同归档',
'menu.business.contract-template': '合同模板',
'menu.business.tenant': '租客管理',
'menu.business.tenant-list': '租客列表',
'menu.business.tenant.contacts': '租客联系人',
'add-project': '新建项目',
'menu.setting.user': '用户设置',
'menu.setting.user-list': '用户列表',
'menu.setting.role': '角色权限',
'menu.setting.system': '系统配置',
'organization': '组织架构',
'general-setting': '通用设置',
'security-setting': '安全设置',
'company-info': '公司信息',
'appearance-setting': '外观设置',
'add-department': '新建部门',
'department-name': '部门名称',
'belongto-department': '所属部门',
'department-head': '部门主管',
'department-desc': '部门描述',
'remark': '备注',
'confirm.delete': '确认删除',
'delete.some': '删除{name}',
'please.confirm.delete.some': '请确认是否要删除此{name}?',
'department': '部门',
'delete': '删除',
'confirm': '确定',
'cancel': '取消',
...localeSettings,
...localeMessageBox,
@@ -54,8 +84,6 @@ export default {
...locale404,
...locale500,
...localeBusiness,
...localeFinance,
...localeProperty,
...localeSetting,
};
+3 -2
View File
@@ -1,12 +1,12 @@
import PageLayout from '@/layout/page-layout.vue';
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
import PageLayout from '@/layout/page-layout.vue';
// 设置
const SETTING: AppRouteRecordRaw = {
path: '/setting',
name: 'setting',
component: DEFAULT_LAYOUT,
redirect: '/setting/basic',
redirect: '/setting/user',
meta: {
locale: 'menu.setting',
requiresAuth: true,
@@ -20,6 +20,7 @@ const SETTING: AppRouteRecordRaw = {
path: 'user',
name: 'user-setting',
component: PageLayout,
redirect: '/setting/user/list',
meta: {
locale: 'menu.setting.user',
requiresAuth: true,
+13
View File
@@ -22,4 +22,17 @@ export const regexUrl = new RegExp(
'i'
);
export function debounce<T extends (...args: any[]) => any>(
fn: T,
delay = 300
) {
let timer: ReturnType<typeof setTimeout> | null = null;
return function (this: any, ...args: Parameters<T>) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
export default null;
-14
View File
@@ -1,14 +0,0 @@
export default {
'menu.business.project': '项目管理',
'menu.business.space': '空间管理',
'menu.business.house-view': '房源剖视',
'menu.business.house-list': '房源列表',
'menu.business.contract': '合同管理',
'menu.business.contract-list': '合同列表',
'menu.business.contract-file': '合同归档',
'menu.business.contract-template': '合同模板',
'menu.business.tenant': '租客管理',
'menu.business.tenant-list': '租客列表',
'menu.business.tenant.contacts': '租客联系人',
'add-project': '新建项目',
};
+14
View File
@@ -77,6 +77,7 @@
PROJECT_TYPE_COLORS,
PROJECT_TYPE_LABELS,
} from '@/types/business';
import { ProjectParams, getAllProjectList } from '@/api/business';
const { loading, setLoading } = useLoading(false);
const renderData = ref<ProjectRecord[]>([
@@ -138,4 +139,17 @@
pagination.current = current;
// TODO: 这里应该调用API获取对应页码的数据
};
const getAllProjectListData = async () => {
setLoading(true);
try {
const { data } = await getAllProjectList();
console.log(data, 'getAllProjectList');
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
// getAllProjectListData()
</script>
@@ -0,0 +1,78 @@
import { ref, onMounted } from 'vue';
import {
getDepartmentList,
DepartmentItem,
getDepartmentDetail,
} from '@/api/setting';
export default function useDepartmentList(autoFetch = true) {
const departmentListData = ref<DepartmentItem[]>([]);
const allDepartmentKeys = ref<number[]>([]);
function markNonLevel1Departments(list: DepartmentItem[]): DepartmentItem[] {
const traverse = (items: DepartmentItem[]) => {
return items.map((item) => {
const newItem: DepartmentItem = {
...item,
isShowEditIcon: false,
children: [],
};
// 收集 ID
allDepartmentKeys.value.push(item.id);
if (item.children && item.children.length > 0) {
newItem.children = traverse(item.children);
}
return newItem;
});
};
return traverse(list);
}
const getDepartmentListData = async () => {
try {
const { code, data } = await getDepartmentList();
if (code === 200) {
if (data?.departmentList?.length === 0) return;
allDepartmentKeys.value = []; // 清空再收集
const fetchDepartmentList = markNonLevel1Departments(
data?.departmentList
);
departmentListData.value = fetchDepartmentList;
}
} catch (error) {
console.error('获取部门列表失败:', error);
}
};
const getDepartmentDetailData = async (
id: number
): Promise<DepartmentItem | null> => {
try {
const { code, data } = await getDepartmentDetail(id);
if (code === 200 && data?.department) {
return data.department;
}
return null;
} catch (error) {
console.error('获取部门详情失败:', error);
return null;
}
};
if (autoFetch) {
onMounted(() => {
getDepartmentListData();
});
}
return {
departmentListData,
allDepartmentKeys,
getDepartmentListData,
getDepartmentDetailData,
};
}
-6
View File
@@ -1,6 +0,0 @@
export default {
'menu.setting.user': '用户设置',
'menu.setting.user-list': '用户列表',
'menu.setting.role': '角色权限',
'menu.setting.system': '系统配置',
};
@@ -0,0 +1,148 @@
<template>
<a-modal
:visible="visible"
:title="isEdit ? '编辑部门' : '新增部门'"
unmount-on-close
@ok="handleSubmit"
@cancel="handleCancel"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
auto-label-width
layout="vertical"
>
<!-- 部门名称 -->
<a-form-item :label="t('department-name')" field="name" required>
<a-input v-model="form.name" placeholder="请输入部门名称" />
</a-form-item>
<!-- 所属部门 -->
<a-form-item :label="t('belongto-department')" field="parent_id" required>
<a-tree-select
v-model="form.parent_id"
:data="departmentListData"
:field-names="{ key: 'id', title: 'name', children: 'children' }"
placeholder="请选择所属部门"
allow-clear
/>
</a-form-item>
<!-- 部门主管 -->
<a-form-item :label="t('department-head')" field="user_id">
<a-select
v-model="form.user_id"
:options="props.userListData"
placeholder="请选择部门主管"
allow-clear
/>
</a-form-item>
<!-- 备注 -->
<a-form-item :label="t('remark')" field="remark">
<a-textarea
v-model="form.remark"
placeholder="请输入备注"
allow-clear
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { FormInstance } from '@arco-design/web-vue';
import {
createDepartment,
updateDepartment,
DepartmentItem,
UserList,
} from '@/api/setting';
import { Message } from '@arco-design/web-vue';
// Props & Emits
// interface LeaderOption {
// name: string;
// id: number;
// }
interface DepartmentForm {
id?: number;
name: string;
parent_id: number | null;
user_id?: number;
remark?: string;
}
const props = defineProps<{
visible: boolean;
isEdit: boolean;
record?: DepartmentItem | null;
departmentListData: any[]; // 新增
userListData: UserList;
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
(e: 'submitted'): void;
}>();
// 表单数据
const formRef = ref<FormInstance>();
const form = reactive<DepartmentForm>({
name: '',
parent_id: null,
user_id: undefined,
remark: '',
id: undefined,
});
const { t } = useI18n();
// 校验规则
const rules = {
name: [{ required: true, message: '请输入部门名称' }],
parent_id: [{ required: true, message: '请选择所属部门' }],
};
// Watch 弹窗控制 & 初始化
watch(
() => props.visible,
(val) => {
if (val) {
if (props.record) {
form.id = props.record?.id;
form.name = props.record?.name;
form.parent_id = props.record?.parentId ?? '';
form.user_id = props.record?.UserId ?? '';
form.remark = props.record?.remark ?? '';
// Object.assign(form, props.record);
} else {
Object.assign(form, {
id: undefined,
name: '',
parent_id: null,
user_id: undefined,
remark: '',
});
}
}
},
{ immediate: true }
);
// 表单事件
const handleCancel = () => {
emit('update:visible', false);
};
const handleSubmit = async () => {
await formRef.value?.validate();
const { code, message } = props.isEdit
? await updateDepartment(form)
: await createDepartment(form);
if (code === 200) {
Message.success(message);
handleCancel();
// 通知父组件更新部门列表数据
emit('submitted');
}
};
</script>
+80 -1
View File
@@ -1,3 +1,82 @@
<template>
<div>帅哥kilo</div>
<div class="setting_user_list">
<Breadcrumb :items="['menu.setting', 'menu.setting.system']" />
<a-row :gutter="12">
<a-col flex="310px">
<a-card
class="full_height"
:bordered="false"
:title="$t('menu.setting.system')"
>
<ul class="system_setting_list">
<li
v-for="item in systemSettingList"
:key="item.id"
:class="[
'system_setting_item',
{ active: currentActive === item.id },
]"
@click="handleChooseSystemItem(item.id)"
>
<component :is="item.icon" class="mr-2" />
<p class="name">{{ item.name }}</p>
</li>
</ul>
</a-card>
</a-col>
<a-col flex="auto">
<organization v-if="currentActive === 4" />
<div v-else>开发中{{ currentActive }}</div>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import {
IconSettings,
IconSafe,
IconPaste,
IconFullscreen,
IconTiktokColor,
} from '@arco-design/web-vue/es/icon';
import organization from './organization.vue';
// import { CreateDepartmentParams, createDepartment, getDepartmentList, DepartmentItem } from '@/api/setting'
const { t } = useI18n();
const currentActive = ref(4);
const systemSettingList = [
{ id: 1, name: t('general-setting'), icon: IconSettings },
{ id: 2, name: t('security-setting'), icon: IconSafe },
{ id: 3, name: t('company-info'), icon: IconPaste },
{ id: 4, name: t('organization'), icon: IconFullscreen },
{ id: 5, name: t('appearance-setting'), icon: IconTiktokColor },
];
const handleChooseSystemItem = (id: number) => {
currentActive.value = id;
};
</script>
<style lang="less" scoped>
.setting_user_list {
.system_setting_list {
.system_setting_item {
cursor: pointer;
padding: 8px;
width: 100%;
&.active {
background-color: #efefef;
}
display: flex;
align-items: center;
.name {
margin-left: 12px;
}
}
}
}
</style>
+234
View File
@@ -0,0 +1,234 @@
<template>
<a-card class="full_height" :bordered="false">
<template #title>
<div class="card-title_between">
<p>
<icon-drag-arrow />
<span class="title" style="margin-left: 15px">{{
$t('organization')
}}</span>
</p>
<a-button type="primary" @click="handleAdd">
<template #icon>
<icon-plus />
</template>
{{ $t('add-department') }}
</a-button>
</div>
</template>
<a-row :gutter="12">
<a-col class="border" :span="12">
<a-tree
v-model:expanded-keys="expandedKeys"
:field-names="{ key: 'id', title: 'name' }"
:data="departmentListData"
@select="onSelect"
>
<template #icon>
<IconStar />
</template>
<template #extra="nodeData">
<a-row
v-if="nodeData.isShowEditIcon"
style="position: absolute; right: 0"
:gutter="24"
>
<a-col :span="4" @click="handleEdit"><icon-edit /></a-col>
<a-col :span="4">
<a-popconfirm
:ok-text="t('cancel')"
:cancel-text="t('delete')"
:ok-button-props="{ type: 'outline' }"
:cancel-button-props="{ type: 'primary', status: 'danger' }"
:content="
t('please.confirm.delete.some', { name: t('department') })
"
type="error"
@cancel="handleDelete(nodeData)"
>
<icon-delete />
</a-popconfirm>
</a-col>
<a-col :span="4" @click="handleMoveUp(nodeData)"
><icon-up
/></a-col>
<a-col :span="4" @click="handleMoveDown(nodeData)"
><icon-down
/></a-col>
</a-row>
</template>
</a-tree>
</a-col>
<a-col :span="12">
<a-descriptions
v-if="departmentDetailInfo"
:column="1"
layout="horizontal"
bordered
>
<a-descriptions-item :label="t('department-name')">
{{ departmentDetailInfo.name }}</a-descriptions-item
>
<a-descriptions-item :label="t('belongto-department')">
{{ departmentDetailInfo.parentName || '-' }}</a-descriptions-item
>
<a-descriptions-item :label="t('department-head')">
{{ departmentDetailInfo.username || '-' }}</a-descriptions-item
>
<a-descriptions-item :label="t('department-desc')">
{{ departmentDetailInfo.remark }}</a-descriptions-item
>
</a-descriptions>
</a-col>
</a-row>
<departmentFormModal
v-model:visible="showModal"
:department-list-data="departmentListData"
:record="departmentDetailInfo"
:user-list-data="allUserList"
:is-edit="isEdit"
@submitted="getDepartmentListData"
/>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, watch, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import useDepartmentList from '@/views/setting/composables/useDepartmentList';
import {
DepartmentItem,
getAllUserList,
UserList,
departmentSetUp,
departmentSetDown,
delDepartment,
} from '@/api/setting';
import { debounce } from '@/utils';
import { Message } from '@arco-design/web-vue';
import departmentFormModal from './components/department-form-modal.vue';
const {
departmentListData,
allDepartmentKeys,
getDepartmentListData,
getDepartmentDetailData,
} = useDepartmentList();
const emit = defineEmits(['onSelectTree']);
const expandedKeys = ref<(string | number)[]>([]);
const { t } = useI18n();
watch(
allDepartmentKeys,
(val) => {
expandedKeys.value = val;
},
{ immediate: true }
);
const showModal = ref<boolean>(false);
const isEdit = ref<boolean>(false);
const departmentDetailInfo = ref<DepartmentItem | null>(null);
const resetEditIcon = (nodes: any[]) => {
nodes.forEach((node) => {
node.isShowEditIcon = false;
if (node.children && node.children.length > 0) {
resetEditIcon(node.children);
}
});
};
const currentNode = ref<any>(null);
const onSelect = debounce(async (_: (string | number)[], event: any) => {
const { node } = event;
if (node.id === currentNode.value?.id) return;
currentNode.value = node;
// 递归重置所有节点的 isShowEditIcon
resetEditIcon(departmentListData.value);
// 只设置当前节点
if (currentNode.value.level !== 1) {
node.isShowEditIcon = true;
}
departmentDetailInfo.value = await getDepartmentDetailData(
currentNode.value.id
);
}, 300);
// 所有用户数据
const allUserList = ref<UserList>([]);
const userKeyword = ref<string>('');
const getAllUserListData = async () => {
try {
const { code, data } = await getAllUserList({
keyword: userKeyword.value,
});
if (code === 200 && data?.length) {
allUserList.value = data;
}
} catch (error) {
console.error('获取所以用户数据失败:', error);
}
};
const handleAdd = () => {
showModal.value = true;
isEdit.value = false;
};
const handleEdit = () => {
showModal.value = true;
isEdit.value = true;
};
const handleDelete = async (item) => {
const { id } = item;
try {
const { code, message } = await delDepartment(id);
if (code === 200) {
Message.success(message);
getDepartmentListData();
}
} catch (error) {
console.log('删除失败:', error);
}
};
const handleMoveUp = async (item) => {
const { id } = item;
try {
const { code, message } = await departmentSetUp(id);
if (code === 200) {
Message.success(message);
getDepartmentListData();
} else {
Message.info(message);
}
} catch (error) {
console.log('向上移动失败:', error);
}
};
const handleMoveDown = async (item) => {
const { id } = item;
try {
const { code, message } = await departmentSetDown(id);
if (code === 200) {
Message.success(message);
getDepartmentListData();
} else {
Message.info(message);
}
} catch (error) {
console.log('向下移动失败:', error);
}
};
onMounted(() => {
getAllUserListData();
});
</script>
<style lang="less" scoped>
.border {
border: 1px solid #f4f4f4;
height: 100%;
padding: 15px 0;
}
</style>
+55 -1
View File
@@ -1,3 +1,57 @@
<template>
<div>帅哥kilo</div>
<div class="setting_user_list">
<Breadcrumb
:items="['menu.setting', 'menu.setting.user', 'menu.setting.user-list']"
/>
<a-row class="" :gutter="12">
<a-col flex="310px">
<a-card :bordered="false">
<template #title>
<div class="card-title_between">
<p class="title">{{ $t('organization') }}</p>
<a-button @click="onClick"> {{ $t('watch-detail') }} </a-button>
</div>
</template>
<a-tree
:field-names="{ key: 'ID', title: 'Name' }"
:data="departmentListData"
>
<template #icon>
<IconStar />
</template>
</a-tree>
</a-card>
</a-col>
<a-col flex="auto">
<a-card title="Arco Card" :bordered="false">
<template #extra>
<a-link>More</a-link>
</template>
Card content
<br />
Card content
</a-card>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { DepartmentFormParams, createDepartment } from '@/api/setting';
import useDepartmentList from '@/views/setting/composables/useDepartmentList';
// import { ref } from 'vue'
const onClick = async () => {
const params: DepartmentFormParams = {
name: '冰一下很开心', // 部门名称
parent_id: 34, // 上一级id
};
try {
const res = await createDepartment(params);
console.log(res, 'res');
} catch (error) {
console.log(error, 'res');
}
};
const { departmentListData, getDepartmentListData } = useDepartmentList();
getDepartmentListData();
</script>