wupengfei
2025-03-11 286f1b727856d6b32a8d237f353ae008f3076deb
feat: 接口对接
36个文件已修改
2个文件已添加
1077 ■■■■ 已修改文件
apps/bMiniApp/src/constants/task.ts 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/flexJob/flexJobDetailFromTask/InnerPage.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/flexJobManage/flexJobManage/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/flexJobManage/flexJobSign/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/jobApplicationManage/components/JobDetail.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/jobApplicationManage/components/SignList.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/jobApplicationManage/jobApplicationManage/InnerPage.vue 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/jobApplicationManage/jobApplicationManage/jobApplicationManage.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/mine/mineContactRecord/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/mine/mineFavorites/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/batchTaskList/InnerPage.vue 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/components/TaskCheckCard.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/components/TaskCheckPersonalView.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/publishTask/InnerPage.vue 176 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/publishTask/publishTask.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/taskCheck/InnerPage.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/taskCheckDetail/InnerPage.vue 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/taskManage/InnerPage.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/subpackages/task/taskManage/taskManage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/hooks/task.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/subpackages/mine/mineAgreementSign/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/subpackages/mine/mineCancel/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/subpackages/mine/mineCollectTask/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/subpackages/mine/mineHire/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/cMiniApp/src/subpackages/mine/mineSign/InnerPage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/ActionSheet/CheckboxActionSheet.vue 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/FlexJobCard.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/FlexJobTopView.vue 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/JobApplicationCard.vue 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/MyTaskCard.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/TaskCard.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Card/TaskPrice.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Input/ChooseInputWithCheckbox.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/Input/ChooseInputWithDatePicker.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/components/src/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/constants/dic.ts 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/hooks/area.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/services/api/typings.d.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
apps/bMiniApp/src/constants/task.ts
@@ -71,3 +71,61 @@
  [FlexTaskSettleTypeEnum.OfWeek]: '周结',
  [FlexTaskSettleTypeEnum.OfDay]: '日结',
};
export enum FlexTaskReleaseStatusEnum {
  /**
   * 发布中
   */
  Releasing = 10,
  /**
   * 已停止
   */
  Stoping = 20,
}
export const FlexTaskReleaseStatusEnumText = {
  [FlexTaskReleaseStatusEnum.Releasing]: '发布中',
  [FlexTaskReleaseStatusEnum.Stoping]: '已停止',
};
export enum FlexTaskWorkerHireEnum {
  /**
   * 待处理
   */
  Wait = 10,
  /**
   * 已录用
   */
  Hired = 20,
  /**
   * 已谢绝
   */
  Refused = 30,
}
export const FlexTaskWorkerHireEnumText = {
  [FlexTaskWorkerHireEnum.Wait]: '待处理',
  [FlexTaskWorkerHireEnum.Hired]: '已录用',
  [FlexTaskWorkerHireEnum.Refused]: '已谢绝',
};
export enum FlexTaskCheckAcceptStatusEnum {
  /**
   * 待验收
   */
  Wait = 10,
  /**
   * 验收通过
   */
  Passed = 20,
  /**
   * 验收未通过
   */
  Refused = 30,
}
export const FlexTaskCheckAcceptStatusEnumText = {
  [FlexTaskCheckAcceptStatusEnum.Wait]: '待验收',
  [FlexTaskCheckAcceptStatusEnum.Passed]: '验收通过',
  [FlexTaskCheckAcceptStatusEnum.Refused]: '验收未通过',
};
apps/bMiniApp/src/subpackages/flexJob/flexJobDetailFromTask/InnerPage.vue
@@ -2,8 +2,15 @@
  <LoadingLayout :loading="isLoading" :error="isError" :loadError="refetch">
    <JobDetailContent :isCollapse="true">
      <template #footer>
        <PageFooterBtn type="primary" plain>谢绝</PageFooterBtn>
        <PageFooterBtn type="primary">录用</PageFooterBtn>
        <PageFooterBtn
          type="primary"
          plain
          @click="taskWorkerHireRefuse(FlexTaskWorkerHireEnum.Refused)"
          >谢绝</PageFooterBtn
        >
        <PageFooterBtn type="primary" @click="taskWorkerHireRefuse(FlexTaskWorkerHireEnum.Hired)"
          >录用</PageFooterBtn
        >
      </template>
    </JobDetailContent>
  </LoadingLayout>
