Browse Source

任务管理

zhangjie 1 year ago
parent
commit
3cf7d4f93a

+ 0 - 28
src/api/admin.ts

@@ -1,28 +0,0 @@
-import axios from 'axios';
-import type {
-  SchoolItem,
-  ScanLogParams,
-  ScanLogRes,
-  AdminRoleItem,
-} from './types/admin';
-
-export function schoolList(): Promise<SchoolItem[]> {
-  return axios.post('/api/admin/common/school/list', {});
-}
-
-// role-manage
-export function adminRoleListPage(): Promise<AdminRoleItem[]> {
-  return axios.post('/api/admin/sys/role/list_to_admin', {});
-}
-interface SetRoleParams {
-  id: string;
-  defaultRole: boolean;
-}
-export function setRoleDefault(params: SetRoleParams) {
-  return axios.post('/api/admin/sys/role/builtin', {}, { params });
-}
-
-// scan-log-manage
-export function scanLogListPage(params: ScanLogParams): Promise<ScanLogRes> {
-  return axios.post('/api/scan/log/list', {}, { params });
-}

+ 0 - 4
src/api/interceptor.ts

@@ -5,7 +5,6 @@ import { initSyncTime, fetchTime } from '../utils/syncServerTime';
 import { getAuthorization } from '../utils/crypto';
 import { DEVICE_ID, PLATFORM } from '../constants/app';
 import { objTypeOf } from '../utils/utils';
