Prechádzať zdrojové kódy

feat: 添加签到导出功能

曾坤森 3 mesiacov pred
rodič
commit
4999955d54

+ 1 - 1
config/vite.config.dev.ts

@@ -12,7 +12,7 @@ export default mergeConfig(
       proxy: {
         '/api': {
           // 代理路径前缀
-          target: 'http://192.168.1.12:8002/', // 目标服务器地址
+          target: 'http://192.168.1.12:8001/', // 目标服务器地址
           changeOrigin: true, // 修改请求头中的 Origin
           rewrite: path => path.replace(/^\/api/, ''), // 重写路径(可选)
         },

+ 13 - 0
src/api/record.ts

@@ -26,3 +26,16 @@ export async function queryRecordList(
   const res = await instance.post('/api/getPassengerRecord', params);
   return res.data;
 }
+export async function fetchCheckIn(params: object): Promise<RecordListRes> {
+  const res = await instance.post('/api/TimeCards', params);
+  return res.data;
+}
+export async function getCheckInList(params: object): Promise<any> {
+  const res = await instance.post('/api/GetOneMonthTimeCards', params, {
+    responseType: 'blob',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+  });
+  return res;
+}

+ 65 - 0
src/components/business/CheckIn/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div>
+    <a-modal
+      v-model:visible="visible"
+      :title="$t('navbar.check.info')"
+      @cancel="() => handleCancel()"
+      @before-ok="handleBeforeOk"
+    >
+      <a-form :model="form">
+        <a-form-item field="name" label="ID">
+          <a-input v-model="form.id" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+<script setup lang="ts" name="CheckInPage">
+import { reactive, ref, shallowRef, watch, getCurrentInstance } from 'vue';
+import { fetchCheckIn } from '@/api/record';
+import dayjs from 'dayjs';
+const this_ = getCurrentInstance()?.appContext.config.globalProperties;
+interface CheckInPageProps {
+  modelValue: boolean;
+}
+interface CheckInPageEmits {
+  (e: 'update:modelValue', value: boolean): void;
+}
+const props = withDefaults(defineProps<CheckInPageProps>(), {
+  modelValue: false,
+});
+const emit = defineEmits<CheckInPageEmits>();
+const visible = shallowRef<boolean>(false);
+
+watch(
+  () => props.modelValue,
+  value => {
+    visible.value = value;
+  }
+);
+let form = reactive({
+  id: '',
+  time: '',
+});
+
+const handleBeforeOk = (done: (closed: boolean) => void) => {
+  form.time = dayjs().format('YYYY-MM-DD HH:mm:ss');
+  fetchCheckIn(form)
+    .then(res => {
+      this_?.$message?.success('签到成功');
+      done(true); // 关闭模态框
+      handleCancel();
+    })
+    .catch(() => {
+      done(false); // 不关闭模态框(例如提交失败时)
+    });
+};
+const handleCancel = () => {
+  form = {
+    id: '',
+    time: '',
+  };
+  visible.value = false;
+  emit('update:modelValue', false);
+};
+</script>

+ 19 - 4
src/components/navbar/index.vue

@@ -21,6 +21,16 @@
       <Menu v-if="topMenu" />
     </div>
     <ul class="right-side">
+      <li>
+        <a-button
+          type="primary"
+          shape="round"
+          size="mini"
+          @click="handleCheckInFun"
+        >
+          {{ $t('navbar.check.locale') }}
+        </a-button>
+      </li>
       <li>
         <a-tooltip :content="$t('settings.search')">
           <a-button class="nav-btn" type="outline" :shape="'circle'">
@@ -80,7 +90,7 @@
           </a-button>
         </a-tooltip>
       </li>
-      <li>
+      <!-- <li>
         <a-tooltip :content="$t('settings.navbar.alerts')">
           <div class="message-box-trigger">
             <a-badge :count="9" dot>
@@ -106,7 +116,7 @@
             <message-box />
           </template>
         </a-popover>
-      </li>
+      </li> -->
       <li>
         <a-tooltip
           :content="
@@ -190,11 +200,12 @@
         </a-dropdown>
       </li>
     </ul>
+    <CheckInPage v-model="showCheckInDialog"></CheckInPage>
   </div>
 </template>
 
 <script name="NavBar" lang="ts" setup>
-import { computed, ref } from 'vue';
+import { computed, ref, shallowRef } from 'vue';
 import { Message } from '@arco-design/web-vue';
 import { useDark, useToggle, useFullscreen } from '@vueuse/core';
 import { useAppStore, useUserStore } from '@/store';
@@ -204,7 +215,7 @@ import useUser from '@/hooks/user';
 import Menu from '@/components/menu/index.vue';
 import MessageBox from '../message-box/index.vue';
 import GlobalBreadcrumb from '../global-breadcrumb/index.vue';
-
+import CheckInPage from '@/components/business/CheckIn/index.vue';
 const appStore = useAppStore();
 const userStore = useUserStore();
 const { logout } = useUser();
@@ -228,6 +239,7 @@ const currentWidth = computed(() => {
   let width = menuWidth.value - 12;
   return collapsed.value ? 'calc(100% - 38px)' : `calc(100% - ${width}px)`;
 });
+const showCheckInDialog = shallowRef<boolean>(false);
 
 const isDark = useDark({
   selector: 'body',
@@ -280,6 +292,9 @@ const toggleCollapse = () => {
   setCollapse(!collapsed.value);
   // toggleWidth(collapsed.value);
 };
+const handleCheckInFun = () => {
+  showCheckInDialog.value = true;
+};
 </script>
 
 <style scoped lang="less">

+ 5 - 2
src/locale/en-US.ts

@@ -15,11 +15,14 @@ export default {
   'menu.server.manage': 'Manage-Server',
   'menu.server.face': 'Face-Server',
   'menu.server.record': 'Record-Server',
-  'menu.list': 'List',
   'menu.exception': 'Exception',
   'menu.user': 'User Center',
-  'navbar.docs': 'Docs',
   'navbar.action.locale': 'Switch to English',
+  'navbar.check.locale': 'Check In',
+  'navbar.check.info': 'Check In Info',
+  'searchTable.form.down': 'Down',
+  'searchTable.form.month': 'Month',
+
   ...localeSettings,
   ...localeMessageBox,
   ...localeLogin,

+ 5 - 2
src/locale/zh-CN.ts

@@ -16,11 +16,14 @@ export default {
   'menu.server.manage': '管理中心-服务端',
   'menu.server.record': '通行记录-服务端',
   'menu.server.face': '人脸库',
-  'menu.list': '列表页',
   'menu.exception': '异常页',
   'menu.user': '个人中心',
-  'navbar.docs': '文档中心',
   'navbar.action.locale': '切换为中文',
+  'navbar.check.locale': '签到',
+  'navbar.check.info': '签到信息',
+  'searchTable.form.down': '下载',
+  'searchTable.form.month': '月份',
+
   ...localeSettings,
   ...localeMessageBox,
   ...localeLogin,

+ 60 - 1
src/views/dashboard/record-list/index.vue

@@ -1,6 +1,35 @@
 <template>
   <div class="container">
     <a-card class="general-card" title="">
+      <a-row>
+        <a-col :flex="1">
+          <a-form
+            :model="formModel"
+            :label-col-props="{ span: 4 }"
+            :wrapper-col-props="{ span: 18 }"
+            label-align="left"
+          >
+            <a-row :gutter="16">
+              <a-col :span="8">
+                <a-form-item
+                  :label="$t('searchTable.form.month')"
+                  label-col-flex="60px"
+                >
+                  <a-month-picker v-model="formModel.time" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-button type="primary" @click="downloadExcel">
+                  <template #icon>
+                    <icon-download />
+                  </template>
+                  {{ $t('searchTable.form.down') }}
+                </a-button>
+              </a-col>
+            </a-row>
+          </a-form>
+        </a-col>
+      </a-row>
       <a-table
         row-key="name"
         :loading="loading"
@@ -30,12 +59,15 @@ import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
 import { useI18n } from 'vue-i18n';
 import useLoading from '@/hooks/loading';
 import { Pagination } from '@/types/global';
-import { queryRecordList } from '@/api/record';
+import { queryRecordList, getCheckInList } from '@/api/record';
 import type { RecordParams, DataList } from '@/api/record';
 import { useIntervalFn } from '@vueuse/core';
+import dayjs from 'dayjs';
+
 interface FaceParams {
   pageIndex: number;
   personNumber: number;
+  time: string;
 }
 type SizeProps = 'mini' | 'small' | 'medium' | 'large';
 type Column = TableColumnData & { checked?: true };
@@ -47,6 +79,7 @@ const generateFormModel = () => {
   return {
     pageIndex: 1,
     personNumber: 20,
+    time: dayjs().format('YYYY-MM'),
   } as FaceParams;
 };
 const formModel = ref<FaceParams>(generateFormModel());
@@ -139,6 +172,32 @@ const onPageChange = (current: number) => {
   formModel.value.pageIndex = current;
   fetchData();
 };
+const downloadExcel = () => {
+  setLoading(true);
+  const params = {
+    time: formModel.value.time,
+  };
+  getCheckInList(params)
+    .then(res => {
+      const contentDisposition = res.headers['content-disposition'];
+      const filenameMatch = contentDisposition.match(/filename="?(.+)"?/);
+      const filename = filenameMatch ? filenameMatch[1] : 'default.xlsx';
+      const blob = new Blob([res.data], {
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+      });
+      const downloadUrl = window.URL.createObjectURL(blob);
+      const link = document.createElement('a');
+      link.href = downloadUrl;
+      link.setAttribute('download', filename);
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      window.URL.revokeObjectURL(downloadUrl);
+    })
+    .finally(() => {
+      setLoading(false);
+    });
+};
 </script>
 
 <style scoped lang="less">