sunshibo 2 dagar sedan
förälder
incheckning
65d6dd429f

+ 5 - 1
src/api/index.ts

@@ -16,6 +16,8 @@ export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
   loading?: boolean;
   cancel?: boolean;
   encrypt?: boolean; // 是否加密请求参数
+  /** 业务 code 非成功时不弹出全局 ElMessage,由调用方自行处理 */
+  hideBusinessErrorMessage?: boolean;
 }
 
 const config = {
@@ -101,7 +103,9 @@ class RequestHttp {
         }
         // 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
         if (processedData.code && processedData.code !== ResultEnum.SUCCESS) {
-          ElMessage.error(processedData.msg);
+          if (!config?.hideBusinessErrorMessage) {
+            ElMessage.error(processedData.msg);
+          }
           return Promise.reject(processedData);
         }
         // 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)

+ 5 - 1
src/api/indexApi.ts

@@ -15,6 +15,8 @@ export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
   loading?: boolean;
   cancel?: boolean;
   encrypt?: boolean; // 是否加密请求参数
+  /** 业务 code 非成功时不弹出全局 ElMessage,由调用方自行处理 */
+  hideBusinessErrorMessage?: boolean;
 }
 
 const config = {
@@ -102,7 +104,9 @@ class RequestHttp {
         }
         // 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
         if (processedData.code && processedData.code !== ResultEnum.SUCCESS) {
-          ElMessage.error(processedData.msg);
+          if (!config?.hideBusinessErrorMessage) {
+            ElMessage.error(processedData.msg);
+          }
           return Promise.reject(processedData);
         }
         // 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)

+ 9 - 3
src/api/modules/storeDecoration.ts

@@ -2,6 +2,7 @@ import { ResPage, StoreUser } from "@/api/interface/index";
 import { PORT_NONE } from "@/api/config/servicePort";
 import http from "@/api";
 import httpApi from "@/api/indexApi";
+import type { CustomAxiosRequestConfig } from "@/api/indexApi";
 import { uploadFormDataSimpleCompat } from "@/api/upload.js";
 
 /**
@@ -375,8 +376,13 @@ export const getStaffConfigDetail = (params: any) => {
   return httpApi.get(`/alienStore/storeStaffConfig/getStaffConfigDeatail`, params);
 };
 /** 新增标题(职位)POST /alienStore/storeStaffTitle/createTitle */
-export const createTitle = (params: { storeId: number | string; titleName: string }) => {
-  return httpApi.post(`/alienStore/storeStaffTitle/createTitle`, params);
+export const createTitle = (
+  params: { storeId: number | string; titleName: string },
+  requestConfig?: Pick<CustomAxiosRequestConfig, "hideBusinessErrorMessage" | "loading" | "cancel">
+) => {
+  return httpApi.post(`/alienStore/storeStaffTitle/createTitle`, params, {
+    ...(requestConfig ?? {})
+  } as CustomAxiosRequestConfig);
 };
 /** 编辑标题 POST /alienStore/storeStaffTitle/updateTitle */
 export const updateTitle = (params: { id: string | number; storeId: number | string; titleName: string }) => {
@@ -406,7 +412,7 @@ export const getTitleDetail = (params: any) => {
 };
 //删除标题(入参 id)
 export const deleteTitle = (params: { id: string | number }) => {
-  return httpApi.get(`/alienStore/storeStaffTitle/deleteTitlev`, params);
+  return httpApi.get(`/alienStore/storeStaffTitle/deleteTitle`, params);
 };
 
 /** 职位/标题列表查询(仅 storeId) */

+ 111 - 66
src/views/storeDecoration/personnelConfig/index.vue

@@ -1,29 +1,9 @@
 <template>
   <div class="personnel-config-container">
-    <!-- 操作按钮区域 -->
-    <div class="action-section">
-      <el-button v-if="!hasTitle" type="primary" @click="handleCreateTitle"> 创建标题 </el-button>
-      <el-button type="primary" @click="openCreateDialog"> 创建人员 </el-button>
-    </div>
-
-    <!-- 表格区域 -->
+    <!-- 表格区域:工具栏在表格上方左侧 -->
     <div class="table-box button-table">
-      <div class="table-header-title">
-        <span class="table-title">演出人员</span>
-        <el-dropdown v-if="hasTitle" trigger="click" @command="handleTitleCommand">
-          <el-icon class="table-icon">
-            <ArrowDown />
-          </el-icon>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item command="edit"> 编辑标题 </el-dropdown-item>
-              <el-dropdown-item command="delete"> 删除标题 </el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-        <el-icon v-else class="table-icon">
-          <ArrowDown />
-        </el-icon>
+      <div class="personnel-table-toolbar">
+        <el-button type="primary"> 人员 </el-button>
       </div>
       <ProTable
         ref="proTable"
@@ -32,6 +12,11 @@
         :init-param="initParam"
         :data-callback="dataCallback"
       >
+        <template #tableHeader>
+          <div class="personnel-table-header-actions">
+            <el-button type="primary" @click="openCreateDialog"> 新增人员 </el-button>
+          </div>
+        </template>
         <template #empty>
           <div class="personnel-config-empty">暂无数据</div>
         </template>
@@ -44,7 +29,7 @@
           <!-- 已驳回(status === 2):显示编辑、删除、查看详情 -->
           <template v-else-if="scope.row.status === 2 || scope.row.status === '2'">
             <el-button type="primary" link @click="editPersonnel(scope.row, 0)"> 编辑 </el-button>
-            <el-button type="primary" link @click="deletePersonnelHandler(scope.row.id!, 0)"> 删除 </el-button>
+            <el-button type="danger" link @click="deletePersonnelHandler(scope.row.id!, 0)"> 删除 </el-button>
             <el-button type="primary" link @click="handleViewDetail(scope.row)"> 查看详情 </el-button>
           </template>
 
@@ -76,7 +61,7 @@
               取消置顶
             </el-button>
             <el-button v-else type="primary" link @click="handlePin(scope.row)"> 置顶 </el-button>
-            <el-button type="primary" link @click="deletePersonnelHandler(scope.row.id!, 0)"> 删除 </el-button>
+            <el-button type="danger" link @click="deletePersonnelHandler(scope.row.id!, 0)"> 删除 </el-button>
             <el-button type="primary" link @click="handleViewDetail(scope.row)"> 查看详情 </el-button>
           </template>
 
@@ -180,18 +165,30 @@
           <div class="detail-item">
             <div class="detail-label">在线状态:</div>
             <div class="detail-value">
-              <span v-if="personnelDetail.onlineStatus === 0 || personnelDetail.onlineStatus === '0'">上线</span>
-              <span v-else-if="personnelDetail.onlineStatus === 1 || personnelDetail.onlineStatus === '1'">下线</span>
-              <span v-else>—</span>
+              <span v-if="personnelDetail.onlineStatus === 1 || personnelDetail.onlineStatus === '1'" class="personnel-cell-dash"
+                >—</span
+              >
+              <span
+                v-else-if="personnelDetail.onlineStatus === 0 || personnelDetail.onlineStatus === '0'"
+                class="personnel-cell-online-up"
+                >在线</span
+              >
+              <span v-else class="personnel-cell-dash">—</span>
             </div>
           </div>
           <div class="detail-item">
             <div class="detail-label">审核状态:</div>
             <div class="detail-value">
-              <el-tag v-if="personnelDetail.status === 1 || personnelDetail.status === '1'" type="success"> 已通过 </el-tag>
-              <el-tag v-else-if="personnelDetail.status === 2 || personnelDetail.status === '2'" type="danger"> 已驳回 </el-tag>
-              <el-tag v-else-if="personnelDetail.status === 0 || personnelDetail.status === '0'" type="info"> 待审核 </el-tag>
-              <span v-else>—</span>
+              <span v-if="personnelDetail.status === 1 || personnelDetail.status === '1'" class="personnel-cell-pass"
+                >已通过</span
+              >
+              <span v-else-if="personnelDetail.status === 2 || personnelDetail.status === '2'" class="personnel-cell-reject"
+                >已驳回</span
+              >
+              <span v-else-if="personnelDetail.status === 0 || personnelDetail.status === '0'" class="personnel-cell-pending"
+                >待审核</span
+              >
+              <span v-else class="personnel-cell-dash">—</span>
             </div>
           </div>
           <div v-if="personnelDetail.status === 2 || personnelDetail.status === '2'" class="detail-item">
@@ -330,7 +327,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted, watch, nextTick } from "vue";
+import { ref, reactive, computed, onMounted, watch, nextTick, h } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
 import type { FormInstance, FormRules } from "element-plus";
 import { Picture, Plus, Delete, ArrowDown, VideoPlay } from "@element-plus/icons-vue";
@@ -411,10 +408,10 @@ const titleRules = reactive<FormRules>({
 // 标题弹窗标题
 const titleDialogTitle = computed(() => (titleEditId.value !== null ? "修改标题" : "创建标题"));
 
-// 在线状态枚举 - 0: 上线, 1: 下线
+// 在线状态枚举 - 0: 在线, 1: 离线(列表展示为「—」)
 const onlineStatusEnum = [
-  { label: "线", value: 0 },
-  { label: "线", value: 1 }
+  { label: "线", value: 0 },
+  { label: "线", value: 1 }
 ];
 
 // 审核状态枚举
@@ -441,6 +438,29 @@ const formatTime = (time: string | null | undefined) => {
   }
 };
 
+/** ProTable「职位」筛选项:GET alienStore/storeStaffTitle/query?storeId= */
+async function fetchPositionSearchEnum() {
+  const storeId = localGet("geeker-user")?.userInfo?.storeId || localGet("createdId");
+  if (!storeId) return { data: [] as { label: string; value: string }[] };
+  try {
+    const res: any = await queryStaffTitle({ storeId });
+    const inner = res?.data;
+    const raw = Array.isArray(inner) ? inner : (inner?.records ?? inner?.list ?? []);
+    const arr = Array.isArray(raw) ? raw : [];
+    const seen = new Set<string>();
+    const list: { label: string; value: string }[] = [];
+    for (const item of arr) {
+      const title = String(item.titleName ?? item.name ?? item.positionName ?? "").trim();
+      if (!title || seen.has(title)) continue;
+      seen.add(title);
+      list.push({ label: title, value: title });
+    }
+    return { data: list };
+  } catch {
+    return { data: [] };
+  }
+}
+
 // 表格配置项
 const columns = reactive<ColumnProps<any>[]>([
   {
@@ -465,9 +485,11 @@ const columns = reactive<ColumnProps<any>[]>([
     prop: "position",
     label: "职位",
     minWidth: 100,
+    fieldNames: { label: "label", value: "value" },
+    enum: fetchPositionSearchEnum,
     search: {
-      el: "input",
-      props: { placeholder: "请输入" },
+      el: "select",
+      props: { placeholder: "请选择", filterable: true, clearable: true },
       order: 2
     }
   },
@@ -494,9 +516,13 @@ const columns = reactive<ColumnProps<any>[]>([
       order: 3
     },
     render: (scope: any) => {
-      if (scope.row.onlineStatus === 0 || scope.row.onlineStatus === "0") return "上线";
-      if (scope.row.onlineStatus === 1 || scope.row.onlineStatus === "1") return "下线";
-      return "—";
+      if (scope.row.onlineStatus === 1 || scope.row.onlineStatus === "1") {
+        return h("span", { class: "personnel-cell-dash" }, "—");
+      }
+      if (scope.row.onlineStatus === 0 || scope.row.onlineStatus === "0") {
+        return h("span", { class: "personnel-cell-online-up" }, "在线");
+      }
+      return h("span", { class: "personnel-cell-dash" }, "—");
     }
   },
   {
@@ -505,17 +531,22 @@ const columns = reactive<ColumnProps<any>[]>([
     width: 120,
     enum: approvalStatusEnum,
     fieldNames: { label: "label", value: "value" },
-    tag: true,
     search: {
       el: "select",
       props: { placeholder: "请选择" },
       order: 4
     },
     render: (scope: any) => {
-      if (scope.row.approvalStatus === 1 || scope.row.approvalStatus === "1") return "已通过";
-      if (scope.row.approvalStatus === 2 || scope.row.approvalStatus === "2") return "已驳回";
-      if (scope.row.approvalStatus === 0 || scope.row.approvalStatus === "0") return "待审核";
-      return "—";
+      if (scope.row.approvalStatus === 1 || scope.row.approvalStatus === "1") {
+        return h("span", { class: "personnel-cell-pass" }, "已通过");
+      }
+      if (scope.row.approvalStatus === 2 || scope.row.approvalStatus === "2") {
+        return h("span", { class: "personnel-cell-reject" }, "已驳回");
+      }
+      if (scope.row.approvalStatus === 0 || scope.row.approvalStatus === "0") {
+        return h("span", { class: "personnel-cell-pending" }, "待审核");
+      }
+      return h("span", { class: "personnel-cell-dash" }, "—");
     }
   },
   {
@@ -1226,8 +1257,13 @@ const handleTitleSubmit = async () => {
       params.id = titleEditId.value;
       res = await updateTitle(params);
     } else {
-      // 创建标题
-      res = await createTitle(params);
+      try {
+        res = await createTitle(params, { hideBusinessErrorMessage: true });
+      } catch {
+        ElMessage.error("此名称已存在");
+        titleSubmitLoading.value = false;
+        return;
+      }
     }
 
     if (res && (res.code === 200 || res.code === "200")) {
@@ -2300,31 +2336,40 @@ onMounted(async () => {
   min-height: 100%;
   padding: 20px;
   background-color: white;
-  .action-section {
+  .personnel-table-toolbar {
     display: flex;
+    flex-wrap: wrap;
     gap: 12px;
+    align-items: center;
     margin-bottom: 16px;
+    .title-dropdown-trigger {
+      display: inline-flex;
+      align-items: center;
+    }
+  }
+
+  /* 列表:在线状态 / 审核状态 文字颜色 */
+  :deep(.personnel-cell-online-up) {
+    color: var(--el-color-success);
+  }
+  :deep(.personnel-cell-dash) {
+    color: var(--el-text-color-secondary);
+  }
+  :deep(.personnel-cell-pass) {
+    color: var(--el-color-success);
+  }
+  :deep(.personnel-cell-reject) {
+    color: var(--el-color-danger);
+  }
+  :deep(.personnel-cell-pending) {
+    color: var(--el-color-warning);
   }
   .table-box {
-    .table-header-title {
+    :deep(.personnel-table-header-actions) {
       display: flex;
       align-items: center;
-      margin-bottom: 16px;
-      font-size: 16px;
-      font-weight: 500;
-      color: var(--el-text-color-primary);
-      .table-title {
-        margin-right: 8px;
-      }
-      .table-icon {
-        font-size: 16px;
-        color: var(--el-text-color-regular);
-        cursor: pointer;
-        transition: transform 0.3s;
-        &:hover {
-          color: var(--el-color-primary);
-        }
-      }
+      width: 100%;
+      padding: 12px 0;
     }
 
     /* 列表为空时去掉空状态残留图形/背景,仅显示「暂无数据」文字 */

+ 6 - 1
src/views/storeDecoration/positionManagement/index.vue

@@ -218,7 +218,12 @@ const submitForm = async () => {
     if (editId.value != null) {
       await updateTitle({ storeId, id: editId.value, titleName });
     } else {
-      await createTitle({ storeId, titleName });
+      try {
+        await createTitle({ storeId, titleName }, { hideBusinessErrorMessage: true });
+      } catch {
+        ElMessage.error("此名称已存在");
+        return;
+      }
     }
     ElMessage.success(editId.value == null ? "新增成功" : "保存成功");
     dialogVisible.value = false;