feat: 新增岗位管理模块功能
This commit is contained in:
14
.vscode/extensions.json
vendored
14
.vscode/extensions.json
vendored
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"antfu.unocss",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"editorconfig.editorconfig",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"kisstkondoros.vscode-gutter-preview",
|
|
||||||
"mariusalchimavicius.json-to-ts",
|
|
||||||
"mhutchie.git-graph",
|
|
||||||
"sdras.vue-vscode-snippets",
|
|
||||||
"vue.volar",
|
|
||||||
"vue.vscode-typescript-vue-plugin"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
22
.vscode/launch.json
vendored
22
.vscode/launch.json
vendored
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Vue Debugger",
|
|
||||||
"url": "http://localhost:9527",
|
|
||||||
"webRoot": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "TS Debugger",
|
|
||||||
"runtimeExecutable": "tsx",
|
|
||||||
"skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"],
|
|
||||||
"program": "${file}",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"internalConsoleOptions": "neverOpen"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
31
.vscode/settings.json
vendored
31
.vscode/settings.json
vendored
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": "explicit",
|
|
||||||
"source.organizeImports": "never"
|
|
||||||
},
|
|
||||||
"editor.formatOnSave": false,
|
|
||||||
"eslint.validate": [
|
|
||||||
"html",
|
|
||||||
"css",
|
|
||||||
"scss",
|
|
||||||
"json",
|
|
||||||
"jsonc",
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"typescriptreact",
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"i18n-ally.displayLanguage": "zh-cn",
|
|
||||||
"i18n-ally.enabledParsers": ["ts"],
|
|
||||||
"i18n-ally.enabledFrameworks": ["vue"],
|
|
||||||
"i18n-ally.editor.preferEditor": true,
|
|
||||||
"i18n-ally.keystyle": "nested",
|
|
||||||
"i18n-ally.localesPaths": ["src/locales/langs"],
|
|
||||||
"i18n-ally.parsers.typescript.compilerOptions": {
|
|
||||||
"moduleResolution": "node"
|
|
||||||
},
|
|
||||||
"prettier.enable": false,
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
|
||||||
"unocss.root": ["./"]
|
|
||||||
}
|
|
||||||
10919
pnpm-lock.yaml
generated
Normal file
10919
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -67,3 +67,11 @@ export const menuRouteKindRecord: Record<Api.SystemManage.MenuRouteKind, App.I18
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const menuRouteKindOptions = transformRecordToOption(menuRouteKindRecord);
|
export const menuRouteKindOptions = transformRecordToOption(menuRouteKindRecord);
|
||||||
|
|
||||||
|
export const postTypeRecord: Record<Api.SystemManage.PostType, App.I18n.I18nKey> = {
|
||||||
|
management: 'page.system.post.type.management',
|
||||||
|
technical: 'page.system.post.type.technical',
|
||||||
|
business: 'page.system.post.type.business'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const postTypeOptions = transformRecordToOption(postTypeRecord);
|
||||||
|
|||||||
@@ -630,6 +630,33 @@ const local: App.I18n.Schema = {
|
|||||||
editType: '编辑字典类型',
|
editType: '编辑字典类型',
|
||||||
addData: '新增字典数据',
|
addData: '新增字典数据',
|
||||||
editData: '编辑字典数据'
|
editData: '编辑字典数据'
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
title: '岗位列表',
|
||||||
|
postName: '岗位名称',
|
||||||
|
postCode: '岗位编码',
|
||||||
|
postType: '岗位类型',
|
||||||
|
levelRank: '岗位职级',
|
||||||
|
sort: '排序',
|
||||||
|
postStatus: '岗位状态',
|
||||||
|
remark: '备注',
|
||||||
|
createTime: '创建时间',
|
||||||
|
form: {
|
||||||
|
postName: '请输入岗位名称',
|
||||||
|
postCode: '请输入岗位编码',
|
||||||
|
postType: '请选择岗位类型',
|
||||||
|
levelRank: '请输入岗位职级',
|
||||||
|
sort: '请输入排序',
|
||||||
|
postStatus: '请选择岗位状态',
|
||||||
|
remark: '请输入备注'
|
||||||
|
},
|
||||||
|
addPost: '新增岗位',
|
||||||
|
editPost: '编辑岗位',
|
||||||
|
type: {
|
||||||
|
management: '管理类',
|
||||||
|
technical: '技术类',
|
||||||
|
business: '业务类'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -243,6 +243,59 @@ export function fetchGetPostSimpleList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取岗位分页 */
|
||||||
|
export function fetchGetPostPage(params?: any) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/page`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取岗位详情 */
|
||||||
|
export function fetchGetPost(id: number) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/get`,
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建岗位 */
|
||||||
|
export function fetchCreatePost(data: any) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/create`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新岗位 */
|
||||||
|
export function fetchUpdatePost(data: { id: number } & any) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/update`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除岗位 */
|
||||||
|
export function fetchDeletePost(id: number) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/delete`,
|
||||||
|
method: 'delete',
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除岗位 */
|
||||||
|
export function fetchBatchDeletePost(ids: number[]) {
|
||||||
|
return request({
|
||||||
|
url: `${POST_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`,
|
||||||
|
method: 'delete'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取用户分页 */
|
/** 获取用户分页 */
|
||||||
export function fetchGetUserPage(params?: Api.SystemManage.UserSearchParams) {
|
export function fetchGetUserPage(params?: Api.SystemManage.UserSearchParams) {
|
||||||
return request<Api.SystemManage.UserList>({
|
return request<Api.SystemManage.UserList>({
|
||||||
|
|||||||
21
src/typings/api/system-manage.d.ts
vendored
21
src/typings/api/system-manage.d.ts
vendored
@@ -177,6 +177,18 @@ declare namespace Api {
|
|||||||
|
|
||||||
type PostType = 'management' | 'technical' | 'business';
|
type PostType = 'management' | 'technical' | 'business';
|
||||||
|
|
||||||
|
interface Post {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
code?: string | null;
|
||||||
|
postType?: PostType | null;
|
||||||
|
levelRank?: number | null;
|
||||||
|
sort?: number | null;
|
||||||
|
status: CommonStatus;
|
||||||
|
remark?: string | null;
|
||||||
|
createTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface PostSimple {
|
interface PostSimple {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -188,6 +200,15 @@ declare namespace Api {
|
|||||||
|
|
||||||
type PostSimpleList = PostSimple[];
|
type PostSimpleList = PostSimple[];
|
||||||
|
|
||||||
|
type PostSearchParams = CommonType.RecordNullable<Pick<Post, 'name' | 'code' | 'postType' | 'status'>> &
|
||||||
|
PageParams;
|
||||||
|
|
||||||
|
type SavePostParams = Pick<Post, 'name' | 'code' | 'postType' | 'levelRank' | 'sort' | 'status'> & {
|
||||||
|
remark?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PostList = PageResult<Post>;
|
||||||
|
|
||||||
type RoleSimple = Pick<Role, 'id' | 'name' | 'code' | 'status' | 'sort'>;
|
type RoleSimple = Pick<Role, 'id' | 'name' | 'code' | 'status' | 'sort'>;
|
||||||
|
|
||||||
type RoleSimpleList = RoleSimple[];
|
type RoleSimpleList = RoleSimple[];
|
||||||
|
|||||||
@@ -1,3 +1,301 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { nextTick, reactive, ref } from 'vue';
|
||||||
|
import type { TableInstance } from 'element-plus';
|
||||||
|
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useBoolean } from '@sa/hooks';
|
||||||
|
import { commonStatusRecord } from '@/constants/business';
|
||||||
|
import { fetchBatchDeletePost, fetchDeletePost, fetchGetPostPage } from '@/service/api';
|
||||||
|
import { useUIPaginatedTable } from '@/hooks/common/table';
|
||||||
|
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import PostOperateDialog from './modules/post-operate-dialog.vue';
|
||||||
|
import PostSearch from './modules/post-search.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'PostManage' });
|
||||||
|
|
||||||
|
function getInitSearchParams(): Api.SystemManage.PostSearchParams {
|
||||||
|
return {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: undefined,
|
||||||
|
code: undefined,
|
||||||
|
postType: undefined,
|
||||||
|
status: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformPageResult(response: Awaited<ReturnType<typeof fetchGetPostPage>>, pageNo: number, pageSize: number) {
|
||||||
|
if (!response.error) {
|
||||||
|
return {
|
||||||
|
data: response.data.list,
|
||||||
|
pageNum: pageNo,
|
||||||
|
pageSize,
|
||||||
|
total: response.data.total
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
pageNum: pageNo,
|
||||||
|
pageSize,
|
||||||
|
total: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(value?: number | null) {
|
||||||
|
if (!value) {
|
||||||
|
return '--';
|
||||||
|
}
|
||||||
|
|
||||||
|
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusTagType(status: Api.SystemManage.CommonStatus): UI.ThemeColor {
|
||||||
|
return status === 0 ? 'success' : 'warning';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusLabel(status: Api.SystemManage.CommonStatus) {
|
||||||
|
return $t(commonStatusRecord[status]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchParams = reactive(getInitSearchParams());
|
||||||
|
const postTableRef = ref<TableInstance>();
|
||||||
|
const postCheckedRowKeys = ref<number[]>([]);
|
||||||
|
|
||||||
|
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useUIPaginatedTable({
|
||||||
|
paginationProps: {
|
||||||
|
currentPage: searchParams.pageNo,
|
||||||
|
pageSize: searchParams.pageSize
|
||||||
|
},
|
||||||
|
api: () => fetchGetPostPage(searchParams),
|
||||||
|
transform: response => transformPageResult(response, searchParams.pageNo, searchParams.pageSize),
|
||||||
|
onPaginationParamsChange: params => {
|
||||||
|
searchParams.pageNo = params.currentPage ?? 1;
|
||||||
|
searchParams.pageSize = params.pageSize ?? 10;
|
||||||
|
},
|
||||||
|
columns: () => [
|
||||||
|
{ prop: 'selection', type: 'selection', width: 48 },
|
||||||
|
{ prop: 'index', type: 'index', label: $t('common.index'), width: 64 },
|
||||||
|
{ prop: 'name', label: $t('page.system.post.postName'), minWidth: 160, showOverflowTooltip: true },
|
||||||
|
{ prop: 'code', label: $t('page.system.post.postCode'), minWidth: 180, showOverflowTooltip: true },
|
||||||
|
{
|
||||||
|
prop: 'postType',
|
||||||
|
label: $t('page.system.post.postType'),
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
formatter: row => row.postType ? $t(`page.system.post.type.${row.postType}`) : '--'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'status',
|
||||||
|
label: $t('page.system.post.postStatus'),
|
||||||
|
width: 110,
|
||||||
|
align: 'center',
|
||||||
|
formatter: row => <ElTag type={getStatusTagType(row.status)}>{getStatusLabel(row.status)}</ElTag>
|
||||||
|
},
|
||||||
|
{ prop: 'levelRank', label: $t('page.system.post.levelRank'), width: 100, align: 'center', formatter: row => row.levelRank ?? '--' },
|
||||||
|
{ prop: 'sort', label: $t('page.system.post.sort'), width: 90, align: 'center' },
|
||||||
|
{
|
||||||
|
prop: 'remark',
|
||||||
|
label: $t('page.system.post.remark'),
|
||||||
|
minWidth: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: row => row.remark || '--'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'createTime',
|
||||||
|
label: $t('page.system.post.createTime'),
|
||||||
|
minWidth: 170,
|
||||||
|
formatter: row => formatTime(row.createTime)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'operate',
|
||||||
|
label: $t('common.operate'),
|
||||||
|
width: 196,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
formatter: row => (
|
||||||
|
<BusinessTableActionCell
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
label: $t('common.edit'),
|
||||||
|
buttonType: 'primary',
|
||||||
|
onClick: () => openEdit(row)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: $t('common.delete'),
|
||||||
|
buttonType: 'danger',
|
||||||
|
onClick: () => handleDeleteAction(row)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal } = useBoolean();
|
||||||
|
const operateType = ref<UI.TableOperateType>('add');
|
||||||
|
const editingData = ref<Api.SystemManage.Post | null>(null);
|
||||||
|
|
||||||
|
function openAdd() {
|
||||||
|
operateType.value = 'add';
|
||||||
|
editingData.value = null;
|
||||||
|
openOperateModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEdit(item: Api.SystemManage.Post) {
|
||||||
|
operateType.value = 'edit';
|
||||||
|
editingData.value = item;
|
||||||
|
openOperateModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(item: Api.SystemManage.Post) {
|
||||||
|
const { error } = await fetchDeletePost(item.id);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.$message?.success($t('common.deleteSuccess'));
|
||||||
|
|
||||||
|
await reloadPostTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDeleteAction(row: Api.SystemManage.Post) {
|
||||||
|
try {
|
||||||
|
await window.$messageBox?.confirm($t('common.confirmDelete'), $t('common.warning'), {
|
||||||
|
confirmButtonText: $t('common.confirm'),
|
||||||
|
cancelButtonText: $t('common.cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleDelete(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
if (!postCheckedRowKeys.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error } = await fetchBatchDeletePost(postCheckedRowKeys.value);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.$message?.success($t('common.deleteSuccess'));
|
||||||
|
await reloadPostTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePostSelectionChange(rows: Api.SystemManage.Post[]) {
|
||||||
|
postCheckedRowKeys.value = rows.map(item => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reloadPostTable(page = searchParams.pageNo) {
|
||||||
|
postCheckedRowKeys.value = [];
|
||||||
|
await getDataByPage(page);
|
||||||
|
await nextTick();
|
||||||
|
postTableRef.value?.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSearchParams() {
|
||||||
|
Object.assign(searchParams, getInitSearchParams());
|
||||||
|
reloadPostTable(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
reloadPostTable(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmitted(postId: number) {
|
||||||
|
closeOperateModal();
|
||||||
|
reloadPostTable();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>岗位管理</h1>
|
<div class="flex-col-stretch gap-16px overflow-hidden">
|
||||||
|
<PostSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
||||||
|
|
||||||
|
<ElCard class="card-wrapper flex-1-hidden" body-class="post-table-card-body">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between gap-12px">
|
||||||
|
<div class="flex items-center gap-10px">
|
||||||
|
<p>{{ $t('page.system.post.title') }}</p>
|
||||||
|
<ElTag effect="plain">{{ mobilePagination.total || data.length }}</ElTag>
|
||||||
|
</div>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="true"
|
||||||
|
:loading="loading"
|
||||||
|
@refresh="reloadPostTable"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<ElButton plain type="primary" @click="openAdd">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-plus class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.add') }}
|
||||||
|
</ElButton>
|
||||||
|
<ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleBatchDelete">
|
||||||
|
<template #reference>
|
||||||
|
<ElButton type="danger" plain :disabled="postCheckedRowKeys.length === 0">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-delete class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.batchDelete') }}
|
||||||
|
</ElButton>
|
||||||
|
</template>
|
||||||
|
</ElPopconfirm>
|
||||||
|
</template>
|
||||||
|
</TableHeaderOperation>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<ElTable
|
||||||
|
ref="postTableRef"
|
||||||
|
v-loading="loading"
|
||||||
|
height="100%"
|
||||||
|
border
|
||||||
|
row-key="id"
|
||||||
|
:data="data"
|
||||||
|
@selection-change="handlePostSelectionChange"
|
||||||
|
>
|
||||||
|
<ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col" />
|
||||||
|
</ElTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-20px flex justify-end">
|
||||||
|
<ElPagination
|
||||||
|
v-if="mobilePagination.total"
|
||||||
|
layout="total,prev,pager,next,sizes"
|
||||||
|
v-bind="mobilePagination"
|
||||||
|
@current-change="mobilePagination['current-change']"
|
||||||
|
@size-change="mobilePagination['size-change']"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
|
<PostOperateDialog
|
||||||
|
v-model:visible="operateVisible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
@submitted="handleSubmitted"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.post-table-card-body) {
|
||||||
|
height: calc(100% - 56px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
215
src/views/system/post/modules/post-operate-dialog.vue
Normal file
215
src/views/system/post/modules/post-operate-dialog.vue
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
import { commonStatusOptions } from '@/constants/business';
|
||||||
|
import { postTypeOptions } from '@/constants/business';
|
||||||
|
import { fetchCreatePost, fetchGetPost, fetchUpdatePost } from '@/service/api';
|
||||||
|
import { useForm, useFormRules } from '@/hooks/common/form';
|
||||||
|
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'PostOperateDialog' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
operateType: UI.TableOperateType;
|
||||||
|
rowData?: Api.SystemManage.Post | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submitted: [postId: number];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate } = useForm();
|
||||||
|
const { createRequiredRule } = useFormRules();
|
||||||
|
|
||||||
|
const detailLoading = ref(false);
|
||||||
|
const submitting = ref(false);
|
||||||
|
const isEdit = computed(() => props.operateType === 'edit');
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
const titleMap: Record<UI.TableOperateType, string> = {
|
||||||
|
add: $t('page.system.post.addPost'),
|
||||||
|
edit: $t('page.system.post.editPost')
|
||||||
|
};
|
||||||
|
|
||||||
|
return titleMap[props.operateType];
|
||||||
|
});
|
||||||
|
|
||||||
|
type Model = Api.SystemManage.SavePostParams;
|
||||||
|
|
||||||
|
const model = ref(createDefaultModel());
|
||||||
|
|
||||||
|
function createDefaultModel(): Model {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
postType: null,
|
||||||
|
levelRank: null,
|
||||||
|
sort: 0,
|
||||||
|
status: 0,
|
||||||
|
remark: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: createRequiredRule($t('page.system.post.form.postName')),
|
||||||
|
code: createRequiredRule($t('page.system.post.form.postCode')),
|
||||||
|
postType: createRequiredRule($t('page.system.post.form.postType')),
|
||||||
|
sort: createRequiredRule($t('page.system.post.form.sort')),
|
||||||
|
status: createRequiredRule($t('page.system.post.form.postStatus'))
|
||||||
|
} satisfies Record<string, App.Global.FormRule>;
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initModel() {
|
||||||
|
model.value = createDefaultModel();
|
||||||
|
|
||||||
|
if (!isEdit.value || !props.rowData) {
|
||||||
|
await nextTick();
|
||||||
|
formRef.value?.clearValidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
detailLoading.value = true;
|
||||||
|
|
||||||
|
const { error, data } = await fetchGetPost(props.rowData.id);
|
||||||
|
|
||||||
|
detailLoading.value = false;
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
model.value = {
|
||||||
|
name: data.name,
|
||||||
|
code: data.code ?? '',
|
||||||
|
postType: data.postType ?? null,
|
||||||
|
levelRank: data.levelRank ?? null,
|
||||||
|
sort: data.sort ?? 0,
|
||||||
|
status: data.status,
|
||||||
|
remark: data.remark ?? ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
formRef.value?.clearValidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
|
||||||
|
submitting.value = true;
|
||||||
|
|
||||||
|
const submitData: Api.SystemManage.SavePostParams = {
|
||||||
|
...model.value,
|
||||||
|
name: model.value.name.trim(),
|
||||||
|
code: model.value.code?.trim() || null,
|
||||||
|
remark: model.value.remark?.trim() || null
|
||||||
|
};
|
||||||
|
|
||||||
|
const request =
|
||||||
|
isEdit.value && props.rowData
|
||||||
|
? await fetchUpdatePost({id: props.rowData.id, ...submitData})
|
||||||
|
: await fetchCreatePost(submitData);
|
||||||
|
|
||||||
|
const { error, data } = await request;
|
||||||
|
|
||||||
|
submitting.value = false;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const postId = isEdit.value && props.rowData ? props.rowData.id : Number(data);
|
||||||
|
|
||||||
|
window.$message?.success($t(isEdit.value ? 'common.updateSuccess' : 'common.addSuccess'));
|
||||||
|
|
||||||
|
closeModal();
|
||||||
|
emit('submitted', postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, value => {
|
||||||
|
if (value) {
|
||||||
|
initModel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BusinessFormDialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="title"
|
||||||
|
preset="md"
|
||||||
|
:loading="detailLoading"
|
||||||
|
:confirm-loading="submitting"
|
||||||
|
:scrollbar="false"
|
||||||
|
@confirm="handleSubmit"
|
||||||
|
>
|
||||||
|
<ElForm ref="formRef" :model="model" :rules="rules" label-position="top">
|
||||||
|
<ElRow :gutter="16">
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postName')" prop="name">
|
||||||
|
<ElInput v-model="model.name" :placeholder="$t('page.system.post.form.postName')" />
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postCode')" prop="code">
|
||||||
|
<ElInput v-model="model.code" :placeholder="$t('page.system.post.form.postCode')" />
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postType')" prop="postType">
|
||||||
|
<ElSelect v-model="model.postType" class="w-full" :placeholder="$t('page.system.post.form.postType')">
|
||||||
|
<ElOption v-for="{ label, value } in postTypeOptions" :key="value" :label="$t(label)" :value="value" />
|
||||||
|
</ElSelect>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.levelRank')" prop="levelRank">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="model.levelRank"
|
||||||
|
class="w-full"
|
||||||
|
:min="0"
|
||||||
|
:placeholder="$t('page.system.post.form.levelRank')"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.sort')" prop="sort">
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="model.sort"
|
||||||
|
class="w-full"
|
||||||
|
:min="0"
|
||||||
|
:placeholder="$t('page.system.post.form.sort')"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postStatus')" prop="status">
|
||||||
|
<ElRadioGroup v-model="model.status" class="business-form-radio-group">
|
||||||
|
<ElRadio v-for="{ label, value } in commonStatusOptions" :key="value" :value="value">
|
||||||
|
{{ $t(label) }}
|
||||||
|
</ElRadio>
|
||||||
|
</ElRadioGroup>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="24">
|
||||||
|
<ElFormItem :label="$t('page.system.post.remark')" prop="remark">
|
||||||
|
<ElInput
|
||||||
|
v-model="model.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
:placeholder="$t('page.system.post.form.remark')"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
</ElForm>
|
||||||
|
</BusinessFormDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
61
src/views/system/post/modules/post-search.vue
Normal file
61
src/views/system/post/modules/post-search.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { commonStatusOptions } from '@/constants/business';
|
||||||
|
import { postTypeOptions } from '@/constants/business';
|
||||||
|
import TableSearchPanel from '@/components/custom/table-search-panel.vue';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'PostSearch' });
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
reset: [];
|
||||||
|
search: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = defineModel<Api.SystemManage.PostSearchParams>('model', { required: true });
|
||||||
|
|
||||||
|
const keyword = computed({
|
||||||
|
get() {
|
||||||
|
return model.value.name ?? model.value.code ?? '';
|
||||||
|
},
|
||||||
|
set(value: string) {
|
||||||
|
const text = value.trim() || undefined;
|
||||||
|
model.value.name = text;
|
||||||
|
model.value.code = text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
emit('reset');
|
||||||
|
}
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
emit('search');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TableSearchPanel :model="model" :action-col-lg="8" @reset="reset" @search="search">
|
||||||
|
<ElCol :lg="8" :md="12" :sm="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postName')" prop="name">
|
||||||
|
<ElInput v-model="keyword" clearable :placeholder="$t('page.system.post.form.postName')" />
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :lg="8" :md="12" :sm="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postType')" prop="postType">
|
||||||
|
<ElSelect v-model="model.postType" clearable :placeholder="$t('page.system.post.form.postType')">
|
||||||
|
<ElOption v-for="{ label, value } in postTypeOptions" :key="value" :label="$t(label)" :value="value" />
|
||||||
|
</ElSelect>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :lg="8" :md="12" :sm="12">
|
||||||
|
<ElFormItem :label="$t('page.system.post.postStatus')" prop="status">
|
||||||
|
<ElSelect v-model="model.status" clearable :placeholder="$t('page.system.post.form.postStatus')">
|
||||||
|
<ElOption v-for="{ label, value } in commonStatusOptions" :key="value" :label="$t(label)" :value="value" />
|
||||||
|
</ElSelect>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElCol>
|
||||||
|
</TableSearchPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Reference in New Issue
Block a user