Restore core admin menu modules
This commit is contained in:
@@ -229,7 +229,6 @@ web/apps/web-ele/src/views/_core/database-manager/
|
||||
web/apps/web-ele/src/views/_core/database-monitor/
|
||||
web/apps/web-ele/src/views/_core/demo/
|
||||
web/apps/web-ele/src/views/_core/mobile-signature/
|
||||
web/apps/web-ele/src/views/_core/post/
|
||||
web/apps/web-ele/src/views/_core/redis-manager/
|
||||
web/apps/web-ele/src/views/_core/redis-monitor/
|
||||
web/apps/web-ele/src/views/_core/region-manager/
|
||||
|
||||
@@ -29,14 +29,23 @@ AI_AGENT_ADMIN_MENU_NAMES = {
|
||||
"Codex",
|
||||
"SystemConfigManager",
|
||||
"SystemManagement",
|
||||
"SystemDept",
|
||||
"SystemPost",
|
||||
"SystemPermission",
|
||||
"UserManagement",
|
||||
"SystemMenu",
|
||||
"SystemRole",
|
||||
"UIConfigManager",
|
||||
"SystemTools",
|
||||
"系统工具",
|
||||
"SystemDict",
|
||||
"FileManager",
|
||||
"Message",
|
||||
"MessageList",
|
||||
"AnnouncementList",
|
||||
"AnnouncementManage",
|
||||
"userCenter",
|
||||
"OrgNode",
|
||||
"userSettings",
|
||||
"LoginLog",
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface PostCreateInput {
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
dept_id?: string;
|
||||
sort?: number;
|
||||
}
|
||||
|
||||
export interface PostUpdateInput extends Partial<PostCreateInput> {}
|
||||
@@ -120,7 +121,7 @@ export async function deletePostApi(postId: string) {
|
||||
*/
|
||||
export async function batchDeletePostApi(data: PostBatchDeleteInput) {
|
||||
return requestClient.post<{ count: number }>(
|
||||
'/api/core/post/batch_delete',
|
||||
'/api/core/post/batch/delete',
|
||||
data,
|
||||
);
|
||||
}
|
||||
@@ -132,7 +133,7 @@ export async function batchUpdatePostStatusApi(
|
||||
data: PostBatchUpdateStatusInput,
|
||||
) {
|
||||
return requestClient.post<{ count: number }>(
|
||||
'/api/core/post/batch_update_status',
|
||||
'/api/core/post/batch/status',
|
||||
data,
|
||||
);
|
||||
}
|
||||
@@ -141,7 +142,7 @@ export async function batchUpdatePostStatusApi(
|
||||
* 根据部门ID获取岗位列表
|
||||
*/
|
||||
export async function getPostsByDeptApi(deptId: string) {
|
||||
return requestClient.get<Post[]>(`/api/core/post/by_dept/${deptId}`);
|
||||
return requestClient.get<Post[]>(`/api/core/post/by/dept/${deptId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +195,7 @@ export async function getPostStatsApi() {
|
||||
* 导出岗位数据
|
||||
*/
|
||||
export async function exportPostApi(params?: PostListParams) {
|
||||
return requestClient.get<Blob>('/api/core/post/export', {
|
||||
return requestClient.get<Blob>('/api/core/post/export/excel', {
|
||||
params,
|
||||
responseType: 'blob',
|
||||
});
|
||||
@@ -207,7 +208,7 @@ export async function importPostApi(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return requestClient.post<{ error_count: number; success_count: number }>(
|
||||
'/api/core/post/import',
|
||||
'/api/core/post/import/excel',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
@@ -221,7 +222,7 @@ export async function importPostApi(file: File) {
|
||||
* 获取简单岗位列表(用于选择器)
|
||||
*/
|
||||
export async function getSimplePostListApi() {
|
||||
return requestClient.get<Post[]>('/api/core/post/simple');
|
||||
return requestClient.get<Post[]>('/api/core/post/all');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,10 +24,15 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
'../views/_core/authentication/login.vue',
|
||||
'../views/_core/authentication/oauth-callback.vue',
|
||||
'../views/_core/control-center/index.vue',
|
||||
'../views/_core/dept/index.vue',
|
||||
'../views/_core/dict/index.vue',
|
||||
'../views/_core/fallback/**/*.vue',
|
||||
'../views/_core/file-manager/index.vue',
|
||||
'../views/_core/login-log/index.vue',
|
||||
'../views/_core/menu/index.vue',
|
||||
'../views/_core/message/index.vue',
|
||||
'../views/_core/org-chart/index.vue',
|
||||
'../views/_core/post/index.vue',
|
||||
'../views/_core/permission/index.vue',
|
||||
'../views/_core/role/index.vue',
|
||||
'../views/_core/system-config/index.vue',
|
||||
|
||||
@@ -43,10 +43,15 @@ const componentKeys: string[] = Object.keys(
|
||||
'../../views/_core/authentication/login.vue',
|
||||
'../../views/_core/authentication/oauth-callback.vue',
|
||||
'../../views/_core/control-center/index.vue',
|
||||
'../../views/_core/dept/index.vue',
|
||||
'../../views/_core/dict/index.vue',
|
||||
'../../views/_core/fallback/**/*.vue',
|
||||
'../../views/_core/file-manager/index.vue',
|
||||
'../../views/_core/login-log/index.vue',
|
||||
'../../views/_core/menu/index.vue',
|
||||
'../../views/_core/message/index.vue',
|
||||
'../../views/_core/org-chart/index.vue',
|
||||
'../../views/_core/post/index.vue',
|
||||
'../../views/_core/permission/index.vue',
|
||||
'../../views/_core/role/index.vue',
|
||||
'../../views/_core/system-config/index.vue',
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import type { Post, PostCreateInput } from '#/api/core/post';
|
||||
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { Edit, Plus, RefreshCw, Search, Trash2 } from '@vben/icons';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElOption,
|
||||
ElPagination,
|
||||
ElSelect,
|
||||
ElSwitch,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElTag,
|
||||
} from 'element-plus';
|
||||
|
||||
import {
|
||||
createPostApi,
|
||||
deletePostApi,
|
||||
getPostListApi,
|
||||
updatePostApi,
|
||||
} from '#/api/core/post';
|
||||
|
||||
defineOptions({ name: 'SystemPost' });
|
||||
|
||||
type NullableStatus = '' | boolean;
|
||||
|
||||
const postTypeOptions = [
|
||||
{ label: '管理岗', value: 0 },
|
||||
{ label: '技术岗', value: 1 },
|
||||
{ label: '业务岗', value: 2 },
|
||||
{ label: '职能岗', value: 3 },
|
||||
{ label: '其他', value: 4 },
|
||||
];
|
||||
|
||||
const postLevelOptions = [
|
||||
{ label: '高层', value: 0 },
|
||||
{ label: '中层', value: 1 },
|
||||
{ label: '基层', value: 2 },
|
||||
{ label: '一般员工', value: 3 },
|
||||
];
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const editingPostId = ref<string>();
|
||||
const tableData = ref<Post[]>([]);
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const query = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
status: '' as NullableStatus,
|
||||
});
|
||||
|
||||
const total = ref(0);
|
||||
|
||||
const form = reactive<PostCreateInput>({
|
||||
code: '',
|
||||
description: '',
|
||||
name: '',
|
||||
post_level: 3,
|
||||
post_type: 4,
|
||||
sort: 0,
|
||||
status: true,
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
code: [
|
||||
{ message: '请输入岗位编码', required: true, trigger: 'blur' },
|
||||
{
|
||||
message: '岗位编码只能包含字母、数字、下划线和横线',
|
||||
pattern: /^[\w-]+$/,
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
name: [{ message: '请输入岗位名称', required: true, trigger: 'blur' }],
|
||||
};
|
||||
|
||||
const dialogTitle = computed(() =>
|
||||
editingPostId.value ? '编辑岗位' : '新增岗位',
|
||||
);
|
||||
|
||||
function getOptionLabel(
|
||||
options: Array<{ label: string; value: number }>,
|
||||
value?: number,
|
||||
) {
|
||||
return options.find((item) => item.value === value)?.label ?? '-';
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
editingPostId.value = undefined;
|
||||
Object.assign(form, {
|
||||
code: '',
|
||||
description: '',
|
||||
name: '',
|
||||
post_level: 3,
|
||||
post_type: 4,
|
||||
sort: 0,
|
||||
status: true,
|
||||
});
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
async function loadPosts() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getPostListApi({
|
||||
code: query.code || undefined,
|
||||
name: query.name || undefined,
|
||||
page: query.page,
|
||||
pageSize: query.pageSize,
|
||||
status: query.status === '' ? undefined : query.status,
|
||||
});
|
||||
tableData.value = res.items ?? [];
|
||||
total.value = res.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onSearch() {
|
||||
query.page = 1;
|
||||
loadPosts();
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
Object.assign(query, {
|
||||
code: '',
|
||||
name: '',
|
||||
page: 1,
|
||||
status: '',
|
||||
});
|
||||
loadPosts();
|
||||
}
|
||||
|
||||
function onCreate() {
|
||||
resetForm();
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function onEdit(row: Post) {
|
||||
resetForm();
|
||||
editingPostId.value = row.id;
|
||||
Object.assign(form, {
|
||||
code: row.code,
|
||||
description: row.description ?? '',
|
||||
name: row.name,
|
||||
post_level: row.post_level ?? 3,
|
||||
post_type: row.post_type ?? 4,
|
||||
sort: row.sort ?? 0,
|
||||
status: row.status,
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
await formRef.value?.validate();
|
||||
saving.value = true;
|
||||
try {
|
||||
const payload = { ...form };
|
||||
if (editingPostId.value) {
|
||||
await updatePostApi(editingPostId.value, payload);
|
||||
ElMessage.success('岗位已更新');
|
||||
} else {
|
||||
await createPostApi(payload);
|
||||
ElMessage.success('岗位已新增');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
await loadPosts();
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete(row: Post) {
|
||||
await ElMessageBox.confirm(
|
||||
`确定删除岗位「${row.name}」吗?`,
|
||||
'删除岗位',
|
||||
{
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonText: '删除',
|
||||
type: 'warning',
|
||||
},
|
||||
);
|
||||
await deletePostApi(row.id);
|
||||
ElMessage.success('岗位已删除');
|
||||
await loadPosts();
|
||||
}
|
||||
|
||||
async function onStatusChange(row: Post, status: boolean) {
|
||||
try {
|
||||
await updatePostApi(row.id, { status });
|
||||
ElMessage.success(status ? '岗位已启用' : '岗位已禁用');
|
||||
} catch (error) {
|
||||
row.status = !status;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadPosts);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="flex h-full min-h-0 flex-col gap-3">
|
||||
<div
|
||||
class="bg-background flex flex-wrap items-end gap-3 rounded-lg border border-border px-4 py-3"
|
||||
>
|
||||
<ElForm inline :model="query" class="post-search-form">
|
||||
<ElFormItem label="岗位名称">
|
||||
<ElInput
|
||||
v-model="query.name"
|
||||
clearable
|
||||
placeholder="请输入岗位名称"
|
||||
@keyup.enter="onSearch"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="岗位编码">
|
||||
<ElInput
|
||||
v-model="query.code"
|
||||
clearable
|
||||
placeholder="请输入岗位编码"
|
||||
@keyup.enter="onSearch"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="状态">
|
||||
<ElSelect
|
||||
v-model="query.status"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
class="w-[120px]"
|
||||
>
|
||||
<ElOption label="启用" :value="true" />
|
||||
<ElOption label="禁用" :value="false" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton type="primary" :icon="Search" @click="onSearch">
|
||||
查询
|
||||
</ElButton>
|
||||
<ElButton :icon="RefreshCw" @click="onReset">重置</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<div class="ml-auto">
|
||||
<ElButton type="primary" :icon="Plus" @click="onCreate">
|
||||
新增岗位
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="bg-background flex min-h-0 flex-1 flex-col rounded-lg border border-border"
|
||||
>
|
||||
<ElTable
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
height="100%"
|
||||
row-key="id"
|
||||
stripe
|
||||
>
|
||||
<ElTableColumn type="index" width="56" label="#" />
|
||||
<ElTableColumn prop="name" label="岗位名称" min-width="150" />
|
||||
<ElTableColumn prop="code" label="岗位编码" min-width="140" />
|
||||
<ElTableColumn label="岗位类型" min-width="110">
|
||||
<template #default="{ row }">
|
||||
{{ getOptionLabel(postTypeOptions, row.post_type) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="岗位级别" min-width="110">
|
||||
<template #default="{ row }">
|
||||
{{ getOptionLabel(postLevelOptions, row.post_level) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="dept_name" label="所属部门" min-width="140">
|
||||
<template #default="{ row }">
|
||||
{{ row.dept_name || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="状态" width="110">
|
||||
<template #default="{ row }">
|
||||
<ElSwitch
|
||||
v-model="row.status"
|
||||
inline-prompt
|
||||
active-text="启"
|
||||
inactive-text="禁"
|
||||
@change="(value) => onStatusChange(row, value as boolean)"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="用户数" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElTag type="info" effect="plain">{{ row.user_count ?? 0 }}</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="sort" label="排序" width="90" align="center" />
|
||||
<ElTableColumn
|
||||
prop="sys_create_datetime"
|
||||
label="创建时间"
|
||||
min-width="170"
|
||||
/>
|
||||
<ElTableColumn label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<ElButton link type="primary" :icon="Edit" @click="onEdit(row)">
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton link type="danger" :icon="Trash2" @click="onDelete(row)">
|
||||
删除
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
|
||||
<div class="flex justify-end border-t border-border px-4 py-3">
|
||||
<ElPagination
|
||||
v-model:current-page="query.page"
|
||||
v-model:page-size="query.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="loadPosts"
|
||||
@size-change="onSearch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElDialog v-model="dialogVisible" :title="dialogTitle" width="520px">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="92px">
|
||||
<ElFormItem label="岗位名称" prop="name">
|
||||
<ElInput v-model="form.name" maxlength="64" placeholder="请输入岗位名称" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="岗位编码" prop="code">
|
||||
<ElInput v-model="form.code" maxlength="32" placeholder="请输入岗位编码" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="岗位类型">
|
||||
<ElSelect v-model="form.post_type" class="w-full">
|
||||
<ElOption
|
||||
v-for="item in postTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="岗位级别">
|
||||
<ElSelect v-model="form.post_level" class="w-full">
|
||||
<ElOption
|
||||
v-for="item in postLevelOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="排序">
|
||||
<ElInputNumber v-model="form.sort" :min="0" class="w-full" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="状态">
|
||||
<ElSwitch
|
||||
v-model="form.status"
|
||||
inline-prompt
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="岗位描述">
|
||||
<ElInput
|
||||
v-model="form.description"
|
||||
maxlength="200"
|
||||
placeholder="请输入岗位描述"
|
||||
show-word-limit
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="saving" @click="onSave">
|
||||
保存
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.post-search-form {
|
||||
margin-bottom: -18px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user