1
0

feat: initial commit

This commit is contained in:
gin
2026-05-07 18:39:00 +08:00
commit cdee21ee8e
653 changed files with 63946 additions and 0 deletions
@@ -0,0 +1,228 @@
<script setup lang="ts">
import { ref } from "vue";
import { usePostHook } from "./utils/hook";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete";
import Search from "@iconify-icons/ep/search";
import Refresh from "@iconify-icons/ep/refresh";
import { useUserStoreHook } from "@/store/modules/user";
// TODO 这个导入声明好长 看看如何优化
import { CommonUtils } from "@/utils/common";
import PostFormModal from "@/views/system/post/post-form-modal.vue";
import EditPen from "@iconify-icons/ep/edit-pen";
import { PostPageResponse } from "@/api/system/post";
import AddFill from "@iconify-icons/ri/add-circle-line";
/** 组件name最好和菜单表中的router_name一致 */
defineOptions({
name: "Post"
});
const loginLogStatusList = useUserStoreHook().dictionaryList["common.status"];
const tableRef = ref();
const searchFormRef = ref();
const {
searchFormParams,
pageLoading,
columns,
dataList,
pagination,
timeRange,
defaultSort,
multipleSelection,
onSearch,
resetForm,
onSortChanged,
exportAllExcel,
getPostList,
handleDelete,
handleBulkDelete
} = usePostHook();
const opType = ref<"add" | "update">("add");
const modalVisible = ref(false);
const opRow = ref<PostPageResponse>();
function openDialog(type: "add" | "update", row?: PostPageResponse) {
opType.value = type;
opRow.value = row;
modalVisible.value = true;
}
</script>
<template>
<div class="main">
<!-- 搜索栏 -->
<el-form
ref="searchFormRef"
:inline="true"
:model="searchFormParams"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
>
<el-form-item label="岗位编码" prop="postCode">
<el-input
v-model="searchFormParams.postCode"
placeholder="请输入岗位编码"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="岗位名称" prop="postName">
<el-input
v-model="searchFormParams.postName"
placeholder="请选择岗位名称"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="状态:" prop="status">
<el-select
v-model="searchFormParams.status"
placeholder="请选择状态"
clearable
class="!w-[180px]"
>
<el-option
v-for="dict in loginLogStatusList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
class="!w-[240px]"
v-model="timeRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="useRenderIcon(Search)"
:loading="pageLoading"
@click="onSearch(tableRef)"
>
搜索
</el-button>
<el-button
:icon="useRenderIcon(Refresh)"
@click="resetForm(searchFormRef, tableRef)"
>
重置
</el-button>
</el-form-item>
</el-form>
<!-- table bar 包裹 table -->
<PureTableBar title="岗位列表" :columns="columns" @refresh="onSearch">
<!-- 表格操作栏 -->
<template #buttons>
<el-button
type="primary"
:icon="useRenderIcon(AddFill)"
@click="openDialog('add')"
>
新增岗位
</el-button>
<el-button
type="danger"
:icon="useRenderIcon(Delete)"
@click="handleBulkDelete(tableRef)"
>
批量删除
</el-button>
<el-button
type="primary"
@click="CommonUtils.exportExcel(columns, dataList, '岗位列表')"
>单页导出</el-button
>
<el-button type="primary" @click="exportAllExcel">全部导出</el-button>
</template>
<template v-slot="{ size, dynamicColumns }">
<pure-table
border
ref="tableRef"
align-whole="center"
showOverflowTooltip
table-layout="auto"
:loading="pageLoading"
:size="size"
adaptive
:data="dataList"
:columns="dynamicColumns"
:default-sort="defaultSort"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
}"
@page-size-change="getPostList"
@page-current-change="getPostList"
@sort-change="onSortChanged"
@selection-change="
rows => (multipleSelection = rows.map(item => item.postId))
"
>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:icon="useRenderIcon(EditPen)"
@click="openDialog('update', row)"
>
编辑
</el-button>
<el-popconfirm
:title="`是否确认删除编号为${row.postId}的这个岗位`"
@confirm="handleDelete(row)"
>
<template #reference>
<el-button
class="reset-margin"
link
type="danger"
:size="size"
:icon="useRenderIcon(Delete)"
>
删除
</el-button>
</template>
</el-popconfirm>
</template>
</pure-table>
</template>
</PureTableBar>
<post-form-modal
v-model="modalVisible"
:type="opType"
:row="opRow"
@success="onSearch"
/>
</div>
</template>
<style scoped lang="scss">
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>
@@ -0,0 +1,131 @@
<script setup lang="ts">
import VDialog from "@/components/VDialog/VDialog.vue";
import { computed, reactive, ref } from "vue";
import {
AddPostCommand,
PostPageResponse,
UpdatePostCommand,
addPostApi,
updatePostApi
} from "@/api/system/post";
import { useUserStoreHook } from "@/store/modules/user";
import { ElMessage, FormInstance, FormRules } from "element-plus";
interface Props {
type: "add" | "update";
modelValue: boolean;
row?: PostPageResponse;
}
const props = defineProps<Props>();
const emits = defineEmits<{
(e: "update:modelValue", v: boolean): void;
(e: "success"): void;
}>();
const visible = computed({
get: () => props.modelValue,
set(v) {
emits("update:modelValue", v);
}
});
const formData = reactive<AddPostCommand | UpdatePostCommand>({
postId: 0,
postCode: "",
postName: "",
postSort: 1,
remark: "",
status: ""
});
const statusList = useUserStoreHook().dictionaryMap["common.status"];
const rules: FormRules = {
postName: [
{
required: true,
message: "岗位名称不能为空"
}
],
postCode: [
{
required: true,
message: "岗位编码不能为空"
}
],
postSort: [
{
required: true,
message: "岗位序号不能为空"
}
]
};
const formRef = ref<FormInstance>();
function handleOpened() {
if (props.row) {
Object.assign(formData, props.row);
} else {
formRef.value?.resetFields();
}
}
const loading = ref(false);
async function handleConfirm() {
try {
loading.value = true;
if (props.type === "add") {
await addPostApi(formData);
} else if (props.type === "update") {
await updatePostApi(formData as UpdatePostCommand);
}
ElMessage.info("提交成功");
visible.value = false;
emits("success");
} catch (e) {
console.error(e);
ElMessage.error((e as Error)?.message || "提交失败");
} finally {
loading.value = false;
}
}
</script>
<template>
<v-dialog
show-full-screen
:fixed-body-height="false"
use-body-scrolling
:title="type === 'add' ? '新增岗位' : '更新岗位'"
v-model="visible"
:loading="loading"
@confirm="handleConfirm"
@cancel="visible = false"
@opened="handleOpened"
>
<el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
<el-form-item prop="postName" label="岗位名称" required inline-message>
<el-input v-model="formData.postName" />
</el-form-item>
<el-form-item prop="postCode" label="岗位编码" required>
<el-input v-model="formData.postCode" />
</el-form-item>
<el-form-item prop="postSort" label="岗位顺序" required>
<el-input-number :min="1" v-model="formData.postSort" />
</el-form-item>
<el-form-item prop="status" label="岗位状态">
<el-radio-group v-model="formData.status">
<el-radio
v-for="item in Object.keys(statusList)"
:key="item"
:label="statusList[item].value"
>{{ statusList[item].label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item prop="remark" label="备注" style="margin-bottom: 0">
<el-input type="textarea" v-model="formData.remark" />
</el-form-item>
</el-form>
</v-dialog>
</template>
@@ -0,0 +1,229 @@
import dayjs from "dayjs";
import { message } from "@/utils/message";
import { ElMessageBox, Sort } from "element-plus";
import { reactive, ref, onMounted, toRaw, computed } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { CommonUtils } from "@/utils/common";
import { PaginationProps } from "@pureadmin/table";
import {
PostListCommand,
getPostListApi,
exportPostExcelApi,
deletePostApi
} from "@/api/system/post";
const statusMap = useUserStoreHook().dictionaryMap["common.status"];
export function usePostHook() {
const defaultSort: Sort = {
prop: "postSort",
order: "ascending"
};
const pagination: PaginationProps = {
total: 0,
pageSize: 10,
currentPage: 1,
background: true
};
const timeRange = computed<[string, string] | null>({
get() {
if (searchFormParams.beginTime && searchFormParams.endTime) {
return [searchFormParams.beginTime, searchFormParams.endTime];
} else {
return null;
}
},
set(v) {
if (v?.length === 2) {
searchFormParams.beginTime = v[0];
searchFormParams.endTime = v[1];
} else {
searchFormParams.beginTime = undefined;
searchFormParams.endTime = undefined;
}
}
});
const searchFormParams = reactive<PostListCommand>({
postCode: "",
postName: "",
status: undefined
});
const dataList = ref([]);
const pageLoading = ref(true);
const multipleSelection = ref([]);
const sortState = ref<Sort>(defaultSort);
const columns: TableColumnList = [
{
type: "selection",
align: "left"
},
{
label: "岗位编号",
prop: "postId",
minWidth: 100
},
{
label: "岗位编码",
prop: "postCode",
minWidth: 120
},
{
label: "岗位名称",
prop: "postName",
minWidth: 120
},
{
label: "岗位排序",
prop: "postSort",
sortable: "custom",
minWidth: 120
},
{
label: "状态",
prop: "status",
minWidth: 120,
cellRenderer: ({ row, props }) => (
<el-tag
size={props.size}
type={statusMap[row.status].cssTag}
effect="plain"
>
{statusMap[row.status].label}
</el-tag>
)
},
{
label: "创建时间",
minWidth: 160,
prop: "createTime",
sortable: "custom",
formatter: ({ createTime }) =>
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
},
{
label: "操作",
fixed: "right",
width: 140,
slot: "operation"
}
];
function onSortChanged(sort: Sort) {
sortState.value = sort;
// 表格列的排序变化的时候,需要重置分页
pagination.currentPage = 1;
getPostList();
}
async function onSearch(tableRef) {
// 点击搜索的时候,需要重置排序,重新排序的时候会重置分页并发起查询请求
tableRef.getTableRef().sort("postSort", "ascending");
}
function resetForm(formEl, tableRef) {
if (!formEl) return;
// 清空查询参数
formEl.resetFields();
// 清空时间查询 TODO 这块有点繁琐 有可以优化的地方吗?
// Form组件的resetFields方法无法清除datepicker里面的数据。
searchFormParams.beginTime = undefined;
searchFormParams.endTime = undefined;
// 重置分页并查询
onSearch(tableRef);
}
async function getPostList() {
pageLoading.value = true;
CommonUtils.fillSortParams(searchFormParams, sortState.value);
CommonUtils.fillPaginationParams(searchFormParams, pagination);
const { data } = await getPostListApi(toRaw(searchFormParams)).finally(
() => {
pageLoading.value = false;
}
);
dataList.value = data.rows;
pagination.total = data.total;
}
async function exportAllExcel() {
if (sortState.value != null) {
CommonUtils.fillSortParams(searchFormParams, sortState.value);
}
CommonUtils.fillPaginationParams(searchFormParams, pagination);
CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value);
exportPostExcelApi(toRaw(searchFormParams), "岗位数据.xlsx");
}
async function handleDelete(row) {
await deletePostApi([row.postId]).then(() => {
message(`您删除了编号为${row.postId}的这条岗位数据`, {
type: "success"
});
// 刷新列表
getPostList();
});
}
async function handleBulkDelete(tableRef) {
if (multipleSelection.value.length === 0) {
message("请选择需要删除的数据", { type: "warning" });
return;
}
ElMessageBox.confirm(
`确认要<strong>删除</strong>编号为<strong style='color:var(--el-color-primary)'>[ ${multipleSelection.value} ]</strong>的岗位数据吗?`,
"系统提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
dangerouslyUseHTMLString: true,
draggable: true
}
)
.then(async () => {
await deletePostApi(multipleSelection.value).then(() => {
message(`您删除了编号为[ ${multipleSelection.value} ]的岗位数据`, {
type: "success"
});
// 刷新列表
getPostList();
});
})
.catch(() => {
message("取消删除", {
type: "info"
});
// 清空checkbox选择的数据
tableRef.getTableRef().clearSelection();
});
}
onMounted(getPostList);
return {
searchFormParams,
pageLoading,
columns,
dataList,
pagination,
defaultSort,
timeRange,
multipleSelection,
onSearch,
onSortChanged,
exportAllExcel,
// exportExcel,
getPostList,
resetForm,
handleDelete,
handleBulkDelete
};
}