zhengyiming
2025-02-18 c3207c8517780ec2019bd1e0943a62cc29b265d0
feat: api
4个文件已添加
5个文件已修改
478 ■■■■■ 已修改文件
src/constants/dic.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/enum.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/hooks/dic.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/hooks/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/typings.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/DictionaryManage/SearchSetting.vue 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/DictionaryManage/components/AddOrEditSearchSetting.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/dic.ts
New file
@@ -0,0 +1,23 @@
export enum SearchType {
  /**身份 */
  Identity = 210,
  /**学历 */
  Education = 220,
  /**岗位 */
  Position = 230,
  /**证书类型 */
  CertificateType = 240,
  /**福利 */
  Welfare = 250,
  /**行业类型 */
  IndustryCategory = 260,
}
export const SearchTypeText = {
  [SearchType.Identity]: '身份',
  [SearchType.Education]: '学历',
  [SearchType.Position]: '岗位',
  [SearchType.CertificateType]: '证书类型',
  [SearchType.Welfare]: '福利',
  [SearchType.IndustryCategory]: '行业类型',
};
src/constants/enum.ts
@@ -38,3 +38,8 @@
  Role = 1,
  User,
}
export const BooleanOptions = [
  { label: '是', value: true },
  { label: '否', value: false },
];
src/constants/index.ts
@@ -9,3 +9,4 @@
export * from './menu';
export * from './role';
export * from './app';
export * from './dic';
src/hooks/dic.ts
New file
@@ -0,0 +1,55 @@
import * as searchSettingServices from '@/services/api/SearchSetting';
import { useQuery } from '@tanstack/vue-query';
import { useQueryClient } from '@tanstack/vue-query';
import { SearchType } from '@/constants';
type UseSearchSettingTypeOptions = {
  searchType: number;
  belongType?: number;
  onSuccess?: (data: API.GetTypeSearchSettingList[]) => any;
};
export function useSearchSettingType({
  searchType,
  belongType = null,
  onSuccess,
}: UseSearchSettingTypeOptions) {
  const { data, refetch } = useQuery({
    queryKey: ['searchSettingServices/getTypeSearchSettingList', { searchType, belongType }],
    queryFn: async () => {
      return await searchSettingServices.getTypeSearchSettingList(
        {
          searchType: searchType,
          belongType: belongType,
        },
        { showLoading: false }
      );
    },
    placeholderData: () => [] as API.GetTypeSearchSettingList[],
    onSuccess(data) {
      onSuccess?.(data);
    },
  });
  const queryClient = useQueryClient();
  async function ensureSearchSettingType() {
    return await queryClient.ensureQueryData({
      queryKey: [
        'searchSettingServices/getTypeSearchSettingList',
        { searchType: searchType, belongType: belongType },
      ],
    });
  }
  function getSearchSettingTypeNameById(id: string) {
    return data.value.find((x) => x.id === id)?.name ?? '';
  }
  return {
    searchSettingTypeList: data,
    ensureSearchSettingType,
    refetchSearchSettingType: refetch,
    getSearchSettingTypeNameById,
  };
}
src/hooks/index.ts
@@ -6,3 +6,4 @@
export * from './useEvent';
export * from './useUser';
export * from './help';
export * from './dic';
src/services/api/typings.d.ts
@@ -713,6 +713,7 @@
    clickCount?: number;
    src?: string;
    isRecommend?: boolean;
    searchType?: number;
  }
  interface GetSearchSettingListInput {
src/utils/request/index.ts
@@ -170,7 +170,6 @@
  requestInterceptors: [
    [
      (config) => {
        console.log('req config: ', config);
        const $config = config;
        // 开启进度条动画
        if (config.needNProcess) {
src/views/DictionaryManage/SearchSetting.vue
New file
@@ -0,0 +1,244 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableQueryFilterBar @on-reset="reset">
        <template #query>
          <QueryFilterItem>
            <FieldSelect
              v-model="extraParamState.searchType"
              @change="getList()"
              :value-enum="SearchTypeText"
              placeholder="请选择所属类别"
            ></FieldSelect>
          </QueryFilterItem>
          <QueryFilterItem v-if="extraParamState.searchType === SearchType.Position">
            <FieldSelect
              v-model="extraParamState.parentId"
              @change="getList()"
              :value-enum="typeList"
              enum-label-key="name"
              enum-value-key="id"
              placeholder="请选择行业类型"
              clearable
            ></FieldSelect>
          </QueryFilterItem>
          <QueryFilterItem>
            <SearchInput
              v-model="extraParamState.name"
              style="width: 200px"
              placeholder="请输入名称"
              @on-click-search="getList"
              @keyup.enter="getList()"
            >
            </SearchInput>
          </QueryFilterItem>
        </template>
        <template #btn>
          <el-button
            v-if="checkSubModuleItemShow('pageButton', 'addBtn')"
            @click="openDialog()"
            icon="Plus"
            type="primary"
            >新增</el-button
          >
        </template>
      </ProTableQueryFilterBar>
      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
        <template #columns="{ row, column }">
          <template v-if="column.property === 'status'">
            <FieldSwitch
              v-model="row.status"
              active-text="显示"
              inactive-text="隐藏"
              :before-change="() => setCategoryVis(row)"
            />
          </template>
        </template>
      </ProTableV2>
    </AppContainer>
    <AddOrEditSearchSetting v-bind="dialogProps" :typeList="typeList" />
  </LoadingLayout>
</template>
<script setup lang="ts">
import {
  ProTableQueryFilterBar,
  OperationBtnType,
  ProTableV2,
  SearchInput,
  LoadingLayout,
  AppContainer,
  QueryFilterItem,
  useTable,
  useFormDialog,
  UploadUserFile,
  FieldSwitch,
  FieldSelect,
  FieldRadio,
} from '@bole-core/components';
import { useAccess } from '@/hooks';
import * as searchSettingServices from '@/services/api/SearchSetting';
import {
  SearchType,
  SearchTypeText,
  // BelongType,
  // BelongTypeText,
  BooleanOptions,
} from '@/constants';
import { OrderInputType, Message } from '@bole-core/core';
import AddOrEditSearchSetting from './components/AddOrEditSearchSetting.vue';
import { convertApi2FormUrl } from '@/utils';
import { useQueryClient } from '@tanstack/vue-query';
import { useSearchSettingType } from '@/hooks';
defineOptions({
  name: 'SearchSetting',
});
const operationBtnMap: Record<string, OperationBtnType> = {
  editBtn: { emits: { onClick: (role) => openDialog(role) } },
};
const { checkSubModuleItemShow, column, operationBtns } = useAccess({
  operationBtnMap,
});
const BaseState = {
  loading: true,
};
const queryClient = useQueryClient();
const { searchSettingTypeList: typeList } = useSearchSettingType({
  searchType: SearchType.IndustryCategory,
});
const state = reactive({ ...BaseState });
onMounted(async () => {
  await getList();
  state.loading = false;
});
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
  reset,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.GetSearchSettingListInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
        name: extraParamState.name,
        // belongType: Number(extraParamState.belongType),
        searchType: Number(extraParamState.searchType),
        status: extraParamState.status,
      };
      if (extraParamState.searchType === SearchType.Position) {
        params.isRecommend = extraParamState.isRecommend;
        params.parentId = extraParamState.parentId;
      }
      let res = await searchSettingServices.getSearchSettingList(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {
      console.log('error: ', error);
    }
  },
  {
    defaultExtraParams: {
      name: '',
      searchType: SearchType.Identity,
      orderInput: [{ property: 'sort', order: OrderInputType.Asc }],
      status: '' as any as boolean,
      isRecommend: '' as any as boolean,
      parentId: '',
    },
    queryKey: ['searchSettingServices/getSearchSettingList'],
    columnsRenderProps: {
      searchType: { type: 'enum', valueEnum: SearchTypeText },
    },
  }
);
function openDialog(row?: API.GetSearchSettingList) {
  if (row) {
    handleEdit({
      id: row.id,
      searchType: extraParamState.searchType,
      name: row.name,
      sort: row.sort,
      status: row.status,
      src: row.src?.length ? [convertApi2FormUrl(row.src)] : [],
      parentId: row.parentId ?? '',
    });
  } else {
    handleAdd({
      searchType: extraParamState.searchType,
    });
  }
}
const { dialogProps, handleAdd, handleEdit, editForm, dialogState } = useFormDialog({
  onConfirm: handleAddOrEdit,
  defaultFormParams: {
    id: '',
    searchType: SearchType.Identity,
    name: '',
    sort: 0,
    status: true,
    src: [] as UploadUserFile[],
    parentId: '',
  },
});
async function handleAddOrEdit() {
  try {
    let params: API.CreateOrEditSearchInput = {
      searchType: extraParamState.searchType,
      name: editForm.name,
      sort: editForm.sort,
      status: editForm.status,
      src: editForm.src?.[0]?.path ?? '',
      parentId: editForm.parentId ?? '',
    };
    if (editForm.id) {
      params.id = editForm.id;
    }
    let res = await searchSettingServices.createOrEditSearchSetting(params);
    if (res) {
      Message.successMessage('操作成功');
      getList(paginationState.pageIndex);
      dialogState.dialogVisible = false;
      updateCategoryMenu();
    }
  } catch (error) {}
}
function updateCategoryMenu() {
  queryClient.invalidateQueries({
    queryKey: [
      'searchSettingServices/getTypeSearchSettingList',
      { searchType: extraParamState.searchType, belongType: null },
    ],
  });
}
async function setCategoryVis(row: API.GetSearchSettingList) {
  try {
    let params: API.EnableSearchSettingInput = {
      id: row.id,
      status: !row.status,
    };
    let res = await searchSettingServices.enableSearchSetting(params);
    updateCategoryMenu();
    getList(paginationState.pageIndex);
    return !!res;
  } catch (error) {}
}
</script>
src/views/DictionaryManage/components/AddOrEditSearchSetting.vue
New file
@@ -0,0 +1,147 @@
<template>
  <ProDialog
    :title="innerForm.title"
    v-model="innerVisible"
    @close="onDialogClose"
    destroy-on-close
    draggable
  >
    <ProForm :rules="rules" :model="innerForm" ref="dialogForm" label-width="90px">
      <ProFormItemV2 label="行业类型:" prop="parentId" v-if="showWorkSearchType">
        <ProFormSelect
          v-model="innerForm.parentId"
          :value-enum="typeList"
          enum-value-key="id"
          enum-label-key="name"
        />
      </ProFormItemV2>
      <ProFormItemV2 label="名称:" prop="name">
        <ProFormText
          placeholder="请输入名称"
          v-model.trim="innerForm.name"
          :maxlength="15"
        ></ProFormText>
      </ProFormItemV2>
      <ProFormItemV2 label="排序:" prop="sort" required>
        <ProFormInputNumber
          v-model="innerForm.sort"
          :controls="false"
          :min="0"
          :max="999"
        ></ProFormInputNumber>
      </ProFormItemV2>
      <ProFormItemV2
        label="类别图标:"
        prop="src"
        :check-rules="[{ type: 'upload', required: false }]"
      >
        <ProFormImageUpload
          v-model:file-url="innerForm.src"
          :limitFileCount="1"
        ></ProFormImageUpload>
      </ProFormItemV2>
      <ProFormItemV2 label="状态:" prop="status" required>
        <el-radio-group v-model="innerForm.status">
          <el-radio-button :value="true">显示</el-radio-button>
          <el-radio-button :value="false">隐藏</el-radio-button>
        </el-radio-group>
      </ProFormItemV2>
    </ProForm>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="emit('onCancel')">取 消</el-button>
        <el-button type="primary" @click="handleConfirm">确 定</el-button>
      </span>
    </template>
  </ProDialog>
</template>
<script setup lang="ts">
import { FormRules, FormInstance } from 'element-plus';
import { SearchType } from '@/constants';
import {
  ProDialog,
  ProForm,
  ProFormItemV2,
  ProFormRadio,
  ProFormText,
  ProFormInputNumber,
  ProFormImageUpload,
  UploadUserFile,
  ProFormSelect,
} from '@bole-core/components';
defineOptions({
  name: 'AddOrEditSearchSetting',
});
type Props = {
  modelValue: boolean;
  form?: {
    title?: string;
    searchType: number;
    name: string;
    sort: number;
    status: boolean;
    src: UploadUserFile[];
    parentId?: string;
  };
  typeList: API.GetTypeSearchSettingList[];
};
const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
});
const showWorkSearchType = computed(() => props.form.searchType === SearchType.Position);
const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void;
  (e: 'update:form', value: Props['form']): void;
  (e: 'onConfirm'): void;
  (e: 'onCancel'): void;
}>();
const dialogForm = ref<FormInstance>();
const innerVisible = computed({
  get() {
    return props.modelValue;
  },
  set(val) {
    emit('update:modelValue', val);
  },
});
const innerForm = computed({
  get() {
    return props.form;
  },
  set(val) {
    emit('update:form', val);
  },
});
const rules = reactive<FormRules>({
  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
  sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
  parentId: [{ required: true, message: '请选择所属类别', trigger: 'change' }],
});
function onDialogClose() {
  if (!dialogForm.value) return;
  dialogForm.value.resetFields();
}
function handleConfirm() {
  if (!dialogForm.value) return;
  dialogForm.value.validate((valid) => {
    if (valid) {
      emit('onConfirm');
    } else {
      return;
    }
  });
}
</script>