Selaa lähdekoodia

新增职位管理

sunshibo 2 päivää sitten
vanhempi
commit
0529ae2763

+ 12 - 7
src/api/modules/storeDecoration.ts

@@ -374,12 +374,12 @@ export const setOnlineStatus = (params: { id: string | number; onlineStatus: 0 |
 export const getStaffConfigDetail = (params: any) => {
   return httpApi.get(`/alienStore/storeStaffConfig/getStaffConfigDeatail`, params);
 };
-//创建标题
-export const createTitle = (params: any) => {
+/** 新增标题(职位)POST /alienStore/storeStaffTitle/createTitle */
+export const createTitle = (params: { storeId: number | string; titleName: string }) => {
   return httpApi.post(`/alienStore/storeStaffTitle/createTitle`, params);
 };
-//修改标题
-export const updateTitle = (params: any) => {
+/** 编辑标题 POST /alienStore/storeStaffTitle/updateTitle */
+export const updateTitle = (params: { id: string | number; storeId: number | string; titleName: string }) => {
   return httpApi.post(`/alienStore/storeStaffTitle/updateTitle`, params);
 };
 //获取标题列表
@@ -404,7 +404,12 @@ export const getTitleDetail = (params: any) => {
     return Promise.resolve({ code: 200, data: null, msg: "" });
   });
 };
-//删除标题
-export const deleteTitle = (params: any) => {
-  return httpApi.get(`/alienStore/storeStaffTitle/deleteTitle`, params);
+//删除标题(入参 id)
+export const deleteTitle = (params: { id: string | number }) => {
+  return httpApi.get(`/alienStore/storeStaffTitle/deleteTitlev`, params);
+};
+
+/** 职位/标题列表查询(仅 storeId) */
+export const queryStaffTitle = (params: { storeId: number | string }) => {
+  return httpApi.get(`/alienStore/storeStaffTitle/query`, params);
 };

+ 35 - 4
src/assets/json/authMenuList.json

@@ -183,9 +183,9 @@
       ]
     },
     {
-      "path": "/storeDecoration/personnelConfig",
-      "name": "personnelConfig",
-      "component": "/storeDecoration/personnelConfig/index",
+      "path": "/personnelConfig",
+      "name": "personnelConfigMenu",
+      "redirect": "/storeDecoration/positionManagement",
       "meta": {
         "icon": "User",
         "title": "人员配置",
@@ -195,7 +195,38 @@
         "isAffix": false,
         "isKeepAlive": false
       },
-      "children": []
+      "children": [
+        {
+          "path": "/storeDecoration/positionManagement",
+          "name": "positionManagement",
+          "component": "/storeDecoration/positionManagement/index",
+          "meta": {
+            "icon": "Postcard",
+            "title": "职位管理",
+            "activeMenu": "/personnelConfig",
+            "isLink": "",
+            "isHide": false,
+            "isFull": false,
+            "isAffix": false,
+            "isKeepAlive": false
+          }
+        },
+        {
+          "path": "/storeDecoration/personnelConfig",
+          "name": "personnelConfig",
+          "component": "/storeDecoration/personnelConfig/index",
+          "meta": {
+            "icon": "UserFilled",
+            "title": "人员管理",
+            "activeMenu": "/personnelConfig",
+            "isLink": "",
+            "isHide": false,
+            "isFull": false,
+            "isAffix": false,
+            "isKeepAlive": false
+          }
+        }
+      ]
     },
     {
       "path": "/performance",

+ 2 - 0
src/stores/modules/auth.ts

@@ -86,6 +86,8 @@ export const useAuthStore = defineStore({
                 menu.meta.isHide = false;
                 break;
               case "人员配置":
+              case "人员管理":
+              case "职位管理":
                 menu.meta.isHide = !storeId;
                 break;
               case "食品经营许可证":

+ 264 - 0
src/views/storeDecoration/positionManagement/index.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="table-box position-management">
+    <div class="filter-bar">
+      <div class="filter-row">
+        <span class="filter-label">职位</span>
+        <el-input
+          v-model="filterPositionName"
+          clearable
+          placeholder="请输入"
+          style="width: 220px"
+          maxlength="50"
+          @keyup.enter="handleSearch"
+        />
+        <el-button type="primary" @click="handleSearch"> 搜索 </el-button>
+        <el-button @click="handleReset"> 重置 </el-button>
+      </div>
+    </div>
+
+    <ProTable
+      ref="proTable"
+      :columns="columns"
+      :request-api="getTableList"
+      :init-param="initParam"
+      :data-callback="dataCallback"
+      :tool-button="false"
+    >
+      <template #tableHeader>
+        <div class="table-header">
+          <el-button type="primary" @click="openCreate"> 新增职位 </el-button>
+        </div>
+      </template>
+      <template #operation="scope">
+        <el-button link type="primary" @click="openEdit(scope.row)"> 编辑 </el-button>
+        <el-button link type="danger" @click="onDeleteClick(scope.row)"> 删除 </el-button>
+      </template>
+    </ProTable>
+
+    <p v-if="deleteHint" class="delete-hint">
+      {{ deleteHint }}
+    </p>
+
+    <el-dialog
+      v-model="dialogVisible"
+      :title="editId == null ? '新增职位' : '编辑职位'"
+      width="420px"
+      append-to-body
+      destroy-on-close
+      @closed="onDialogClosed"
+    >
+      <el-form ref="formRef" :model="form" :rules="formRules" label-width="72px" @submit.prevent>
+        <el-form-item label="职位" prop="positionName">
+          <el-input v-model="form.positionName" placeholder="请输入" maxlength="50" clearable show-word-limit />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false"> 取消 </el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitForm"> 确定 </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="positionManagement">
+import { ref, reactive } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import type { FormInstance, FormRules } from "element-plus";
+import ProTable from "@/components/ProTable/index.vue";
+import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
+import { localGet } from "@/utils";
+import { queryStaffTitle, createTitle, updateTitle, deleteTitle } from "@/api/modules/storeDecoration";
+
+const proTable = ref<ProTableInstance>();
+const formRef = ref<FormInstance>();
+const filterPositionName = ref("");
+const listPositionName = ref("");
+const deleteHint = ref("");
+const dialogVisible = ref(false);
+const submitLoading = ref(false);
+const editId = ref<string | number | null>(null);
+
+const initParam = reactive({
+  storeId: localGet("geeker-user")?.userInfo?.storeId || localGet("createdId")
+});
+
+const form = reactive({
+  positionName: ""
+});
+
+const formRules: FormRules = {
+  positionName: [{ required: true, message: "请输入职位名称", trigger: "blur" }]
+};
+
+const columns = reactive<ColumnProps[]>([
+  { type: "index", label: "序号", width: 80 },
+  { prop: "positionName", label: "职位", minWidth: 200 },
+  { prop: "operation", label: "操作", fixed: "right", width: 160 }
+]);
+
+const dataCallback = (data: any) => {
+  const raw = data?.records ?? data?.list ?? (Array.isArray(data) ? data : []);
+  const list = (Array.isArray(raw) ? raw : []).map((item: any) => ({
+    id: item.id ?? item.titleId,
+    positionName: item.titleName ?? item.positionName ?? item.name ?? item.position ?? "",
+    staffCount: Number(item.staffCount ?? item.bindStaffCount ?? item.personCount ?? 0)
+  }));
+  const total = data?.total ?? data?.totalCount ?? list.length;
+  return { list, total: Number(total) || 0 };
+};
+
+const getTableList = async (params: any) => {
+  const storeId = params.storeId ?? initParam.storeId;
+  if (!storeId) {
+    return Promise.resolve({ data: { list: [], total: 0 } });
+  }
+  const res: any = await queryStaffTitle({ storeId });
+  const inner = res?.data;
+  const raw = Array.isArray(inner) ? inner : (inner?.records ?? inner?.list ?? []);
+  const mapped = (Array.isArray(raw) ? raw : []).map((item: any) => ({
+    id: item.id ?? item.titleId,
+    positionName: item.titleName ?? item.positionName ?? item.name ?? item.position ?? "",
+    staffCount: Number(item.staffCount ?? item.bindStaffCount ?? item.personCount ?? 0)
+  }));
+  const kw = listPositionName.value?.trim().toLowerCase();
+  const filtered = kw
+    ? mapped.filter((row: { positionName: string }) => (row.positionName || "").toLowerCase().includes(kw))
+    : mapped;
+  const pageNum = params.pageNum ?? 1;
+  const pageSize = params.pageSize ?? 10;
+  const total = filtered.length;
+  const start = (pageNum - 1) * pageSize;
+  const list = filtered.slice(start, start + pageSize);
+  return { data: { list, total } };
+};
+
+const handleSearch = () => {
+  listPositionName.value = filterPositionName.value?.trim() ?? "";
+  proTable.value?.getTableList();
+};
+
+const handleReset = () => {
+  filterPositionName.value = "";
+  listPositionName.value = "";
+  proTable.value?.getTableList();
+};
+
+const clearDeleteHint = () => {
+  deleteHint.value = "";
+};
+
+const onDeleteBlocked = () => {
+  deleteHint.value = "此职位下有所属人员,不可删除";
+};
+
+const onDeleteClick = (row: { id: string | number; positionName: string; staffCount?: number }) => {
+  if (Number(row.staffCount) > 0) onDeleteBlocked();
+  else handleDelete(row);
+};
+
+const openCreate = () => {
+  clearDeleteHint();
+  editId.value = null;
+  form.positionName = "";
+  dialogVisible.value = true;
+};
+
+const openEdit = (row: { id: string | number; positionName: string }) => {
+  clearDeleteHint();
+  editId.value = row.id;
+  form.positionName = row.positionName || "";
+  dialogVisible.value = true;
+};
+
+const onDialogClosed = () => {
+  editId.value = null;
+  form.positionName = "";
+  formRef.value?.resetFields();
+};
+
+const handleDelete = (row: { id: string | number; positionName: string }) => {
+  clearDeleteHint();
+  const storeId = initParam.storeId;
+  if (!storeId) {
+    ElMessage.warning("缺少门店信息");
+    return;
+  }
+  ElMessageBox.confirm(`确定删除职位「${row.positionName || ""}」吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning"
+  })
+    .then(async () => {
+      try {
+        await deleteTitle({ id: row.id });
+        ElMessage.success("删除成功");
+        proTable.value?.getTableList();
+      } catch {
+        /* 错误提示由请求拦截器统一处理 */
+      }
+    })
+    .catch(() => {});
+};
+
+const submitForm = async () => {
+  if (!formRef.value) return;
+  try {
+    await formRef.value.validate();
+  } catch {
+    return;
+  }
+  const storeId = initParam.storeId;
+  if (!storeId) {
+    ElMessage.warning("缺少门店信息");
+    return;
+  }
+  submitLoading.value = true;
+  try {
+    const titleName = form.positionName.trim();
+    if (editId.value != null) {
+      await updateTitle({ storeId, id: editId.value, titleName });
+    } else {
+      await createTitle({ storeId, titleName });
+    }
+    ElMessage.success(editId.value == null ? "新增成功" : "保存成功");
+    dialogVisible.value = false;
+    proTable.value?.getTableList();
+  } catch {
+    /* 错误提示由请求拦截器统一处理 */
+  } finally {
+    submitLoading.value = false;
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.position-management {
+  padding: 0;
+}
+.filter-bar {
+  padding: 0 4px;
+  margin-bottom: 12px;
+}
+.filter-row {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+  align-items: center;
+}
+.filter-label {
+  min-width: 36px;
+  font-size: 14px;
+  color: #606266;
+}
+.table-header {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  padding: 12px 0;
+}
+.delete-hint {
+  margin: 8px 4px 0;
+  font-size: 13px;
+  color: var(--el-color-danger);
+}
+</style>