-import ls from '../utils/storage';
 import { useUserStore } from '../store';
 
 export interface HttpResponse<T = unknown> {
@@ -26,9 +25,6 @@ const modifyConfig = (config: AxiosRequestConfig): void => {
   const userStore = useUserStore();
   if (userStore.accessToken) {
     const ids = {
-      privilegeId: ls.get('privilegeId', ''),
-      orgId: userStore.orgInfo?.id,
-      schoolId: userStore.curSchoolInfo?.id,
       userId: userStore.id,
     };
     Object.entries(ids).forEach(([key, val]) => {

+ 25 - 0
src/api/order.ts

@@ -0,0 +1,25 @@
+import axios from 'axios';
+import type {
+  TaskListPageRes,
+  TaskItemDetail,
+  TaskRuleUpdateParams,
+} from './types/order';
+import { AbleParams } from './types/common';
+
+// task-manage
+export function taskListPage(params: {
+  name: string;
+}): Promise<TaskListPageRes> {
+  return axios.post('/api/admin/apply/task/page', {}, { params });
+}
+export function taskDetailInfo(id: string): Promise<TaskItemDetail> {
+  return axios.post('/api/admin/apply/task/find', {}, { params: { id } });
+}
+export function updateTaskRule(
+  datas: TaskRuleUpdateParams
+): Promise<{ id: string }> {
+  return axios.post('/api/admin/apply/task/rule/save', datas);
+}
+export function ableTask(params: AbleParams): Promise<boolean> {
+  return axios.post('/api/admin/apply/task/enable', params);
+}

+ 0 - 38
src/api/types/admin.ts

@@ -1,38 +0,0 @@
-import { PageResult, PageParams } from './common';
-
-export interface SchoolItem {
-  id: string;
-  name: string;
-  code: string;
-}
-
-// role
-export interface AdminRoleItem {
-  id: string;
-  updateId: string;
-  updateTime: number;
-  name: string;
-  enable: boolean;
-  type: string;
-  defaultRole: boolean;
-  source: string;
-  interpret: string;
-}
-
-// scan-log
-export interface ScanLogItem {
-  schoolCode: string;
-  deviceCode: string;
-  fileName: string;
-  type: 'NORMAL' | 'OTHER';
-  md5: string;
-  loginName: string;
-  path: string;
-  uploadTime: number;
-  pathUrl: string;
-}
-export type ScanLogRes = PageResult<ScanLogItem>;
-export type ScanLogParams = PageParams<{
-  schoolCode: string;
-  loginName: string;
-}>;

+ 44 - 0
src/api/types/order.ts

@@ -0,0 +1,44 @@
+import { PageResult, PageParams } from './common';
+
+export interface TaskItem {
+  id: string;
+  name: string;
+  selfApplyStartTime: number;
+  selfApplyEndTime: number;
+  openApplyStartTime: number;
+  openApplyEndTime: number;
+  enable: number; // 1:启用;0:禁用
+  updateTime: number;
+}
+
+export type TaskListPageRes = PageResult<TaskItem>;
+
+export interface TaskItemDetail {
+  id: string;
+  name: string;
+  allowApplyDays: number;
+  allowApplyCancelDays: number;
+  selfApplyStartTime: number;
+  selfApplyEndTime: number;
+  openApplyStartTime: number;
+  openApplyEndTime: number;
+  time: [
+    {
+      id: string;
+      startTime: number;
+      endTime: number;
+    }
+  ];
+  notice: string;
+}
+
+export interface TaskRuleUpdateParams {
+  id?: string;
+  name: string;
+  allowApplyDays: number;
+  allowApplyCancelDays: number;
+  selfApplyStartTime: number;
+  selfApplyEndTime: number;
+  openApplyStartTime: number;
+  openApplyEndTime: number;
+}

+ 1 - 12
src/api/types/user.ts

@@ -6,16 +6,9 @@ export interface LoginData {
 export interface UpdatePwdData {
   id: string;
   oldPassword: string;
-  verifyCode?: string;
-  mobileNumber?: string;
-  password?: string;
+  password: string;
 }
 
-export type UserMenuPrivilegeEnum =
-  | 'conditions'
-  | 'buttons'
-  | 'lists'
-  | 'links';
 export interface UserMenuItem {
   id: string;
   name: string;
@@ -24,10 +17,6 @@ export interface UserMenuItem {
   parentId: string;
   sequence: number;
   enable: boolean;
-  conditions?: UserMenuItem[];
-  buttons?: UserMenuItem[];
-  lists?: UserMenuItem[];
-  links?: UserMenuItem[];
 }
 export interface UserMenuRes {
   userId: string;

+ 0 - 2
src/components/index.ts

@@ -1,13 +1,11 @@
 import { App } from 'vue';
 
 // selection
-import SelectSchool from './select-school/index.vue';
 import SelectOrg from './select-org/index.vue';
 import SelectRangeDatetime from './select-range-datetime/index.vue';
 
 export default {
   install(Vue: App) {
-    Vue.component('SelectSchool', SelectSchool);
     Vue.component('SelectOrg', SelectOrg);
     Vue.component('SelectRangeDatetime', SelectRangeDatetime);
   },

+ 0 - 69
src/components/select-school/index.vue

@@ -1,69 +0,0 @@
-<template>
-  <a-select
-    v-model="selected"
-    :placeholder="placeholder"
-    :allow-clear="clearable"
-    :disabled="disabled"
-    :options="optionList"
-    filter-option
-    v-bind="attrs"
-    @change="onChange"
-  >
-  </a-select>
-</template>
-
-<script setup lang="ts">
-  import { ref, useAttrs, watch } from 'vue';
-  import { schoolList } from '@/api/admin';
-
-  defineOptions({
-    name: 'SelectSchool',
-  });
-
-  const props = defineProps<{
-    modelValue: string;
-    clearable?: boolean;
-    disabled?: boolean;
-    placeholder?: string;
-    multiple?: boolean;
-  }>();
-  const emit = defineEmits(['update:modelValue', 'change']);
-  const attrs = useAttrs();
-
-  interface optionListItem {
-    value: string;
-    label: string;
-  }
-
-  const selected = ref('');
-  const optionList = ref<optionListItem[]>([]);
-  const search = async () => {
-    optionList.value = [];
-    const resData = await schoolList();
-
-    optionList.value = (resData || []).map((item) => {
-      return { ...item, value: item.id, label: item.name };
-    });
-  };
-  search();
-
-  const onChange = () => {
-    const selectedData = props.multiple
-      ? optionList.value.filter(
-          (item) => selected.value && selected.value.includes(item.value)
-        )
-      : optionList.value.filter((item) => selected.value === item.value);
-    emit('update:modelValue', selected.value);
-    emit('change', props.multiple ? selectedData : selectedData[0]);
-  };
-
-  watch(
-    () => props.modelValue,
-    (val) => {
-      selected.value = val;
-    },
-    {
-      immediate: true,
-    }
-  );
-</script>

+ 0 - 24
src/constants/app.ts

@@ -1,28 +1,4 @@
 import { md5 } from 'js-md5';
-import { parseHrefParam } from '../utils/utils';
-
-// domain
-export function getOrgCode() {
-  let domain;
-  const paramCode =
-    parseHrefParam(window.location.href, 'code') ||
-    window.sessionStorage.getItem('paramDomainCode');
-  if (paramCode) {
-    domain = paramCode;
-    window.sessionStorage.setItem('paramDomainCode', domain);
-    return domain;
-  }
-
-  domain = window.localStorage.getItem('domain_in_url');
-  if (domain) return domain;
-
-  const ipFormat = new RegExp(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
-  const { hostname } = window.location;
-  if (ipFormat.test(hostname) || hostname.includes('localhost')) return '';
-
-  domain = hostname.split('.')[0];
-  return domain;
-}
 
 export const PLATFORM = 'WEB';
 

+ 0 - 29
src/constants/staticMenu.ts

@@ -1,29 +0,0 @@
-export default [
-  {
-    id: '999901',
-    name: '首页',
-    url: 'HomePage',
-    type: 'MENU',
-    parentId: '-1',
-    sequence: 1,
-    enable: true,
-  },
-  {
-    id: '9999011',
-    name: '我的工作台',
-    url: 'work',
-    type: 'MENU',
-    parentId: '999901',
-    sequence: 1,
-    enable: true,
-  },
-  {
-    id: '99990111',
-    name: '待办任务',
-    url: 'WaitTask',
-    type: 'MENU',
-    parentId: '9999011',
-    sequence: 1,
-    enable: true,
-  },
-];

+ 17 - 155
src/layout/default-layout.vue

@@ -1,34 +1,12 @@
 <template>
-  <div :class="['home', { 'home-page-main': IS_HOME_PAGE }]">
+  <div class="home">
     <div class="home-header">
-      <div class="head-menu menu-list">
-        <ul>
-          <li
-            v-for="(menu, index) in appStore.appMenus"
-            :key="index"
-            :class="[
-              'menu-item',
-              { 'menu-item-act': curMenu.url === menu.url },
-            ]"
-            @click="toMenu(menu)"
-          >
-            <span>{{ menu.name }}</span>
-          </li>
-        </ul>
-      </div>
+      <div> </div>
       <div class="head-user menu-list">
         <ul>
-          <li
-            v-if="userStore.curSchoolInfo.name"
-            title="切换学校"
-            @click="toSelectSchool"
-          >
-            <icon-home />
-            <span>{{ userStore.curSchoolInfo.name }}</span>
-          </li>
           <li class="menu-item menu-item-account" @click="toResetPwd">
             <icon-user />
-            <span :title="userStore.realName">{{ userStore.realName }}</span>
+            <span :title="userStore.name">{{ userStore.name }}</span>
           </li>
           <li class="menu-item" title="退出登录" @click="toLogout">
             <icon-export />
@@ -37,26 +15,21 @@
       </div>
     </div>
 
-    <div v-if="!IS_HOME_PAGE" class="home-navs">
+    <div class="home-navs">
       <div class="head-logo">
         <div class="head-logo-content">
-          <img
-            v-if="userStore.curSchoolInfo.logo"
-            :src="userStore.curSchoolInfo.logo"
-            alt="预约报名系统"
-          />
-          <h1 v-else>预约报名系统</h1>
+          <h1>预约报名系统</h1>
         </div>
       </div>
 
       <a-menu
-        v-if="curMenu.children && curMenu.children.length"
+        v-if="appStore.appMenus && appStore.appMenus.length"
         class="arco-menu-home"
         :default-selected-keys="[curRouteName]"
-        :default-open-keys="curSubMenuNames"
+        auto-open
         @menu-item-click="toMenuItem"
       >
-        <template v-for="submenu in curMenu.children">
+        <template v-for="submenu in appStore.appMenus">
           <a-sub-menu
             v-if="submenu.children && submenu.children.length"
             :key="submenu.url"
@@ -67,12 +40,6 @@
 
             <a-menu-item v-for="nav in submenu.children" :key="nav.url">
               <span>{{ nav.name }}</span>
-              <!-- <span
-                v-if="nav.url === 'WaitTask' && waitTaskCount"
-                class="nav-item-info"
-              >
-                {{ waitTaskCount }}
-              </span> -->
             </a-menu-item>
           </a-sub-menu>
           <a-menu-item v-else :key="submenu.url">
@@ -84,14 +51,17 @@
 
     <div class="home-body">
       <div class="home-body-content">
-        <div v-if="breadcrumbs.length" class="home-breadcrumb">
+        <div v-if="appStore.breadcrumbs.length" class="home-breadcrumb">
           <span class="breadcrumb-tips">
             <icon-home />
             <span>当前所在位置:</span>
           </span>
           <a-breadcrumb>
-            <a-breadcrumb-item v-for="bread in breadcrumbs" :key="bread.url">
-              {{ bread.name }}
+            <a-breadcrumb-item
+              v-for="(bread, index) in appStore.breadcrumbs"
+              :key="index"
+            >
+              {{ bread }}
             </a-breadcrumb-item>
           </a-breadcrumb>
         </div>
@@ -115,19 +85,15 @@
 </template>
 
 <script lang="ts" setup>
-  import { computed, onMounted, ref, watch } from 'vue';
+  import { onMounted, ref, watch } from 'vue';
   import { useRoute, useRouter } from 'vue-router';
   import { IconHome, IconUser, IconExport } from '@arco-design/web-vue/es/icon';
   import { useAppStore, useUserStore } from '@/store';
-  import { SYS_ADMIN_NAME } from '@/constants/enumerate';
-  import { HOME_PAGE_ROUTE } from '@/router/constants';
   import { modalConfirm } from '@/utils/arco';
 
   import ResetPwd from '@/views/login/login/ResetPwd.vue';
   import Footer from '@/components/footer/index.vue';
 
-  import type { AppMenuItem } from '@/store/modules/app/types';
-
   defineOptions({
     name: 'DefaultLayout',
   });
@@ -137,10 +103,7 @@
   const route = useRoute();
   const router = useRouter();
 
-  const curMenu = ref({ url: '', children: [] });
   const curRouteName = ref('');
-  const curSubMenuNames = ref([]);
-  const breadcrumbs = ref([]);
   const userInfo = ref({
     pwdCount: 0,
     mobileNumber: 1,
@@ -148,115 +111,15 @@
   });
   const ResetPwdRef = ref(null);
 
-  const IS_SUPER_ADMIN = computed(() => {
-    return userStore.loginName === SYS_ADMIN_NAME;
-  });
-  const IS_HOME_PAGE = computed(() => {
-    return route.name === HOME_PAGE_ROUTE;
-  });
-  const hasMoreSchool = computed(() => {
-    return userStore.schoolInfo?.length > 1;
-  });
-
   function initData() {
-    if (IS_HOME_PAGE.value) {
-      curMenu.value = { url: HOME_PAGE_ROUTE, children: [] };
-      return;
-    }
-
-    updateBreadcrumbs();
-    const curUrl = breadcrumbs.value[0].url;
-    const currentMenu = appStore.appMenus.find((menu) => menu.url === curUrl);
-
-    menuChange(currentMenu);
-
-    // 待办任务数量展示
-    if (curMenu.value.url === 'exam') {
-      // updateWaitTaskCount();
-    }
-  }
-
-  function toMenu(menu: AppMenuItem) {
-    if (menu.url === HOME_PAGE_ROUTE) {
-      if (route.name !== HOME_PAGE_ROUTE) {
-        curMenu.value = menu;
-        router.push({
-          name: HOME_PAGE_ROUTE,
-        });
-      }
-      return;
-    }
-    if (curMenu.value.url === menu.url) return;
-    menuChange(menu);
-    const firstRouteName = getCurMenuFirstRouter();
-    router.push({
-      name: firstRouteName,
-    });
-  }
-
-  function getCurMenuFirstRouter() {
-    let firstRouteName = '';
-    let menu = curMenu.value;
-    while (menu) {
-      firstRouteName = menu.url;
-      menu = menu.children && menu.children[0];
-    }
-
-    return firstRouteName;
-  }
-
-  function updateBreadcrumbs() {
     curRouteName.value = route.name;
-    const breadcrumbsData = [];
-
-    let curBreadcrumb = appStore.privilegeList.find(
-      (item) => item.url === curRouteName.value
-    );
-    if (curBreadcrumb) breadcrumbsData.push({ ...curBreadcrumb });
-
-    while (curBreadcrumb && curBreadcrumb.parentId !== '-1') {
-      const pid = curBreadcrumb.parentId;
-      curBreadcrumb = appStore.privilegeList.find((item) => item.id === pid);
-      if (curBreadcrumb) breadcrumbsData.unshift({ ...curBreadcrumb });
-    }
-    breadcrumbs.value = breadcrumbsData;
-  }
-
-  function menuChange(menu: AppMenuItem) {
-    curMenu.value = menu;
-    curSubMenuNames.value = appStore.privilegeList
-      .filter((item) => item.parentId === menu.id)
-      .map((item) => item.url);
-  }
-
-  function routerChange() {
-    updateBreadcrumbs();
-
-    const curUrl = breadcrumbs.value[0].url;
-    const currentMenu = appStore.appMenus.find((menu) => menu.url === curUrl);
-    menuChange(currentMenu);
-
-    if (curMenu.value.url === 'exam') {
-      // updateWaitTaskCount();
-    }
   }
 
   function toMenuItem(val) {
     router.push({ name: val });
   }
 
-  function toSelectSchool() {
-    if (IS_SUPER_ADMIN.value) {
-      router.push({ name: 'SelectSchool' });
-      return;
-    }
-    if (hasMoreSchool.value) {
-      // this.$refs.SwitchSchoolDialog.open();
-    }
-  }
-
   function toResetPwd() {
-    if (IS_SUPER_ADMIN.value) return;
     ResetPwdRef.value?.open();
   }
 
@@ -278,9 +141,8 @@
 
   watch(
     () => route.name,
-    (val) => {
-      if (val === HOME_PAGE_ROUTE) return;
-      routerChange();
+    () => {
+      initData();
     }
   );
 </script>

+ 0 - 16
src/layout/page-layout.vue

@@ -1,16 +0,0 @@
-<template>
-  <router-view v-slot="{ Component, route }">
-    <transition name="fade" mode="out-in" appear>
-      <component
-        :is="Component"
-        v-if="route.meta.ignoreCache"
-        :key="route.fullPath"
-      />
-      <keep-alive v-else>
-        <component :is="Component" :key="route.fullPath" />
-      </keep-alive>
-    </transition>
-  </router-view>
-</template>
-
-<script lang="ts" setup></script>

+ 6 - 6
src/mock/datas/user.ts

@@ -11,7 +11,7 @@ export const menus = [
   {
     id: '2',
     name: '预约任务管理',
-    url: 'OrganizationManage',
+    url: 'TaskManage',
     type: 'MENU',
     parentId: '1',
     sequence: 1,
@@ -19,20 +19,20 @@ export const menus = [
   },
   {
     id: '3',
-    name: '预约任务管理',
-    url: '考生信息导入',
+    name: '考生信息导入',
+    url: 'StudentManage',
     type: 'MENU',
     parentId: '1',
-    sequence: 1,
+    sequence: 2,
     enable: true,
   },
   {
     id: '4',
     name: '预约名单详情',
-    url: 'OrganizationManage',
+    url: 'OrderRecodeManage',
     type: 'MENU',
     parentId: '1',
-    sequence: 1,
+    sequence: 3,
     enable: true,
   },
 ];

+ 1 - 7
src/router/constants.ts

@@ -2,12 +2,6 @@ export const NOT_FOUND = {
   name: 'NotFound',
 };
 
-export const HOME_PAGE_ROUTE = 'HomePage';
-
 export const DEFAULT_ROUTE_NAME = 'Workplace';
 
-export const WHITE_LIST = [
-  NOT_FOUND,
-  { name: 'Login' },
-  { name: HOME_PAGE_ROUTE },
-];
+export const WHITE_LIST = [NOT_FOUND, { name: 'Login' }];

+ 0 - 28
src/router/routes/modules/admin.ts

@@ -1,28 +0,0 @@
-import { AppRouteRecordRaw } from '../types';
-
-const ADMIN: AppRouteRecordRaw = {
-  path: '/admin',
-  component: () => import('@/views/admin/index.vue'),
-  children: [
-    {
-      path: 'system-role-manage',
-      name: 'SystemRoleManage',
-      component: () => import('@/views/admin/system-role-manage/index.vue'),
-      meta: {
-        title: '角色管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'scan-log-manage',
-      name: 'ScanLogManage',
-      component: () => import('@/views/admin/scan-log-manage/index.vue'),
-      meta: {
-        title: '扫描日志管理',
-        requiresAuth: true,
-      },
-    },
-  ],
-};
-
-export default ADMIN;

+ 42 - 0
src/router/routes/modules/order.ts

@@ -0,0 +1,42 @@
+import { DEFAULT_LAYOUT } from '../base';
+import { AppRouteRecordRaw } from '../types';
+
+const ORDER: AppRouteRecordRaw = {
+  path: '/order',
+  name: 'order',
+  component: DEFAULT_LAYOUT,
+  meta: {
+    requiresAuth: true,
+  },
+  children: [
+    {
+      path: 'task-manage',
+      name: 'TaskManage',
+      component: () => import('@/views/order/task-manage/index.vue'),
+      meta: {
+        title: '预约任务管理',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: 'student-manage',
+      name: 'StudentManage',
+      component: () => import('@/views/order/student-manage/index.vue'),
+      meta: {
+        title: '考生信息导入',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: 'order-recode-manage',
+      name: 'OrderRecodeManage',
+      component: () => import('@/views/order/order-recode-manage/index.vue'),
+      meta: {
+        title: '预约名单详情',
+        requiresAuth: true,
+      },
+    },
+  ],
+};
+
+export default ORDER;

+ 2 - 22
src/store/modules/app/index.ts

@@ -1,27 +1,9 @@
 import { defineStore } from 'pinia';
 import { sysMenu } from '@/api/user';
-import staticMenu from '@/constants/staticMenu';
-import { HOME_PAGE_ROUTE } from '@/router/constants';
 
 import type { UserMenuItem } from '@/api/types/user';
 import { AppState, PrivilegeItem, AppMenuItem } from './types';
 
-function initPrivilegeMap(data: UserMenuItem[]) {
-  const privilegeMap: Record<string, string[]> = {};
-  const pageSetTypes = ['conditions', 'buttons', 'lists', 'links'];
-  data.forEach((item) => {
-    privilegeMap[item.url] = [item.id];
-    pageSetTypes.forEach((type) => {
-      if (item[type] && item[type].length) {
-        item[type].forEach((elem: UserMenuItem) => {
-          privilegeMap[item.url].push(`${elem.type}_${elem.url}`.toLowerCase());
-        });
-      }
-    });
-  });
-  return privilegeMap;
-}
-
 function transformMenu(list: UserMenuItem[]): PrivilegeItem[] {
   return list.map((item) => {
     return {
@@ -66,10 +48,10 @@ function getMenu(privilegeData: PrivilegeItem[]): {
 const useAppStore = defineStore('app', {
   state: (): AppState => ({
     version: '',
-    privilegeMap: {},
     appMenus: [],
     privilegeList: [],
     validRoutes: [],
+    breadcrumbs: [],
   }),
 
   getters: {
@@ -88,8 +70,7 @@ const useAppStore = defineStore('app', {
     async fetchServerMenu() {
       const res = await sysMenu().catch(() => false);
       if (!res) return;
-      const privilegeList: UserMenuItem[] = [...staticMenu, ...res.privileges];
-      this.privilegeMap = initPrivilegeMap(privilegeList);
+      const privilegeList: UserMenuItem[] = [...res.privileges];
       this.privilegeList = transformMenu(privilegeList);
       const { menuList, validRoutes } = getMenu(this.privilegeList);
       this.appMenus = menuList;
@@ -98,7 +79,6 @@ const useAppStore = defineStore('app', {
     getMenuFirstRouter() {
       let firstRouteName = '';
       let menu = this.appMenus[0];
-      if (menu.url === HOME_PAGE_ROUTE) return { name: HOME_PAGE_ROUTE };
 
       while (menu) {
         firstRouteName = menu.url;

+ 2 - 7
src/store/modules/app/types.ts

@@ -1,16 +1,11 @@
 import type { UserMenuItem } from '@/api/types/user';
 
-export type PrivilegeItem = Pick<
-  UserMenuItem,
-  'id' | 'name' | 'type' | 'url' | 'parentId'
->;
-
-export type AppMenuItem = { children: AppMenuItem[] } & PrivilegeItem;
+export type AppMenuItem = { children: AppMenuItem[] } & UserMenuItem;
 
 export interface AppState {
   version: string;
-  privilegeMap: Record<string, string[]>;
   appMenus: AppMenuItem[];
   privilegeList: PrivilegeItem[];
   validRoutes: string[];
+  breadcrumbs: string[];
 }

+ 0 - 223
src/views/admin/index.vue

@@ -1,223 +0,0 @@
-<template>
-  <div class="admin">
-    <div class="home-header">
-      <div class="head-menu"></div>
-      <div class="head-user menu-list">
-        <ul>
-          <li @click="toSelectSchool">
-            <icon-home />
-            <span>切换学校</span>
-          </li>
-          <li class="menu-item menu-item-account" @click="toResetPwd">
-            <icon-user />
-            <span :title="userStore.realName">{{ userStore.realName }}</span>
-          </li>
-          <li class="menu-item" @click="toLogout">
-            <i class="icon icon-logout" title="退出登录"></i>
-          </li>
-        </ul>
-      </div>
-    </div>
-
-    <div class="home-navs">
-      <div class="head-logo">
-        <div class="head-logo-content">
-          <h1>预约报名系统</h1>
-        </div>
-      </div>
-      <a-menu
-        v-if="menus.length"
-        class="arco-menu-home"
-        :default-selected-keys="[curRouteName]"
-        :default-open-keys="curSubMenuNames"
-        @menu-item-click="toMenuItem"
-      >
-        <a-sub-menu v-for="submenu in menus" :key="submenu.url">
-          <template #title>
-            <span>{{ submenu.name }}</span>
-          </template>
-
-          <a-menu-item v-for="nav in submenu.children" :key="nav.url">
-            <span>{{ nav.name }}</span>
-          </a-menu-item>
-        </a-sub-menu>
-      </a-menu>
-    </div>
-
-    <div class="home-body">
-      <div class="home-body-content">
-        <div v-if="breadcrumbs.length" class="home-breadcrumb">
-          <span class="breadcrumb-tips">
-            <icon-home />
-            <span>当前所在位置:</span>
-          </span>
-          <a-breadcrumb>
-            <a-breadcrumb-item
-              v-for="(bread, index) in breadcrumbs"
-              :key="index"
-              >{{ bread.name }}</a-breadcrumb-item
-            >
-          </a-breadcrumb>
-        </div>
-
-        <!-- home-view: page detail -->
-        <div class="home-view">
-          <router-view />
-        </div>
-      </div>
-    </div>
-  </div>
-  <!-- ResetPwd -->
-  <ResetPwd
-    ref="ResetPwdRef"
-    :user-info="userInfo"
-    @modified="resetPwdModified"
-  />
-</template>
-
-<script setup lang="ts">
-  import { onMounted, ref, watch } from 'vue';
-  import { useRoute, useRouter } from 'vue-router';
-  import { Message, Modal } from '@arco-design/web-vue';
-  import { IconHome, IconUser } from '@arco-design/web-vue/es/icon';
-  import localNavs from '@/constants/adminNavs';
-  import { useUserStore } from '@/store';
-  import { SYS_ADMIN_NAME } from '@/constants/enumerate';
-
-  import ResetPwd from '@/views/login/login/ResetPwd.vue';
-
-  defineOptions({
-    name: 'Admin',
-  });
-
-  const userStore = useUserStore();
-  const route = useRoute();
-  const router = useRouter();
-
-  const menus = ref([]);
-  const privileges = ref(localNavs);
-  const curRouteName = ref('');
-  const curSubMenuNames = ref([]);
-  const breadcrumbs = ref([]);
-  const userInfo = ref({
-    pwdCount: 0,
-    mobileNumber: 1,
-    userId: '',
-  });
-  const ResetPwdRef = ref(null);
-
-  function initData() {
-    menus.value = getMenu();
-    curSubMenuNames.value = menus.value.map((item) => item.url);
-
-    if (route.name === 'Admin') {
-      const firstRouteName = getFirstRouter();
-      router.replace({
-        name: firstRouteName,
-      });
-      return;
-    }
-
-    actCurNav();
-  }
-
-  function getFirstRouter() {
-    let childNavs = privileges.value;
-    const parentId = childNavs[0].id;
-    let firstRouteName = '';
-    while (childNavs.length) {
-      firstRouteName = childNavs[0].url;
-      childNavs = privileges.value.filter((item) => item.parentId === parentId);
-    }
-
-    return firstRouteName;
-  }
-
-  function getMenu() {
-    const menuList = privileges.value.filter((item) => item.parentId === '-1');
-    const toTree = (data) => {
-      data.forEach((menu) => {
-        const children = privileges.value.filter(
-          (item) => item.parentId === menu.id
-        );
-        if (children.length) {
-          menu.children = children;
-          toTree(menu.children);
-        }
-      });
-    };
-    toTree(menuList);
-
-    return menuList;
-  }
-
-  function actCurNav() {
-    curRouteName.value = route.name;
-    const breadcrumbsData = [];
-
-    let curBreadcrumb = privileges.value.find(
-      (item) => item.url === curRouteName.value
-    );
-    if (curBreadcrumb) breadcrumbsData.push({ ...curBreadcrumb });
-
-    while (curBreadcrumb && curBreadcrumb.parentId !== '-1') {
-      const pid = curBreadcrumb.parentId;
-      curBreadcrumb = privileges.value.find((item) => item.id === pid);
-      if (curBreadcrumb) breadcrumbsData.unshift({ ...curBreadcrumb });
-    }
-    breadcrumbs.value = breadcrumbsData;
-  }
-
-  function toLogout() {
-    Modal.confirm({
-      title: '提示',
-      content: '确定要退出登录吗?',
-      escToClose: false,
-      maskClosable: false,
-      closable: false,
-      hideCancel: true,
-      titleAlign: 'start',
-      onOk() {
-        userStore.logout();
-      },
-    });
-  }
-
-  function toMenuItem(val) {
-    router.push({ name: val });
-  }
-
-  function toSelectSchool() {
-    router.push({ name: 'SelectSchool' });
-  }
-  function toResetPwd() {
-    ResetPwdRef.value?.open();
-  }
-  function resetPwdModified() {
-    userStore.logout();
-  }
-
-  onMounted(() => {
-    if (userStore.loginName !== SYS_ADMIN_NAME) {
-      Message.error('非法操作!');
-      router.replace({ name: 'Login' });
-      return;
-    }
-
-    if (route.name === 'Admin') {
-      router.replace({
-        name: 'SchoolManage',
-      });
-      return;
-    }
-    initData();
-  });
-
-  watch(
-    () => route.name,
-    (val) => {
-      if (val === 'Admin') return;
-      actCurNav();
-    }
-  );
-</script>

+ 0 - 112
src/views/admin/scan-log-manage/index.vue

@@ -1,112 +0,0 @@
-<template>
-  <div class="part-box is-filter">
-    <a-form
-      layout="inline"
-      :model="searchModel"
-      :label-col-props="{ span: 0, offset: 0 }"
-      :wrapper-col-props="{ span: 24, offset: 0 }"
-    >
-      <a-form-item label="学校">
-        <a-select
-          v-model="searchModel.schoolCode"
-          placeholder="选择学校"
-          filterable
-        >
-          <a-option
-            v-for="item in schools"
-            :key="item.code"
-            :value="item.code"
-            :label="item.name"
-          ></a-option>
-        </a-select>
-      </a-form-item>
-      <a-form-item label="关键词">
-        <a-input
-          v-model.trim="searchModel.loginName"
-          placeholder="扫描账号模糊查询"
-          clearable
-        ></a-input>
-      </a-form-item>
-      <a-form-item>
-        <a-button type="primary" @click="toPage(1)">查询</a-button>
-      </a-form-item>
-    </a-form>
-  </div>
-  <div class="part-box">
-    <a-table :columns="columns" :data="dataList" :pagination="pagination">
-      <template #action="{ record }">
-        <a-button type="text" @click="toView(record)">查看</a-button>
-      </template>
-    </a-table>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref, reactive } from 'vue';
-  import { schoolList, scanLogListPage } from '@/api/admin';
-  import { SchoolItem, ScanLogItem } from '@/api/types/admin';
-  import { xhrDownload } from '@/utils/download';
-  import useTable from '@/hooks/table';
-  import { TableColumnData } from '@arco-design/web-vue';
-
-  defineOptions({
-    name: 'ScanLogManage',
-  });
-
-  const searchModel = reactive({
-    schoolCode: '',
-    loginName: '',
-  });
-
-  const schools = ref<SchoolItem>([]);
-  async function getSchools() {
-    const res = await schoolList();
-    schools.value = res || [];
-  }
-  getSchools();
-
-  const columns: TableColumnData[] = [
-    {
-      title: '学校Code',
-      dataIndex: 'schoolCode',
-    },
-    {
-      title: '扫描账号',
-      dataIndex: 'loginName',
-    },
-    {
-      title: '设备号',
-      dataIndex: 'deviceCode',
-    },
-    {
-      title: '文件名',
-      dataIndex: 'fileName',
-    },
-    {
-      title: '更新时间',
-      dataIndex: 'uploadTime',
-    },
-    {
-      title: '操作',
-      slotName: 'action',
-      width: 100,
-      cellClass: 'action-column',
-    },
-  ];
-  const { dataList, pagination, toPage } = useTable<ScanLogItem[]>(
-    scanLogListPage,
-    searchModel,
-    true
-  );
-
-  async function toView(row: ScanLogItem) {
-    if (row.type === 'NORMAL') {
-      const res = await xhrDownload({ url: row.pathUrl });
-      const win = window.open('', '_blank');
-      win.document.write(`<pre>${res}</pre>`);
-      win.document.close();
-    } else {
-      window.open(row.pathUrl);
-    }
-  }
-</script>

+ 0 - 0
src/views/admin/school-manage/index.vue


+ 0 - 62
src/views/admin/system-role-manage/index.vue

@@ -1,62 +0,0 @@
-<template>
-  <div class="part-box box-justify">
-    <div></div>
-    <a-button type="primary" @click="toAdd">添加角色</a-button>
-  </div>
-  <div class="part-box">
-    <a-table :columns="columns" :data="dataList" :pagination="false">
-      <template #index="{ rowIndex }">
-        {{ getRowIndex(rowIndex) }}
-      </template>
-      <template #action="{ record }">
-        <a-button type="text" @click="toEdit(record)">编辑</a-button>
-      </template>
-    </a-table>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue';
-  import { adminRoleListPage } from '@/api/admin';
-  import { AdminRoleItem } from '@/api/types/admin';
-  import { TableColumnData } from '@arco-design/web-vue';
-
-  defineOptions({
-    name: 'SystemRoleManage',
-  });
-
-  const columns: TableColumnData[] = [
-    {
-      title: '序号',
-      slotName: 'index',
-      width: 80,
-    },
-    {
-      title: '角色名称',
-      dataIndex: 'name',
-    },
-    {
-      title: '操作',
-      slotName: 'action',
-      width: 120,
-      cellClass: 'action-column',
-    },
-  ];
-  const dataList = ref([]);
-  async function getList() {
-    const res = await adminRoleListPage();
-    dataList.value = (res || []).map((item, iindex) => {
-      return {
-        ...item,
-        index: iindex + 1,
-      };
-    });
-  }
-  getList();
-
-  function toAdd() {}
-
-  function toEdit(row: AdminRoleItem) {
-    console.log(row);
-  }
-</script>

+ 50 - 164
src/views/login/login/ResetPwd.vue

@@ -1,74 +1,39 @@
 <template>
-  <a-modal v-model:visible="visible">
-    <template #title> {{ title }} </template>
+  <a-modal v-model:visible="visible" title="修改密码">
     <a-form ref="formRef" :model="formData" :rules="rules">
-      <!-- reset pwd -->
-      <template v-if="needResetPwd">
-        <a-form-item field="oldPassword" label="旧密码" auto-label-width>
-          <a-input-password
-            v-model.trim="formData.oldPassword"
-            placeholder="请输入旧密码"
-            allow-clear
-          >
-            <template #prefix>
-              <icon-lock />
-            </template>
-          </a-input-password>
-        </a-form-item>
-        <a-form-item field="password" label="新密码">
-          <a-input-password
-            v-model.trim="formData.password"
-            placeholder="请输入新密码"
-            allow-clear
-          >
-            <template #prefix>
-              <icon-lock />
-            </template>
-          </a-input-password>
-        </a-form-item>
-        <a-form-item field="rePassword" label="再次密码">
-          <a-input-password
-            v-model.trim="formData.rePassword"
-            placeholder="请再次输入新密码"
-            allow-clear
-          >
-            <template #prefix>
-              <icon-lock />
-            </template>
-          </a-input-password>
-        </a-form-item>
-      </template>
-      <!-- bind mobile -->
-      <template v-if="needBindMobile">
-        <a-form-item field="mobileNumber" label="手机号">
-          <a-input
-            v-model.trim="formData.mobileNumber"
-            placeholder="请输入手机号"
-            allow-clear
-          >
-            <template #prefix> <icon-mobile /> </template>
-          </a-input>
-        </a-form-item>
-        <a-form-item field="code" label="验证码">
-          <div class="box-justify">
-            <a-input
-              v-model.trim="formData.code"
-              placeholder="请输入手机验证码"
-              allow-clear
-            >
-              <template #prefix>
-                <icon-message />
-              </template>
-            </a-input>
-            <a-button
-              type="primary"
-              :disabled="isFetchingCode"
-              @click="fetchMobileSmsCode"
-              >{{ codeContent }}</a-button
-            >
-          </div>
-        </a-form-item>
-      </template>
+      <a-form-item field="oldPassword" label="旧密码" auto-label-width>
+        <a-input-password
+          v-model.trim="formData.oldPassword"
+          placeholder="请输入旧密码"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
+      <a-form-item field="password" label="新密码">
+        <a-input-password
+          v-model.trim="formData.password"
+          placeholder="请输入新密码"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
+      <a-form-item field="rePassword" label="再次密码">
+        <a-input-password
+          v-model.trim="formData.rePassword"
+          placeholder="请再次输入新密码"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
     </a-form>
 
     <template #footer>
@@ -81,67 +46,41 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, reactive, ref } from 'vue';
+  import { reactive, ref } from 'vue';
   import type { FormInstance, FieldRule } from '@arco-design/web-vue/es/form';
-  import { smscode, phone, password, strictPassword } from '@/utils/formRules';
+  import { password, strictPassword } from '@/utils/formRules';
   import useLoading from '@/hooks/loading';
-  import useSms from '@/hooks/sms';
-  import { getSmsCodeForBind, updatePwd } from '@/api/user';
+  import { updatePwd } from '@/api/user';
   import { Message } from '@arco-design/web-vue';
   import { getBase64 } from '@/utils/crypto';
+  import { useUserStore } from '@/store';
+  import useModal from '@/hooks/modal';
 
   defineOptions({
     name: 'ResetPwd',
   });
 
-  const defaultUserInfo = {
-    userId: '',
-    loginName: '',
-    schoolCode: '',
-    password: '',
-    mobileNumber: '',
-    pwdCount: 0,
-    phoneLogin: false,
-  };
+  /* modal */
+  const { visible, open, close } = useModal();
+  defineExpose({ open, close });
+
+  const emit = defineEmits(['modified']);
+
+  const userStore = useUserStore();
   const defaultFormData = {
-    id: '',
     oldPassword: '',
     password: '',
     rePassword: '',
-    mobileNumber: '',
-    code: '',
   };
   type FormDataType = typeof defaultFormData;
 
-  const props = defineProps<{
-    userInfo: typeof defaultUserInfo;
-  }>();
-
-  const emit = defineEmits(['modified']);
-
-  const needBindMobile = computed(() => {
-    return !props.userInfo.mobileNumber && props.userInfo.phoneLogin;
-  });
-  const needResetPwd = computed(() => {
-    return !props.userInfo.pwdCount;
-  });
-  const title = computed(() => {
-    if (needBindMobile.value && needResetPwd.value)
-      return '修改密码与绑定手机号';
-    if (needBindMobile.value) return '绑定手机号';
-    if (needResetPwd.value) return '修改密码';
-
-    return '修改密码';
-  });
-
-  const visible = ref(false);
   const formRef = ref<FormInstance>();
   const formData = reactive<FormDataType>({ ...defaultFormData });
   const passwordRule: FieldRule[] = [
     ...strictPassword,
     {
       validator: (value, callback) => {
-        if (value === props.userInfo.loginName) {
+        if (value === userStore.loginName) {
           return callback('禁止使用用户账户号作为密码');
         }
         return callback();
@@ -149,8 +88,6 @@
     },
   ];
   const rules: Record<keyof FormDataType, FieldRule[]> = {
-    code: smscode,
-    mobileNumber: phone,
     oldPassword: password,
     password: [
       ...passwordRule,
@@ -178,36 +115,6 @@
     ],
   };
 
-  /* sms  ----start */
-  const { isFetchingCode, codeContent, changeContent } = useSms('login');
-
-  async function fetchMobileSmsCode() {
-    const unvalid = await formRef.value.validateField([
-      'mobileNumber',
-      'schoolCode',
-    ]);
-    if (unvalid) return;
-
-    isFetchingCode.value = true;
-    const res = await getSmsCodeForBind({
-      schoolCode: formData.schoolCode,
-      mobileNumber: formData.mobileNumber,
-      loginName: formData.loginName,
-      password: getBase64(formData.oldPassword),
-    }).catch(() => {
-      isFetchingCode.value = false;
-    });
-    if (!res) return;
-
-    Message.success(
-      `已向手机尾号【${formData.mobileNumber.slice(
-        -4
-      )}】成功发送短信,请在2分钟内进行验证!`
-    );
-    changeContent();
-  }
-  /* sms  ----end */
-
   /* confirm */
   const { loading, setLoading } = useLoading();
   async function confirm() {
@@ -215,23 +122,11 @@
     if (err) return;
 
     setLoading(true);
-    let datas = {
-      id: formData.id,
+    const datas = {
+      id: userStore.id,
       oldPassword: getBase64(formData.oldPassword),
+      password: getBase64(formData.password),
     };
-    if (needBindMobile.value) {
-      datas = {
-        ...datas,
-        mobileNumber: formData.mobileNumber,
-        verifyCode: formData.code,
-      };
-    }
-    if (needResetPwd.value) {
-      datas = {
-        ...datas,
-        password: getBase64(formData.password),
-      };
-    }
     const res = await updatePwd(datas).catch(() => false);
     setLoading(false);
     if (!res) return;
@@ -239,13 +134,4 @@
     emit('modified', datas);
     close();
   }
-
-  /* modal */
-  function open() {
-    visible.value = true;
-  }
-  function close() {
-    visible.value = false;
-  }
-  defineExpose({ open, close });
 </script>

+ 0 - 0
src/views/admin/auth-set/index.vue → src/views/order/order-recode-manage/index.vue


+ 0 - 0
src/views/admin/privilege-manage/index.vue → src/views/order/student-manage/index.vue


+ 129 - 0
src/views/order/task-manage/index.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="part-box is-filter">
+    <a-form
+      layout="inline"
+      :model="searchModel"
+      :label-col-props="{ span: 0, offset: 0 }"
+      :wrapper-col-props="{ span: 24, offset: 0 }"
+    >
+      <a-form-item label="任务名称">
+        <a-input
+          v-model.trim="searchModel.name"
+          placeholder="任务名称"
+          allow-clear
+        ></a-input>
+      </a-form-item>
+      <a-form-item>
+        <a-button type="primary" @click="toPage(1)">查询</a-button>
+      </a-form-item>
+    </a-form>
+    <div>
+      <a-button type="primary" @click="toAdd">新增</a-button>
+    </div>
+  </div>
+  <div class="part-box">
+    <a-table :columns="columns" :data="dataList" :pagination="pagination">
+      <!-- <template #roles="{ record }">
+        {{ getRoleLabel(record.roles) }}
+      </template> -->
+      <template #action="{ record }">
+        <a-button type="text" class="btn-primary" @click="toEdit(record)"
+          >编辑</a-button
+        >
+        <a-button
+          :class="record.enable ? 'btn-danger' : 'btn-primary'"
+          type="text"
+          @click="toEnable(record)"
+          >{{ record.enable ? '禁用' : '启用' }}</a-button
+        >
+      </template>
+    </a-table>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref } from 'vue';
+  import { Message, TableColumnData } from '@arco-design/web-vue';
+  import { ableTask, taskListPage } from '@/api/order';
+  import { TaskItem } from '@/api/types/order';
+  import useTable from '@/hooks/table';
+
+  import { useUserStore } from '@/store';
+  import { modalConfirm } from '@/utils/arco';
+
+  defineOptions({
+    name: 'TaskManage',
+  });
+
+  const userStore = useUserStore();
+
+  const searchModel = reactive({
+    name: '',
+  });
+
+  const columns: TableColumnData[] = [
+    {
+      title: '用户名/工号',
+      dataIndex: 'loginName',
+    },
+    {
+      title: '姓名',
+      dataIndex: 'realName',
+    },
+    {
+      title: '手机号',
+      dataIndex: 'mobileNumber',
+    },
+    {
+      title: '所在机构',
+      dataIndex: 'orgName',
+    },
+    {
+      title: '角色',
+      dataIndex: 'roles',
+      slotName: 'roles',
+    },
+    {
+      title: '状态',
+      dataIndex: 'enable',
+      slotName: 'enable',
+    },
+    {
+      title: '操作',
+      slotName: 'action',
+      width: 220,
+      fixed: 'right',
+      cellClass: 'action-column',
+    },
+  ];
+  const { dataList, pagination, toPage, getList } = useTable<TaskItem[]>(
+    taskListPage,
+    searchModel,
+    true
+  );
+
+  // table action
+  const modifyUserRef = ref(null);
+  const curRow = ref({});
+  function toAdd() {
+    curRow.value = {};
+    modifyUserRef.value?.open();
+  }
+  function toEdit(row: TaskItem) {
+    curRow.value = row;
+    modifyUserRef.value?.open();
+  }
+
+  async function toEnable(row: TaskItem) {
+    const action = row.enable ? '禁用' : '启用';
+    const confirmRes = await modalConfirm(
+      '提示',
+      `确定要${action}任务【${row.name}】吗?`
+    ).catch(() => false);
+    if (confirmRes !== 'confirm') return;
+
+    await ableTask({ id: row.id, enable: !row.enable });
+    Message.success('操作成功!');
+    getList();
+  }
+</script>