@@ -13,6 +20,8 @@
import Taro from '@tarojs/taro';
import { useQuery } from '@tanstack/vue-query';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { Message } from '@12333/utils';
import { FlexTaskWorkerHireEnum } from '@/constants/task';
defineOptions({
  name: 'InnerPage',
@@ -27,17 +36,34 @@
  data: detail,
  refetch,
} = useQuery({
  queryKey: ['flexWorkerServices/getOrdeForDetail', taskId],
  queryKey: ['flexWorkerServices/getFlexTaskDto', taskId],
  queryFn: async () => {
    return await flexWorkerServices.getOrdeForDetail(
    return await flexWorkerServices.getFlexTaskDto(
      { id: taskId },
      {
        showLoading: false,
      }
    );
  },
  placeholderData: () => ({} as API.OrderInfoDto),
  placeholderData: () => ({} as API.GetFlexTaskDtoOutput),
});
async function taskWorkerHireRefuse(hireStatus: FlexTaskWorkerHireEnum) {
  try {
    let params: API.TaskWorkerHireRefuseInput = {
      flexTaskId: taskId,
      flexWorkerId: detail.value?.taskId,
      hireStatus: hireStatus,
    };
    let res = await flexWorkerServices.taskWorkerHireRefuse(params);
    if (res) {
      Message.success('操作成功');
      refetch({
        type: 'inactive',
      });
    }
  } catch (error) {}
}
</script>
<style lang="scss">
apps/bMiniApp/src/subpackages/flexJobManage/flexJobManage/InnerPage.vue
@@ -59,7 +59,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/bMiniApp/src/subpackages/flexJobManage/flexJobSign/InnerPage.vue
@@ -52,7 +52,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/bMiniApp/src/subpackages/jobApplicationManage/components/JobDetail.vue
@@ -1,15 +1,21 @@
<template>
  <LoadingLayout :loading="isLoading" :error="isError" :loadError="refetch">
    <ContentScrollView style="background-color: transparent">
      <Cell :title="'客房服务员'" titleSize="large">
        <div class="taskDetail-time">2025年2月5日 至 2025年3月5日</div>
      <Cell :title="detail.taskName" titleSize="large">
        <div class="taskDetail-time">
          {{
            `${dayjs(detail.startDate).format('YYYY年MM月DD日')}至${dayjs(detail.endDate).format(
              'YYYY年MM月DD日'
            )}`
          }}
        </div>
        <div class="task-card-welfare-wrapper">
          <div class="task-card-welfare-list">
            <div class="task-card-welfare-list-item">日结</div>
            <div class="task-card-welfare-list-item">男女不限</div>
            <div class="task-card-welfare-list-item">包三餐</div>
            <div class="task-card-welfare-list-item">
              {{ FlexTaskSettleTypeEnumText[detail.settleType] }}
          </div>
          <TaskPrice :value="212" />
          </div>
          <TaskPrice :value="detail.fee" :unit="SalaryTimeTypeEnumUnit[detail.feeType]" />
        </div>
        <div class="taskDetail-address-wrapper">
          <div class="taskDetail-address-title-wrapper">
@@ -25,11 +31,12 @@
      <Cell :show-title="false">
        <CellChunk title="福利信息">
          <div class="taskDetail-welfare-list">
            <TaskDetailWelfareItem :icon="IconLocaltion" text="高温补贴" />
            <TaskDetailWelfareItem :icon="IconLocaltion" text="高温补贴" />
            <TaskDetailWelfareItem :icon="IconLocaltion" text="高温补贴" />
            <TaskDetailWelfareItem :icon="IconLocaltion" text="高温补贴" />
            <TaskDetailWelfareItem :icon="IconLocaltion" text="高温补贴" />
            <TaskDetailWelfareItem
              v-for="item in detail.taskWeals"
              :key="item.id"
              :icon="setOSSLink(item.name)"
              :text="item.name"
            />
          </div>
        </CellChunk>
      </Cell>
@@ -43,6 +50,9 @@
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import IconLocaltion from '@/assets/flexJob/icon-localtion.png';
import { TaskPrice, TaskDetailWelfareItem } from '@12333/components';
import { SalaryTimeTypeEnumUnit, FlexTaskSettleTypeEnumText } from '@/constants/task';
import dayjs from 'dayjs';
import { setOSSLink } from '@12333/utils';
defineOptions({
  name: 'JobDetail',
@@ -53,7 +63,7 @@
// const props = withDefaults(defineProps<Props>(), {});
const router = Taro.useRouter();
const taskId = router.params?.id ?? 'c4cfe028-23e7-0be8-ee56-3a11e3743b9d';
const taskId = router.params?.taskId;
const {
  isLoading,
@@ -61,16 +71,16 @@
  data: detail,
  refetch,
} = useQuery({
  queryKey: ['flexWorkerServices/getOrdeForDetail', taskId],
  queryKey: ['flexWorkerServices/getFlexTaskDto', taskId],
  queryFn: async () => {
    return await flexWorkerServices.getOrdeForDetail(
    return await flexWorkerServices.getFlexTaskDto(
      { id: taskId },
      {
        showLoading: false,
      }
    );
  },
  placeholderData: () => ({} as API.OrderInfoDto),
  placeholderData: () => ({} as API.GetFlexTaskDtoOutput),
  onSuccess(data) {},
});
</script>
apps/bMiniApp/src/subpackages/jobApplicationManage/components/SignList.vue
@@ -1,12 +1,17 @@
<template>
  <InfiniteLoading scrollViewClassName="common-infinite-scroll-list" v-bind="infiniteLoadingProps">
    <template #renderItem="{ item }">
      <FlexJobCard>
      <FlexJobCard
        :name="item.name"
        :age="item.age"
        :genderType="item.genderType"
        :workExperience="item.workExperience"
      >
        <template #footerLeft>
          <div class="flexJob-card-footer-text">已录用</div>
          <div class="flexJob-card-footer-text">{{ FlexTaskWorkerHireEnum[item.hireStatus] }}</div>
        </template>
        <template #footerRight>
          <nut-button type="primary" @click="goToJobDetail(item.id)">查看详情</nut-button>
          <nut-button type="primary" @click="goToJobDetail(item.userId)">查看详情</nut-button>
        </template>
      </FlexJobCard>
    </template>
@@ -16,6 +21,7 @@
<script setup lang="ts">
import { OrderInputType } from '@12333/constants';
import { RouterPath } from '@/constants';
import { FlexTaskWorkerHireEnum } from '@/constants/task';
import { useInfiniteLoading } from '@12333/hooks';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { FlexJobCard } from '@12333/components';
@@ -29,22 +35,26 @@
// const props = withDefaults(defineProps<Props>(), {});
const router = Taro.useRouter();
const taskId = router.params?.taskId ?? '';
const { infiniteLoadingProps } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
    let params: API.GetFlexTaskWorkerApplyListInput = {
      flexTaskId: taskId,
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
    return flexWorkerServices.getFlexTaskByArrange(params, {
    return flexWorkerServices.getFlexTaskWorkerApplyList(params, {
      showLoading: false,
    });
  },
  {
    queryKey: ['flexWorkerServices/getFlexTaskByArrange'],
    queryKey: ['flexWorkerServices/getFlexTaskWorkerApplyList'],
  }
);
apps/bMiniApp/src/subpackages/jobApplicationManage/jobApplicationManage/InnerPage.vue
@@ -1,22 +1,49 @@
<template>
  <ProTabs
    v-model="queryState.mineHireType"
    v-model="queryState.flexTaskReleaseStatus"
    name="home-tab"
    :showPaneContent="false"
    class="home-tabs"
    isTransparent
    title-gutter="12"
    title-scroll
    @change="invalidateQueries"
  >
    <ProTabPane :title="`发布中(${1})`" :pane-key="0"></ProTabPane>
    <ProTabPane :title="`已停止(${1})`" :pane-key="10"></ProTabPane>
    <ProTabPane
      :title="`${
        FlexTaskReleaseStatusEnumText[FlexTaskReleaseStatusEnum.Releasing]
      }(${releaseing})`"
      :pane-key="FlexTaskReleaseStatusEnum.Releasing"
    ></ProTabPane>
    <ProTabPane
      :title="`${FlexTaskReleaseStatusEnumText[FlexTaskReleaseStatusEnum.Stoping]}(${stoping})`"
      :pane-key="FlexTaskReleaseStatusEnum.Stoping"
    ></ProTabPane>
  </ProTabs>
  <InfiniteLoading
    scrollViewClassName="common-infinite-scroll-list home-list"
    v-bind="infiniteLoadingProps"
    :key="queryState.flexTaskReleaseStatus"
  >
    <template #renderItem="{ item }">
      <JobApplicationCard @edit="goEdit" @detail="goDetail"> </JobApplicationCard>
      <JobApplicationCard
        :taskName="item.taskName"
        :startDate="item.startDate"
        :endDate="item.endDate"
        :creationTime="item.creationTime"
        :address="item.address"
        :fee="item.fee"
        :applyWorkerCount="item.applyWorkerCount"
        :unit="SalaryTimeTypeEnumUnit[item.feeType]"
        :releaseStatus="item.releaseStatus"
        @edit="goEdit(item)"
        @copy="goEdit(item, true)"
        @detail="goDetail(item)"
        @publish="handleChangeStatus(item, FlexTaskReleaseStatusEnum.Releasing)"
        @stop="handleChangeStatus(item, FlexTaskReleaseStatusEnum.Stoping)"
        @delete="handleDelete(item)"
      >
      </JobApplicationCard>
    </template>
  </InfiniteLoading>
</template>
@@ -26,54 +53,95 @@
import { RouterPath } from '@/constants';
import { useInfiniteLoading } from '@12333/hooks';
import { OrderInputType } from '@12333/constants';
import {
  FlexTaskReleaseStatusEnum,
  FlexTaskReleaseStatusEnumText,
  SalaryTimeTypeEnumUnit,
} from '@/constants/task';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { TaskStatus } from '@/constants/task';
import Taro from '@tarojs/taro';
import { Message } from '@12333/utils';
defineOptions({
  name: 'InnerPage',
});
const queryState = reactive({
  mineHireType: TaskStatus.All,
  flexTaskReleaseStatus: FlexTaskReleaseStatusEnum.Releasing,
});
const { infiniteLoadingProps } = useInfiniteLoading(
const { infiniteLoadingProps, invalidateQueries } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
    let params: API.GetFlexTaskListByStatusInput = {
      releaseStatus: queryState.flexTaskReleaseStatus,
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
    return flexWorkerServices.getFlexTaskByArrange(params, {
    return flexWorkerServices.getFlexTaskList(params, {
      showLoading: false,
    });
  },
  {
    queryKey: ['flexWorkerServices/getFlexTaskByArrange'],
    queryKey: ['flexWorkerServices/getFlexTaskList'],
  }
);
function goPage(routeName: string) {
  Taro.navigateTo({
    url: routeName,
const releaseing = computed(() => {
  return infiniteLoadingProps.value?.listData?.pages?.[0]?.objectData?.releaseing ?? 0;
  });
const stoping = computed(() => {
  return infiniteLoadingProps.value?.listData?.pages?.[0]?.objectData?.stoping ?? 0;
});
async function handleChangeStatus(
  item: API.GetFlexTaskListOutput,
  releaseStatus: FlexTaskReleaseStatusEnum
) {
  try {
    let params: API.UpdateTaskReleaseStatusInput = {
      taskId: item.taskId,
      releaseStatus: releaseStatus,
    };
    let res = await flexWorkerServices.updateFlexTaskReleaseStatus(params);
    if (res) {
      Message.success('操作成功');
      invalidateQueries();
    }
  } catch (error) {}
}
function goEdit() {
  goPage(RouterPath.publishTask);
async function handleDelete(item: API.GetFlexTaskListOutput) {
  try {
    await Message.confirm({
      message: '确定要删除吗?',
    });
    let params: API.APIdeleteFlexTaskParams = {
      id: item.taskId,
    };
    let res = await flexWorkerServices.deleteFlexTask(params);
    if (res) {
      Message.success('删除成功');
      invalidateQueries();
}
function goDetail() {
  goPage(RouterPath.jobApplicationDetail);
  } catch (error) {}
}
function goEdit(item: API.GetFlexTaskListOutput, isCopy = false) {
  Taro.navigateTo({
    url: `${RouterPath.publishTask}?taskId=${item.taskId}&isCopy=${isCopy}`,
  });
}
function goDetail(item: API.GetFlexTaskListOutput) {
  Taro.navigateTo({
    url: `${RouterPath.jobApplicationDetail}?taskId=${item.taskId}`,
  });
}
</script>
<style lang="scss">
@import '@/styles/common.scss';
// .jobApplicationManage-page-wrapper {
// }
</style>
apps/bMiniApp/src/subpackages/jobApplicationManage/jobApplicationManage/jobApplicationManage.vue
@@ -1,5 +1,9 @@
<template>
  <PageLayoutWithBg class="jobApplicationManage-page-wrapper" :title="'应聘管理'">
  <PageLayoutWithBg
    class="jobApplicationManage-page-wrapper"
    :title="'应聘管理'"
    :need-auth="false"
  >
    <InnerPage></InnerPage>
  </PageLayoutWithBg>
</template>
apps/bMiniApp/src/subpackages/mine/mineContactRecord/InnerPage.vue
@@ -28,7 +28,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/bMiniApp/src/subpackages/mine/mineFavorites/InnerPage.vue
@@ -28,7 +28,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/bMiniApp/src/subpackages/task/batchTaskList/InnerPage.vue
@@ -9,9 +9,16 @@
  </div>
  <InfiniteLoading scrollViewClassName="common-infinite-scroll-list" v-bind="infiniteLoadingProps">
    <template #renderItem="{ item }">
      <FlexJobCard :showFooterLeft="false">
      <FlexJobCard
        :name="item.name"
        :genderType="item.genderType"
        :age="item.age"
        :educationalLevel="item.educationalLevel"
        :arrangeCount="item.arrangeCount"
        :showFooterLeft="false"
      >
        <template #footerRight>
          <nut-button type="primary">安排</nut-button>
          <nut-button type="primary" @click="handleArrange(item)">安排</nut-button>
          <!-- <div class="batch-task-card-status">已安排</div> -->
        </template>
      </FlexJobCard>
@@ -25,7 +32,7 @@
import { OrderInputType } from '@12333/constants';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import _ from 'lodash';
import { trim } from '@12333/utils';
import { Message, trim } from '@12333/utils';
import { FlexJobCard } from '@12333/components';
defineOptions({
@@ -33,7 +40,8 @@
});
const searchValue = ref('');
const router = Taro.useRouter();
const taskId = router.params?.taskId ?? '';
const queryState = reactive({
  searchValueTrim: '',
});
@@ -42,9 +50,11 @@
  queryState.searchValueTrim = trim(searchValue.value);
}, 300);
const { infiniteLoadingProps } = useInfiniteLoading(
const { infiniteLoadingProps, invalidateQueries } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
    let params: API.GetFlexTaskWorkerArrangeListInput = {
      searchKeys: queryState.searchValueTrim,
      flexTaskId: taskId,
      pageModel: {
        rows: 20,
        page: pageParam,
@@ -52,14 +62,29 @@
      },
    };
    return flexWorkerServices.getFlexTaskByArrange(params, {
    return flexWorkerServices.getFlexTaskWorkerArrangeList(params, {
      showLoading: false,
    });
  },
  {
    queryKey: ['flexWorkerServices/getFlexTaskByArrange', queryState],
    queryKey: ['flexWorkerServices/getFlexTaskWorkerArrangeList', queryState],
  }
);
async function handleArrange(item: API.GetNewestWorkerListOutput) {
  try {
    let params: API.TaskWorkerArrangeInput = {
      flexTaskId: taskId,
      flexWorkerId: item.userId,
      arrange: true,
    };
    let res = await flexWorkerServices.taskWorkerArrange(params);
    if (res) {
      Message.success('已安排');
      invalidateQueries();
    }
  } catch (error) {}
}
</script>
<style lang="scss">
apps/bMiniApp/src/subpackages/task/components/TaskCheckCard.vue
@@ -1,9 +1,23 @@
<template>
  <div class="task-check-card-wrapper">
    <TaskCheckPersonalView class="task-check-card-view">
    <TaskCheckPersonalView
      class="task-check-card-view"
      :avatarUrl="avatarUrl"
      :name="name"
      :genderType="genderType"
      :isRealName="isRealName"
      :contactPhone="contactPhone"
    >
      <template #actions>
        <nut-button type="primary" class="task-check-card-phone-btn">验收</nut-button>
        <!-- <div class="task-check-card-phone-status" :style="{ color: Colors.Success }">验收通过</div> -->
        <nut-button
          v-if="checkAcceptStatus === FlexTaskCheckAcceptStatusEnum.Wait"
          type="primary"
          class="task-check-card-phone-btn"
          >验收</nut-button
        >
        <div v-else class="task-check-card-phone-status" :style="{ color: Colors.Success }">
          {{ FlexTaskCheckAcceptStatusEnumText[checkAcceptStatus] }}
        </div>
      </template>
    </TaskCheckPersonalView>
  </div>
@@ -11,15 +25,23 @@
<script setup lang="ts">
import TaskCheckPersonalView from './TaskCheckPersonalView.vue';
import { Colors } from '@12333/constants';
import { Colors, Gender } from '@12333/constants';
import { FlexTaskCheckAcceptStatusEnum, FlexTaskCheckAcceptStatusEnumText } from '@/constants/task';
defineOptions({
  name: 'TaskCheckCard',
});
// type Props = {};
type Props = {
  avatarUrl?: string;
  name?: string;
  genderType?: Gender;
  isRealName?: boolean;
  contactPhone?: string;
  checkAcceptStatus?: API.FlexTaskCheckAcceptStatusEnum;
};
// const props = withDefaults(defineProps<Props>(), {});
const props = withDefaults(defineProps<Props>(), {});
</script>
<style lang="scss">
apps/bMiniApp/src/subpackages/task/components/TaskCheckPersonalView.vue
@@ -1,10 +1,15 @@
<template>
  <FlexJobTopView>
  <FlexJobTopView
    :avatarUrl="avatarUrl"
    :name="name"
    :genderType="genderType"
    :isRealName="isRealName"
  >
    <template #detail>
      <div class="task-check-card-phone-container">
        <div class="task-check-card-phone-wrapper">
          <div class="task-check-card-phone-label">手机号:</div>
          <div class="task-check-card-phone">13000000000</div>
          <div class="task-check-card-phone">{{ contactPhone }}</div>
        </div>
        <slot name="actions"></slot>
      </div>
@@ -14,10 +19,21 @@
<script setup lang="ts">
import { FlexJobTopView } from '@12333/components';
import { Gender } from '@12333/constants';
defineOptions({
  name: 'TaskCheckPersonalView',
});
type Props = {
  avatarUrl?: string;
  name?: string;
  genderType?: Gender;
  isRealName?: boolean;
  contactPhone?: string;
};
const props = withDefaults(defineProps<Props>(), {});
</script>
<style lang="scss">
apps/bMiniApp/src/subpackages/task/publishTask/InnerPage.vue
@@ -6,10 +6,11 @@
      </nut-form-item>
      <nut-form-item
        label="服务费:"
        class="bole-form-item"
        class="bole-form-item alignTop"
        prop="feeType"
        required
        label-width="90px"
        label-position="top"
      >
        <nut-radio-group v-model="form.feeType" direction="horizontal">
          <BlRadio :label="Number(key)" v-for="(val, key) in FlexTaskFeeTypeEnumText" :key="key">{{
@@ -17,7 +18,7 @@
          }}</BlRadio>
        </nut-radio-group>
      </nut-form-item>
      <nut-form-item label=" " class="bole-form-item" prop="fee" label-width="90px">
      <nut-form-item label=" " class="bole-form-item" prop="fee" label-width="0">
        <div class="bole-form-input-wrapper">
          <nut-input
            v-model.trim="form.fee"
@@ -44,55 +45,62 @@
          >
        </nut-radio-group>
      </nut-form-item>
      <nut-form-item label="福利:" class="bole-form-item" prop="listAideIds" label-width="90px">
        <ChooseInputWithPicker
      <nut-form-item label="福利:" class="bole-form-item" prop="settleType" label-width="90px">
        <ChooseInputWithCheckbox
          v-model="form.listAideIds"
          title="请选择福利"
          :columns="WelfareTypeList"
          placeholder="请选择福利"
          :value-enum="TaskStatusText"
        />
      </nut-form-item>
      <nut-form-item
        label="年龄范围:"
        class="bole-form-item"
        prop="ageStart"
        prop="minAge"
        required
        label-width="90px"
      >
        <div class="bole-form-input-wrapper">
          <NumberInput
            v-model.trim="form.ageStart"
            v-model.trim="form.minAge"
            class="nut-input-text bole-input-text"
            placeholder="请选择年龄范围"
            :min="1"
          />
          <div class="form-input-separator">至</div>
          <NumberInput
            v-model.trim="form.ageEnd"
            v-model.trim="form.maxAge"
            class="nut-input-text bole-input-text"
            placeholder="请选择年龄范围"
            :min="1"
          />
        </div>
      </nut-form-item>
      <nut-form-item label="性别:" class="bole-form-item" prop="welfare" label-width="90px">
      <nut-form-item label="性别:" class="bole-form-item" prop="sexType" label-width="90px">
        <ChooseInputWithPicker
          v-model="form.welfare"
          v-model="form.sexType"
          placeholder="请选择性别要求"
          :value-enum="TaskStatusText"
          :value-enum="GenderText"
        />
      </nut-form-item>
      <nut-form-item label="资格证书:" class="bole-form-item" prop="welfare" label-width="90px">
        <ChooseInputWithPicker
          v-model="form.welfare"
          placeholder="请选择要求的资格证书"
          :value-enum="TaskStatusText"
      <nut-form-item label="资格证书:" class="bole-form-item" prop="settleType" label-width="90px">
        <ChooseInputWithCheckbox
          v-model="form.listCertionIds"
          title="请选择资格证书"
          :columns="CertificateTypeList"
          placeholder="请选择资格证书"
        />
      </nut-form-item>
      <nut-form-item label="任务地点" class="bole-form-item" prop="weMapInfo" required>
        <ChooseLocationInput placeholder="请选择任务所在地" v-model="form.weMapInfo" />
      <nut-form-item label="任务地点" class="bole-form-item" prop="areaList" required>
        <!-- <ChooseLocationInput placeholder="请选择任务所在地" v-model="form.weMapInfo" /> -->
        <ChooseInputWithAreaPicker
          :columns="areaTreeList"
          v-model="form.areaList"
          placeholder="请选择所在地区"
        ></ChooseInputWithAreaPicker>
      </nut-form-item>
      <nut-form-item label="详细地址:" class="bole-form-item" prop="name" label-width="90px">
        <nut-input v-model="form.name" placeholder="请输入详细地址"> </nut-input>
      <nut-form-item label="详细地址:" class="bole-form-item" prop="address" label-width="90px">
        <nut-input v-model="form.address" placeholder="请输入详细地址"> </nut-input>
      </nut-form-item>
      <nut-form-item
        label="任务开始日期:"
@@ -115,8 +123,6 @@
<script setup lang="ts">
import {
  TaskStatus,
  TaskStatusText,
  FlexTaskSettleTypeEnum,
  FlexTaskSettleTypeEnumText,
  FlexTaskFeeTypeEnum,
@@ -130,21 +136,37 @@
  NumberInput,
  ChooseLocationInput,
  Radio as BlRadio,
  ChooseInputWithAreaPicker,
  ChooseInputWithCheckbox,
} from '@12333/components';
import { FormValidator, Message } from '@12333/utils';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
import Taro from '@tarojs/taro';
import { goBack } from '@/utils';
import { Gender, GenderText, SearchType } from '@12333/constants';
import { useAllAreaList, useSearchSettingType } from '@12333/hooks';
import { useQuery } from '@tanstack/vue-query';
import dayjs from 'dayjs';
defineOptions({
  name: 'InnerPage',
});
const { userDetail } = useUser();
const { areaTreeList } = useAllAreaList();
const router = Taro.useRouter();
const taskId = router.params?.taskId ?? '';
const isEdit = computed(() => !!taskId);
const isEdit = !!taskId;
const isCopy = router.params?.isCopy === 'true';
console.log('isCopy: ', router.params?.isCopy);
const { searchSettingTypeList: WelfareTypeList } = useSearchSettingType({
  searchType: SearchType.Welfare,
});
const { searchSettingTypeList: CertificateTypeList } = useSearchSettingType({
  searchType: SearchType.CertificateType,
});
const form = reactive({
  taskName: '',
@@ -152,43 +174,101 @@
  fee: 0,
  settleType: FlexTaskSettleTypeEnum.OfMonth,
  listAideIds: [] as string[],
  welfare: TaskStatus.All,
  salaryTimeType: SalaryTimeTypeEnum.Month,
  salaryType: SalaryType.month,
  minAge: 0,
  maxAge: 0,
  sexType: Gender.Male,
  listCertionIds: [] as string[],
  address: '',
  startDate: '',
  endDate: '',
  ageStart: '',
  ageEnd: '',
  areaList: [] as number[],
  weMapInfo: {} as WeMapModel,
});
const rules = reactive<FormRules>({
  taskName: [{ required: true, message: '请输入任务名称' }],
  weMapInfo: [
    { required: true, message: '请设置工作地点', validator: FormValidator.validatorWeMap },
  // weMapInfo: [
  //   { required: true, message: '请设置工作地点', validator: FormValidator.validatorWeMap },
  // ],
  areaList: [
    { required: true, message: '清添加工作地点', validator: FormValidator.validatorArray },
  ],
  ageStart: [
  minAge: [
    {
      required: true,
      message: '请输入年龄要求',
      validator: () => {
        if (!form.ageStart) {
        if (!form.minAge) {
          return Promise.reject('请输入年龄要求');
        }
        if (!form.ageEnd) {
        if (!form.maxAge) {
          return Promise.reject('请输入年龄要求');
        }
        if (Number(form.ageEnd) <= Number(form.ageStart)) {
        if (Number(form.maxAge) <= Number(form.minAge)) {
          return Promise.reject('最大年龄不能小于最小年龄');
        }
        return Promise.resolve(true);
      },
    },
  ],
  startDate: [
    {
      required: true,
      message: '请选择开始日期',
    },
  ],
  endDate: [
    {
      required: true,
      message: '请选择结束日期',
      validator(value) {
        if (!value) return Promise.reject('请选择结束日期');
        if (value <= form.startDate) return Promise.reject('结束日期不能小于开始日期');
        return Promise.resolve(true);
      },
    },
  ],
});
const {
  isLoading,
  isError,
  data: detail,
  refetch,
} = useQuery({
  queryKey: ['flexWorkerServices/getFlexTaskDto', taskId],
  queryFn: async () => {
    return await flexWorkerServices.getFlexTaskDto(
      { id: taskId },
      {
        showLoading: false,
      }
    );
  },
  placeholderData: () => ({} as API.GetFlexTaskDtoOutput),
  enabled: isEdit,
  onSuccess(data) {
    form.taskName = data.taskName;
    form.feeType = data.feeType;
    form.fee = data.fee;
    form.settleType = data.settleType;
    form.listAideIds = data.taskWeals?.length > 0 ? data.taskWeals.map((item) => item.id) : [];
    form.minAge = data.minAge;
    form.maxAge = data.maxAge;
    form.sexType = data.sexType;
    form.listCertionIds = data.taskCerts?.length > 0 ? data.taskCerts.map((item) => item.id) : [];
    form.address = data.address;
    form.startDate = dayjs(data.startDate).format('YYYY-MM-DD');
    form.endDate = dayjs(data.endDate).format('YYYY-MM-DD');
    form.areaList = [data.provinceId, data.cityId, data.areaId];
    form.address = data.address;
  },
});
const formRef = ref<any>(null);
function handleConfirm() {
  if (!formRef.value) return;
@@ -203,14 +283,30 @@
  try {
    let params: API.AddEidtFlexTaskInput = {
      taskName: form.taskName,
      feeType: form.salaryType,
      feeType: form.feeType,
      fee: form.fee,
      settleType: form.settleType,
      listAideIds: form.listAideIds,
      minAge: form.minAge,
      maxAge: form.maxAge,
      sexType: form.sexType,
      listCertionIds: form.listCertionIds,
      address: form.address,
      startDate: form.startDate,
      endDate: form.endDate,
      provinceId: form.areaList[0],
      cityId: form.areaList[1],
      areaId: form.areaList[2],
    };
    if (isEdit.value) {
    if (isEdit) {
      params.taskId = taskId;
    }
    if (isCopy) {
      params.taskId = '';
    }
    let res = await flexWorkerServices.addEidtFlexTask(params);
    if (res) {
      Message.success(isEdit.value ? '编辑成功' : '发布成功', {
      Message.success(isEdit ? '编辑成功' : '发布成功', {
        onClosed() {
          goBack();
        },
@@ -238,5 +334,9 @@
    color: boleGetCssVar('text-color', 'primary');
    flex-shrink: 0;
  }
  .form-input-separator {
    margin-right: 10px;
  }
}
</style>
apps/bMiniApp/src/subpackages/task/publishTask/publishTask.vue
@@ -1,5 +1,5 @@
<template>
  <PageLayout class="publishTask-page-wrapper" :title="'发布招聘'" has-border>
  <PageLayout class="publishTask-page-wrapper" :title="'发布招聘'" has-border :need-auth="false">
    <InnerPage></InnerPage>
  </PageLayout>
</template>
apps/bMiniApp/src/subpackages/task/taskCheck/InnerPage.vue
@@ -9,8 +9,8 @@
    title-gutter="8"
    title-scroll
  >
    <ProTabPane :title="`待验收`" :pane-key="10"></ProTabPane>
    <ProTabPane :title="`已验收`" :pane-key="20"></ProTabPane>
    <ProTabPane title="待验收" pane-key="10"></ProTabPane>
    <ProTabPane title="已验收" pane-key="20"></ProTabPane>
  </ProTabs>
  <InfiniteLoading
    scrollViewClassName="common-infinite-scroll-list"
@@ -18,7 +18,13 @@
    :key="queryState.status"
  >
    <template #renderItem="{ item }">
      <MyTaskCard @click="goSubmitTaskDetail(item)" />
      <MyTaskCard
        :taskName="item.taskName"
        :startDate="item.startDate"
        :endDate="item.endDate"
        :address="item.address"
        @click="goSubmitTaskDetail(item)"
      />
    </template>
  </InfiniteLoading>
</template>
@@ -35,32 +41,33 @@
});
const queryState = reactive({
  status: 10,
  status: '10',
  date: new Date(),
});
const { infiniteLoadingProps } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
      isOverCheck: queryState.status === '20',
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'lastShelfTime', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
    return flexWorkerServices.getFlexTaskByArrange(params, {
    return flexWorkerServices.getFlexTaskByIsOverCheck(params, {
      showLoading: false,
    });
  },
  {
    queryKey: ['flexWorkerServices/getFlexTaskByArrange', queryState],
    queryKey: ['flexWorkerServices/getFlexTaskByIsOverCheck', queryState],
  }
);
function goSubmitTaskDetail(item: API.GetFlexTaskListOutput) {
  Taro.navigateTo({
    url: `${RouterPath.taskCheckDetail}?id=${item.id}`,
    url: `${RouterPath.taskCheckDetail}?taskId=${item.taskId}`,
  });
}
</script>
apps/bMiniApp/src/subpackages/task/taskCheckDetail/InnerPage.vue
@@ -1,7 +1,12 @@
<template>
  <LoadingLayout :loading="isLoading" :error="isError" :loadError="refetch">
    <ContentView>
      <MyTaskCard :showMyTaskArrow="false" :showTime="false"></MyTaskCard>
      <MyTaskCard
        :task-name="detail.taskName"
        :address="address"
        :showMyTaskArrow="false"
        :showTime="false"
      ></MyTaskCard>
      <ChunkTitle title="验收列表" />
    </ContentView>
    <InfiniteLoading
@@ -10,7 +15,15 @@
      :key="queryState.status"
    >
      <template #renderItem="{ item }">
        <TaskCheckCard @click="goHandleTaskDetail(item)" />
        <TaskCheckCard
          :avatarUrl="setOSSLink(item.avatarUrl)"
          :name="item.name"
          :genderType="item.genderType"
          :isRealName="item.isRealName"
          :contactPhone="item.contactPhone"
          :checkAcceptStatus="item.checkAcceptStatus"
          @click="goHandleTaskDetail(item)"
        />
      </template>
    </InfiniteLoading>
  </LoadingLayout>
@@ -24,13 +37,14 @@
import { OrderInputType } from '@12333/constants';
import TaskCheckCard from '../components/TaskCheckCard.vue';
import { MyTaskCard } from '@12333/components';
import { setOSSLink } from '@12333/utils';
defineOptions({
  name: 'InnerPage',
});
const router = Taro.useRouter();
const taskId = router.params?.id ?? '';
const taskId = router.params?.taskId ?? '';
const {
  isLoading,
@@ -38,16 +52,20 @@
  data: detail,
  refetch,
} = useQuery({
  queryKey: ['flexWorkerServices/getOrdeForDetail', taskId],
  queryKey: ['flexWorkerServices/getFlexTaskDto', taskId],
  queryFn: async () => {
    return await flexWorkerServices.getOrdeForDetail(
    return await flexWorkerServices.getFlexTaskDto(
      { id: taskId },
      {
        showLoading: false,
      }
    );
  },
  placeholderData: () => ({} as API.OrderInfoDto),
  placeholderData: () => ({} as API.GetFlexTaskDtoOutput),
});
const address = computed(() => {
  return `${detail.value.provinceName} ${detail.value.cityName} ${detail.value.areaName} ${detail.value.address}`;
});
const queryState = reactive({
@@ -56,7 +74,7 @@
const { infiniteLoadingProps } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
    let params: API.GetFlexTaskWorkerCheckListInput = {
      pageModel: {
        rows: 20,
        page: pageParam,
@@ -64,18 +82,18 @@
      },
    };
    return flexWorkerServices.getFlexTaskByArrange(params, {
    return flexWorkerServices.getFlexTaskWorkerCheckList(params, {
      showLoading: false,
    });
  },
  {
    queryKey: ['flexWorkerServices/getFlexTaskByArrange', queryState],
    queryKey: ['flexWorkerServices/getFlexTaskWorkerCheckList', queryState],
  }
);
function goHandleTaskDetail(item: API.GetFlexTaskListOutput) {
function goHandleTaskDetail(item: API.GetNewestWorkerListOutput) {
  Taro.navigateTo({
    url: `${RouterPath.taskHandleCheckDetail}?id=${item.id}`,
    url: `${RouterPath.taskHandleCheckDetail}?userId=${item.userId}`,
  });
}
</script>
apps/bMiniApp/src/subpackages/task/taskManage/InnerPage.vue
@@ -8,8 +8,8 @@
    title-gutter="8"
    title-scroll
  >
    <ProTabPane :title="`待安排(10)`" :pane-key="10"></ProTabPane>
    <ProTabPane :title="`已安排(11)`" :pane-key="20"></ProTabPane>
    <ProTabPane :title="`待安排(${notCount})`" pane-key="10"></ProTabPane>
    <ProTabPane :title="`已安排(${hasCount})`" pane-key="20"></ProTabPane>
  </ProTabs>
  <InfiniteLoading
    scrollViewClassName="common-infinite-scroll-list"
@@ -17,10 +17,27 @@
    :key="queryState.status"
  >
    <template #renderItem="{ item }">
      <JobApplicationCard @click="goSubmitTaskDetail(item)" mode="taskManage">
      <JobApplicationCard
        :taskName="item.taskName"
        :startDate="item.startDate"
        :endDate="item.endDate"
        :address="item.address"
        :creationTime="item.creationTime"
        :fee="item.fee"
        :unit="SalaryTimeTypeEnumUnit[item.feeType]"
        @click="goSubmitTaskDetail(item)"
        mode="taskManage"
      >
        <template #footer-actions>
          <!-- <nut-button type="primary">人员安排</nut-button> -->
          <nut-button type="primary" :color="Colors.Info" class="dark-btn">详情</nut-button>
          <nut-button
            v-if="item.isArrange"
            type="primary"
            :color="Colors.Info"
            class="dark-btn"
            @click="goSubmitTaskDetail(item)"
            >详情</nut-button
          >
          <nut-button type="primary" v-else @click="goBatchTaskList(item)">人员安排</nut-button>
        </template>
      </JobApplicationCard>
    </template>
@@ -33,22 +50,24 @@
import { useInfiniteLoading } from '@12333/hooks';
import { OrderInputType, Colors } from '@12333/constants';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { SalaryTimeTypeEnumUnit } from '@/constants/task';
defineOptions({
  name: 'InnerPage',
});
const queryState = reactive({
  status: 10,
  status: '10',
});
const { infiniteLoadingProps } = useInfiniteLoading(
  ({ pageParam }) => {
    let params: API.GetFlexTaskListInput = {
      isArrange: queryState.status === '20',
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'lastShelfTime', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
@@ -61,9 +80,22 @@
  }
);
const hasCount = computed(() => {
  return infiniteLoadingProps.value?.listData?.pages?.[0]?.objectData?.hasCount ?? 0;
});
const notCount = computed(() => {
  return infiniteLoadingProps.value?.listData?.pages?.[0]?.objectData?.notCount ?? 0;
});
function goSubmitTaskDetail(item: API.GetFlexTaskListOutput) {
  Taro.navigateTo({
    url: `${RouterPath.batchTaskList}?id=${item.id}`,
    url: `${RouterPath.flexJobDetail}?taskId=${item.taskId}`,
  });
}
function goBatchTaskList(item: API.GetFlexTaskListOutput) {
  Taro.navigateTo({
    url: `${RouterPath.batchTaskList}?taskId=${item.taskId}`,
  });
}
</script>
apps/bMiniApp/src/subpackages/task/taskManage/taskManage.vue
@@ -1,5 +1,5 @@
<template>
  <PageLayoutWithBg class="taskManage-page-wrapper" title="任务管理">
  <PageLayoutWithBg class="taskManage-page-wrapper" title="任务管理" :need-auth="false">
    <InnerPage />
  </PageLayoutWithBg>
</template>
apps/cMiniApp/src/hooks/task.ts
@@ -41,7 +41,7 @@
          page: pageParam,
          orderInput: [
            queryState.orderType === HomeOrderType.Recommend
              ? { property: 'isRecommend', order: OrderInputType.Desc }
              ? { property: 'creationTime', order: OrderInputType.Desc }
              : { property: 'lastShelfTime', order: OrderInputType.Desc },
          ],
        },
apps/cMiniApp/src/subpackages/mine/mineAgreementSign/InnerPage.vue
@@ -57,7 +57,7 @@
        page: pageParam,
        orderInput: [
          queryState.mineAgreementSignType === TaskStatus.All
            ? { property: 'isRecommend', order: OrderInputType.Desc }
            ? { property: 'creationTime', order: OrderInputType.Desc }
            : { property: 'lastShelfTime', order: OrderInputType.Desc },
        ],
      },
apps/cMiniApp/src/subpackages/mine/mineCancel/InnerPage.vue
@@ -32,7 +32,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/cMiniApp/src/subpackages/mine/mineCollectTask/InnerPage.vue
@@ -32,7 +32,7 @@
      pageModel: {
        rows: 20,
        page: pageParam,
        orderInput: [{ property: 'isRecommend', order: OrderInputType.Desc }],
        orderInput: [{ property: 'creationTime', order: OrderInputType.Desc }],
      },
    };
apps/cMiniApp/src/subpackages/mine/mineHire/InnerPage.vue
@@ -57,7 +57,7 @@
        page: pageParam,
        orderInput: [
          queryState.mineHireType === TaskStatus.All
            ? { property: 'isRecommend', order: OrderInputType.Desc }
            ? { property: 'creationTime', order: OrderInputType.Desc }
            : { property: 'lastShelfTime', order: OrderInputType.Desc },
        ],
      },
apps/cMiniApp/src/subpackages/mine/mineSign/InnerPage.vue
@@ -56,7 +56,7 @@
        page: pageParam,
        orderInput: [
          queryState.mineSignType === TaskStatus.All
            ? { property: 'isRecommend', order: OrderInputType.Desc }
            ? { property: 'creationTime', order: OrderInputType.Desc }
            : { property: 'lastShelfTime', order: OrderInputType.Desc },
        ],
      },
packages/components/src/ActionSheet/CheckboxActionSheet.vue
New file
@@ -0,0 +1,86 @@
<template>
  <div class="checkbox-action-sheet">
    <div class="checkbox-action-sheet-title">{{ title }}</div>
    <nut-checkbox-group v-model="model" :max="max">
      <nut-checkbox v-for="item in columns" :key="item.id" :label="item.id" icon-size="16">
        {{ item.name }}</nut-checkbox
      >
    </nut-checkbox-group>
    <div class="checkbox-action-sheet-footer">
      <nut-button type="primary" plain @click="handleCancel">取消</nut-button>
      <nut-button type="primary" @click="handleConfirm">确定</nut-button>
    </div>
  </div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'CheckboxActionSheet',
});
type Props = {
  max?: number;
  title?: string;
  columns?: API.GetTypeSearchSettingList[];
};
const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits<{
  (e: 'update:modelValue', value: Array<string | number>): void;
  (e: 'update:visible', value: boolean): void;
}>();
const model = defineModel<Array<string | number>>();
const visible = defineModel<boolean>('visible');
function handleCancel() {
  model.value = [];
  emit('update:visible', false);
}
function handleConfirm() {
  emit('update:visible', false);
  emit('update:modelValue', model.value);
}
</script>
<style lang="scss">
@import '@/styles/common.scss';
.checkbox-action-sheet {
  padding: 30px 30px 0;
  .checkbox-action-sheet-title {
    font-size: 32px;
    text-align: center;
    margin-bottom: 30px;
    color: #2878ff;
  }
  .nut-checkbox-group {
    padding-bottom: 30rpx;
    max-height: 400px;
    min-height: 200px;
    .nut-checkbox {
      margin-bottom: 20rpx;
      .nut-checkbox__label {
        margin-left: 10rpx;
      }
    }
  }
  .checkbox-action-sheet-footer {
    --nut-button-default-padding: 0 80px;
    display: flex;
    justify-content: space-around;
    .nut-button--primary {
      border-width: 1px;
    }
  }
}
</style>
packages/components/src/Card/FlexJobCard.vue
@@ -1,10 +1,8 @@
<template>
  <div class="flexJob-card-wrapper">
    <FlexJobTopView />
    <FlexJobTopView :name="name" :age="age" :genderType="genderType" />
    <div class="flexJob-card-done-list">
      {{
        '做过:客房服务员、客房服务员、客房客房服务员、客房做过:客房服务员、客房服务员、客房客房服务员、客房'
      }}
      {{ workExperience }}
    </div>
    <div class="flexJob-card-done-detail" v-if="showDoneDetail">
      <div class="flexJob-card-done-detail-item">
@@ -35,6 +33,7 @@
import IconFemale from '@/assets/mine/icon-female.png';
import { CommonTaskCardProps } from './card';
import FlexJobTopView from './FlexJobTopView.vue';
import { Gender } from '@12333/constants';
defineOptions({
  name: 'FlexJobCard',
@@ -44,6 +43,13 @@
  showFooterLeft?: boolean;
  showFooterRight?: boolean;
  showDoneDetail?: boolean;
  name?: string;
  genderType?: Gender;
  age?: number;
  educationalLevel?: string;
  workExperience?: string;
  arrangeCount?: number;
};
const props = withDefaults(defineProps<Props>(), {
packages/components/src/Card/FlexJobTopView.vue
@@ -1,17 +1,23 @@
<template>
  <div :class="['flexJob-card-top-wrapper', size]">
    <UserAvatar :size="size === 'small' ? 50 : 60" class="flexJob-card-top-avatar" />
    <Avatar :src="avatarUrl" :size="size === 'small' ? 50 : 60" class="flexJob-card-top-avatar" />
    <div class="flexJob-card-top-info">
      <div class="flexJob-card-top-info-item">
        <div class="flexJob-card-top-info-name">{{ '洋洋' }}</div>
        <div class="flexJob-card-top-info-name">{{ name }}</div>
        <div class="flexJob-card-top-info-gender">
          <img v-if="1" :src="IconMale" class="flexJob-card-top-info-gender-icon" />
          <img
            v-if="genderType === Gender.Male"
            :src="IconMale"
            class="flexJob-card-top-info-gender-icon"
          />
          <img v-else :src="IconFemale" class="flexJob-card-top-info-gender-icon" />
        </div>
        <div class="flexJob-card-top-info-auth">{{ '已实名' }}</div>
        <div class="flexJob-card-top-info-auth">{{ isRealName ? '已实名' : '未实名' }}</div>
      </div>
      <slot name="detail">
        <div class="flexJob-card-top-info-detail">{{ '26岁 | 非学生 | 本科 | 上岗121次' }}</div>
        <div class="flexJob-card-top-info-detail">
          {{ `${age}岁 | ${educationalLevel} |  ${educationalLevel} | 上岗${arrangeCount}次` }}
        </div>
      </slot>
    </div>
  </div>
@@ -20,6 +26,8 @@
<script setup lang="ts">
import IconMale from '@/assets/mine/icon-male.png';
import IconFemale from '@/assets/mine/icon-female.png';
import { AvatarImage, Gender } from '@12333/constants';
import { Avatar } from '@12333/components';
defineOptions({
  name: 'FlexJobTopView',
@@ -27,10 +35,19 @@
type Props = {
  size?: 'normal' | 'small';
  avatarUrl?: string;
  name?: string;
  genderType?: Gender;
  age?: number;
  educationalLevel?: string;
  arrangeCount?: number;
  isRealName?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
  size: 'normal',
  avatarUrl: AvatarImage,
});
</script>
packages/components/src/Card/JobApplicationCard.vue
@@ -1,28 +1,38 @@
<template>
  <div class="job-application-card-wrapper">
    <div class="job-application-card-title-wrapper">
      <div class="job-application-card-title">客房服务员</div>
      <TaskPrice :value="212" v-if="mode === 'taskManage'" />
      <div class="job-application-card-title">{{ taskName }}</div>
      <TaskPrice :value="fee" :unit="unit" v-if="mode === 'taskManage'" />
      <div v-else class="job-application-card-title-more" @click.stop="handleMore">
        <img :src="IconMore" class="more-btn-icon" />
      </div>
    </div>
    <div class="job-application-card-line">
      <div class="job-application-card-time">{{ `${'2025年2月5日'}至${'2025年3月5日'}` }}</div>
      <div class="job-application-card-status" v-if="mode === 'normal'">{{ '生效中' }}</div>
      <div class="job-application-card-time">
        {{
          `${dayjs(startDate).format('YYYY年MM月DD日')}至${dayjs(endDate).format('YYYY年MM月DD日')}`
        }}
      </div>
      <div class="job-application-card-status" v-if="mode === 'normal'">
        {{ FlexTaskReleaseStatusEnumText[releaseStatus] }}
      </div>
    </div>
    <div class="job-application-card-line">
      <div class="job-application-card-company">
        <div class="job-application-card-company-tag">H</div>
        <div class="job-application-card-company-address">宁波雷迪森酒店</div>
        <div class="job-application-card-company-address">{{ address }}</div>
      </div>
      <div class="job-application-card-people" v-if="mode === 'normal'">{{ `报名人数:${5}` }}</div>
      <div class="job-application-card-people" v-if="mode === 'normal'">
        {{ `报名人数:${applyWorkerCount}` }}
      </div>
    </div>
    <div class="job-application-card-line">
      <div class="job-application-card-publish-time">{{ `发布日期:${'2022年12月25日'}` }}</div>
      <div class="job-application-card-publish-time">
        {{ `发布日期:${dayjs(creationTime).format('YYYY-MM-DD')}` }}
      </div>
      <slot name="footer-actions">
        <div class="job-application-card-fee">
          <TaskPrice :value="212" />
          <TaskPrice :value="fee" :unit="unit" />
        </div>
      </slot>
    </div>
@@ -36,6 +46,7 @@
import { ActionSheet } from '@nutui/nutui-taro';
import { Portal } from 'senin-mini/components';
import { TaskPrice } from '@12333/components';
import dayjs from 'dayjs';
defineOptions({
  name: 'JobApplicationCard',
@@ -50,37 +61,36 @@
  Delete,
}
enum ResourceStatus {
enum FlexTaskReleaseStatusEnum {
  /**
   * 已驳回
   * 发布中
   */
  Reject = -10,
  Releasing = 10,
  /**
   * 未提交
   * 已停止
   */
  Draft = 10,
  /**
   * 待审核
   */
  WaitAudit = 20,
  /**
   * 进行中
   */
  Running = 30,
  /**
   * 已下架
   */
  OffShelf = 40,
  /**
   * 系统下架
   */
  SystemOffShelf = 50,
  Stoping = 20,
}
const FlexTaskReleaseStatusEnumText = {
  [FlexTaskReleaseStatusEnum.Releasing]: '发布中',
  [FlexTaskReleaseStatusEnum.Stoping]: '已停止',
};
type Props = CommonTaskCardProps & {
  showActions?: boolean;
  status?: ResourceStatus;
  status?: FlexTaskReleaseStatusEnum;
  mode?: 'taskManage' | 'normal';
  taskName?: string;
  startDate?: string;
  endDate?: string;
  address?: string;
  creationTime?: string;
  fee?: number;
  applyWorkerCount?: number;
  unit?: string;
  releaseStatus?: API.FlexTaskReleaseStatusEnum;
};
const props = withDefaults(defineProps<Props>(), {
@@ -99,29 +109,28 @@
const menuList = computed(() => {
  let _menuList = [];
  if (props.status !== ResourceStatus.WaitAudit) {
    _menuList.push({
  _menuList.push(
    {
      name: '编辑',
      value: ManageActions.Edit,
    });
  }
  _menuList.push({
    },
    {
    name: '查看详情',
    value: ManageActions.Detail,
  });
  if (props.status === ResourceStatus.OffShelf) {
    }
  );
  if (props.releaseStatus === FlexTaskReleaseStatusEnum.Stoping) {
    _menuList.push({
      name: '发布',
      value: ManageActions.Publish,
    });
  }
  if (props.status === ResourceStatus.Running) {
  if (props.releaseStatus === FlexTaskReleaseStatusEnum.Releasing) {
    _menuList.push({
      name: '停止发布',
      value: ManageActions.Stop,
    });
  }
  if (props.status !== ResourceStatus.WaitAudit) {
    _menuList.push(
      {
        name: '复制',
@@ -132,7 +141,6 @@
        value: ManageActions.Delete,
      }
    );
  }
  return _menuList;
});
packages/components/src/Card/MyTaskCard.vue
@@ -4,7 +4,11 @@
      <RectRight :size="12" class="my-task-card-arrow" v-if="showMyTaskArrow" />
      <div v-else></div>
    </template>
    <div class="my-task-card-time" v-if="showTime">2025年2月5日 至 2025年3月5日</div>
    <div class="my-task-card-time" v-if="showTime">
      {{
        `${dayjs(startDate).format('YYYY年MM月DD日')}至${dayjs(endDate).format('YYYY年MM月DD日')}`
      }}
    </div>
    <div v-else></div>
  </TaskCard>
</template>
@@ -13,6 +17,7 @@
import TaskCard from './TaskCard.vue';
import { CommonTaskCardProps } from './card';
import { RectRight } from '@nutui/icons-vue-taro';
import dayjs from 'dayjs';
defineOptions({
  name: 'MyTaskCard',
@@ -21,6 +26,11 @@
type Props = CommonTaskCardProps & {
  showMyTaskArrow?: boolean;
  showTime?: boolean;
  startDate?: string;
  endDate?: string;
  taskName?: string;
  address?: string;
};
const props = withDefaults(defineProps<Props>(), {
packages/components/src/Card/TaskCard.vue
@@ -1,7 +1,7 @@
<template>
  <div class="task-card-wrapper">
    <div class="task-card-title-wrapper">
      <div class="task-card-title">客房服务员</div>
      <div class="task-card-title">{{ taskName }}</div>
      <slot name="title-right">
        <TaskPrice :value="212" />
      </slot>
@@ -17,7 +17,7 @@
    <div class="task-card-footer">
      <div class="task-card-left">
        <div class="task-card-footer-tag">H</div>
        <div class="task-card-footer-address">宁波雷迪森酒店</div>
        <div class="task-card-footer-address">{{ address }}</div>
      </div>
      <div class="task-card-actions" v-if="showActions">
        <slot name="actions">
@@ -38,6 +38,9 @@
type Props = CommonTaskCardProps & {
  showActions?: boolean;
  taskName?: string;
  address?: string;
};
const props = withDefaults(defineProps<Props>(), {
packages/components/src/Card/TaskPrice.vue
@@ -1,7 +1,7 @@
<template>
  <div class="task-price">
    <div class="task-price-decimal">{{ value }}</div>
    <div class="task-price-unit">元/小时</div>
    <div class="task-price-unit">{{ unit }}</div>
  </div>
</template>
@@ -12,6 +12,7 @@
type Props = {
  value?: number;
  unit?: string;
};
const props = withDefaults(defineProps<Props>(), {});
packages/components/src/Input/ChooseInputWithCheckbox.vue
New file
@@ -0,0 +1,66 @@
<template>
  <ChooseInput :modelValue="inputValue" @click="handleOpen()"></ChooseInput>
</template>
<script setup lang="ts">
import { Portal } from 'senin-mini/components';
import ChooseInput from './ChooseInput.vue';
import { computed, h } from 'vue';
import { Popup, DatePicker, Picker } from '@nutui/nutui-taro';
import CheckboxActionSheet from '../ActionSheet/CheckboxActionSheet.vue';
import _ from 'lodash';
defineOptions({
  name: 'ChooseInputWithCheckbox',
});
type Props = {
  columns: API.GetTypeSearchSettingList[];
  modelValue: Array<string | number>;
  title?: string;
  max?: number;
};
const props = withDefaults(defineProps<Props>(), {});
const inputValue = computed(() =>
  props.modelValue.map((x) => props.columns.find((y) => y.id === x)?.name).join(',')
);
const emit = defineEmits<{
  (e: 'update:modelValue', val: Array<string | number>): void;
}>();
function handleOpen() {
  Portal.add((key) => {
    return h(
      Portal.Container,
      { keyNumber: key, delayOpen: true },
      {
        default: ({ open, onClose }) =>
          h(
            Popup,
            {
              visible: open.value,
              'onUpdate:visible': (value) => !value && onClose(),
              position: 'bottom',
              closeOnClickOverlay: false,
            },
            {
              default: () =>
                h(CheckboxActionSheet, {
                  modelValue: props.modelValue,
                  columns: props.columns,
                  title: props.title,
                  visible: open.value,
                  'onUpdate:visible': (value) => !value && onClose(),
                  'onUpdate:modelValue': (value) => {
                    emit('update:modelValue', value);
                  },
                }),
            }
          ),
      }
    );
  });
}
</script>
packages/components/src/Input/ChooseInputWithDatePicker.vue
@@ -44,7 +44,6 @@
                  modelValue: _modelValue,
                  onCancel: onClose,
                  onConfirm: ({ selectedValue }) => {
                    console.log('selectedValue: ', selectedValue);
                    emit('update:modelValue', dayjs(selectedValue).format('YYYY-MM-DD'));
                    onClose();
                  },
packages/components/src/index.ts
@@ -12,6 +12,7 @@
export { default as ChooseInputWithPicker } from './Input/ChooseInputWithPicker.vue';
export { default as ChooseInputWithDatePicker } from './Input/ChooseInputWithDatePicker.vue';
export { default as ChooseInputWithAreaPicker } from './Input/ChooseInputWithAreaPicker.vue';
export { default as ChooseInputWithCheckbox } from './Input/ChooseInputWithCheckbox.vue';
export { default as ChooseInputWithAreaSheet } from './Input/ChooseInputWithAreaSheet.vue';
export { default as ChooseLocationInput } from './Input/ChooseLocationInput.vue';
export { default as PreviewImage } from './Image/PreviewImage.vue';
packages/constants/dic.ts
@@ -1,45 +1,25 @@
export enum SearchType {
  Hot = 10,
  Service = 20,
  Work = 30,
  Park = 40,
  Info = 50,
  Policy = 60,
  RegionalManagement = 70,
  ProductType = 80,
  DemandType = 90,
  DemandRange = 100,
  EmployeeBenefits = 110,
  CompanyIndustry = 120,
  HeadHunterPosition = 130,
  ConsultationCategory = 140,
  FAQCategory = 150,
  IndustryCategory = 160,
  FirstPartyIndustry = 170,
  MatingServiceType = 180,
  IndustryBodyType = 190,
  /**身份 */
  Identity = 210,
  /**学历 */
  Education = 220,
  /**岗位 */
  Position = 230,
  /**证书类型 */
  CertificateType = 240,
  /**福利 */
  Welfare = 250,
  /**行业类型 */
  IndustryCategory = 260,
}
export const SearchTypeText = {
  [SearchType.Hot]: '热搜词',
  [SearchType.Service]: '服务类型',
  [SearchType.Work]: '工种',
  [SearchType.Park]: '园区类型',
  [SearchType.Info]: '资讯活动类型',
  [SearchType.Policy]: '政策颁布机构',
  [SearchType.RegionalManagement]: '区域管理',
  [SearchType.ProductType]: '产品类型',
  [SearchType.DemandType]: '需求类型',
  [SearchType.DemandRange]: '需求范围',
  [SearchType.EmployeeBenefits]: '员工福利',
  [SearchType.CompanyIndustry]: '公司行业',
  [SearchType.HeadHunterPosition]: '猎头职位',
  [SearchType.ConsultationCategory]: '咨询类别',
  [SearchType.FAQCategory]: '问题分类',
  [SearchType.IndustryCategory]: '行业类别',
  [SearchType.FirstPartyIndustry]: '甲方行业',
  [SearchType.MatingServiceType]: '配套服务类型',
  [SearchType.IndustryBodyType]: '行业机构类型',
  [SearchType.Identity]: '身份',
  [SearchType.Education]: '学历',
  [SearchType.Position]: '岗位',
  [SearchType.CertificateType]: '证书类型',
  [SearchType.Welfare]: '福利',
  [SearchType.IndustryCategory]: '行业类型',
};
export enum BelongType {
packages/hooks/area.ts
@@ -1,5 +1,5 @@
import { flattenAreaTree, formatAreaListToTree } from '@12333/utils';
import * as areaServices from '@12333/services/api/Area';
import * as flexWorkerServices from '@12333/services/api/FlexWorker';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { AreaType } from '@12333/constants';
@@ -14,14 +14,14 @@
  const queryClient = useQueryClient();
  const { data: areaStore } = useQuery({
    queryKey: ['areaServices/getRegionalManagementList'],
    queryKey: ['flexWorkerServices/getAreaList'],
    queryFn: async () => {
      return await areaServices.getRegionalManagementList({ showLoading: false });
      return await flexWorkerServices.getAreaList({}, { showLoading: false });
    },
    placeholderData: () => [] as API.AreaInfo[],
    placeholderData: () => [] as API.AreaDto[],
    staleTime: Infinity,
    select(data) {
      const areaItemMap: Record<API.AreaInfo['areaCode'], API.AreaInfo> = {};
      const areaItemMap: Record<API.AreaDto['areaCode'], API.AreaDto> = {};
      data.forEach((item) => {
        areaItemMap[item.areaCode] = item;
      });
packages/services/api/typings.d.ts
@@ -42,9 +42,9 @@
    sexType?: GenderTypeEnum;
    /** 证书Id */
    listCertionIds?: string[];
    provinceId?: string;
    cityId?: string;
    areaId?: string;
    provinceId?: number;
    cityId?: number;
    areaId?: number;
    address?: string;
    startDate?: string;
    endDate?: string;
@@ -380,16 +380,18 @@
  }
  interface AreaDto {
    /** 编码 */
    id?: string;
    areaCode?: number;
    /** 父级编码 */
    parentCode?: number;
    /** 名称 */
    parentId?: number;
    areaName?: string;
    /** 1省 2市 3区 4镇 */
    layer?: number;
    /** 排序 */
    sort?: number;
    children?: AreaDto[];
    /** 简易拼音 */
    simpleSpelling?: string;
    /** 快速检索 */
    quickQuery?: string;
  }
  interface BaseAuthorizeDto {
@@ -880,9 +882,9 @@
    taskWeals?: FlexTaskAideDto[];
    taskCerts?: FlexTaskAideDto[];
    fee?: number;
    provinceId?: string;
    cityId?: string;
    areaId?: string;
    provinceId?: number;
    cityId?: number;
    areaId?: number;
    provinceName?: string;
    cityName?: string;
    areaName?: string;
@@ -910,6 +912,8 @@
    taskId?: string;
    taskName?: string;
    isArrange?: boolean;
    isOverCheck?: boolean;
    releaseStatus?: FlexTaskReleaseStatusEnum;
    startDate?: string;
    endDate?: string;
    feeType?: FlexTaskFeeTypeEnum;