wuheng 1 жил өмнө
parent
commit
9fce909d1e

+ 1 - 0
.env

@@ -18,3 +18,4 @@ VITE_ICON_PREFFIX=icon
 # 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFFIX
 # 格式 {VITE_ICON_PREFFIX}-{本地图标集合名称}
 VITE_ICON_LOCAL_PREFFIX=icon-local
+

+ 1 - 0
package.json

@@ -64,6 +64,7 @@
     "@fast-crud/ui-interface": "^1.13.6",
     "@fast-crud/ui-naive": "^1.13.6",
     "@soybeanjs/vue-materials": "^0.1.9",
+    "@vicons/antd": "^0.12.0",
     "@vueuse/core": "^10.1.2",
     "ant-design-vue": "4.0.0-rc.6",
     "axios": "1.4.0",

+ 7 - 0
pnpm-lock.yaml

@@ -37,6 +37,9 @@ dependencies:
   '@soybeanjs/vue-materials':
     specifier: ^0.1.9
     version: 0.1.9(vue@3.3.4)
+  '@vicons/antd':
+    specifier: ^0.12.0
+    version: 0.12.0
   '@vueuse/core':
     specifier: ^10.1.2
     version: 10.1.2(vue@3.3.4)
@@ -4199,6 +4202,10 @@ packages:
       nanoid: 3.3.6
     dev: false
 
+  /@vicons/antd@0.12.0:
+    resolution: {integrity: sha512-C0p6aO1EmGG1QHrqgUWQS1No20934OdWSRQshM5NIDK5H1On6tC26U0hT6Rmp40KfUsvhvX5YW8BoWJdNFifPg==}
+    dev: false
+
   /@vicons/fluent@0.12.0:
     resolution: {integrity: sha512-ATCiqPuiJ6RI5GBlD3BIpZ9Xw4MsCA4RpI5oR6MCti4quS4mX1Gp6N74FCzw7lgOj+80rV4HMKhZTVInwimpVQ==}
     dev: true

+ 0 - 0
src/java.pdf → src/public/java.pdf


BIN
src/public/学员导入.xlsx


+ 11 - 0
src/typings/global.d.ts

@@ -19,3 +19,14 @@ declare namespace Common {
 
 /** 构建时间 */
 declare const PROJECT_BUILD_TIME: string;
+
+declare type Nullable<T> = T | null;
+declare type Recordable<T = any> = Record<string, T>;
+declare type ReadonlyRecordable<T = any> = {
+  readonly [key: string]: T;
+};
+
+declare module '*.xlsx' {
+  const src: string;
+  export default src;
+}

+ 1 - 0
src/utils/index.ts

@@ -3,3 +3,4 @@ export * from './storage';
 export * from './service';
 export * from './router';
 export * from './form';
+export * from './tools';

+ 71 - 0
src/utils/tools/index.ts

@@ -0,0 +1,71 @@
+import { unref } from 'vue';
+
+/**
+ * @description: 判断值是否未某个类型
+ */
+export function is(val: unknown, type: string) {
+  return toString.call(val) === `[object ${type}]`;
+}
+
+/**
+ * @description: 是否为对象
+ */
+const isObject = (val: any): val is Record<any, any> => {
+  return val !== null && is(val, 'Object');
+};
+
+/**
+ * 判断是否 url
+ * */
+export function isUrl(url: string) {
+  return /^(http|https):\/\//g.test(url);
+}
+
+export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
+  let key: string;
+  for (key in target) {
+    if (isObject(src[key])) {
+      deepMerge(src[key], target[key]);
+    } else {
+      src[key] = target[key];
+    }
+  }
+  return src;
+}
+
+export function getDynamicProps<T extends object, U>(props: T): Partial<U> {
+  const ret: Recordable = {};
+
+  Object.keys(props).map(key => {
+    ret[key] = unref((props as Recordable)[key]);
+    return ret;
+  });
+
+  return ret as Partial<U>;
+}
+/**
+ * @description: 是否已定义
+ */
+export const isDef = <T = unknown>(val?: T): val is T => {
+  return typeof val !== 'undefined';
+};
+
+// 是否为图片节点
+export function isImageDom(o: Element) {
+  return o && ['IMAGE', 'IMG'].includes(o.tagName);
+}
+
+function isNull(val: unknown): val is null {
+  return val === null;
+}
+
+export const isUnDef = <T = unknown>(val?: T): val is T => {
+  return !isDef(val);
+};
+export function isNullAndUnDef(val: unknown): val is null | undefined {
+  return isUnDef(val) && isNull(val);
+}
+
+export function isNullOrUnDef(val: unknown): val is null | undefined {
+  return isUnDef(val) || isNull(val);
+}

+ 1 - 2
src/views/dashboard/analysis/index.vue

@@ -10,8 +10,7 @@
 </template>
 
 <script lang="ts" setup>
-import PDFView from '../../.././components/View/pdfPreview.vue';
-import jsPdf from '../../../java.pdf';
+import jsPdf from '@/public/java.pdf';
 import { BottomPart, DataCard, TopChart } from './components';
 </script>
 

+ 4 - 4
src/views/management/student/api.ts

@@ -33,7 +33,7 @@ export interface Add_6Res {
 }
 
 export function editRequest(params: Add_Params): Promise<Service.RequestResult<Add_6Res>> {
-  return request.post(`/update`, params);
+  return request.post(`/student/update`, params);
 }
 
 export function pageRequest(
@@ -41,13 +41,13 @@ export function pageRequest(
   pageSize: number,
   params: Add_Params
 ): Promise<Service.RequestResult<Add_6Res>> {
-  return request.post(`/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+  return request.post(`/student/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
 }
 
 export function delRequest(id: number): Promise<Service.RequestResult<Add_6Res>> {
-  return request.post(`/del/${id}`);
+  return request.post(`/student/del/${id}`);
 }
 
 export function addRequest(params: Add_Params): Promise<Service.RequestResult<Add_6Res>> {
-  return request.post(`/add`, params);
+  return request.post(`/student/add`, params);
 }

+ 61 - 3
src/views/management/student/crud.ts

@@ -1,8 +1,8 @@
-import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import type { CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
 import { dict } from '@fast-crud/fast-crud';
+import importStudent from '@/public/学员导入.xlsx';
 import { addRequest, delRequest, editRequest, pageRequest } from './api';
-
-function curd(): CreateCrudOptionsRet {
+function curd({ context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
   return {
     crudOptions: {
       request: {
@@ -20,6 +20,64 @@ function curd(): CreateCrudOptionsRet {
           return delRequest(row.id);
         }
       },
+      toolbar: {
+        show: true
+      },
+      actionbar: {
+        show: true,
+        buttons: {
+          add: {
+            text: '新增学员',
+            title: '使用表单新增学员',
+            circle: false,
+            tooltip: {
+              slots: {
+                default() {
+                  return '使用表单新增学员';
+                }
+              }
+            }
+          },
+          importAdd: {
+            text: 'Excel文件导入',
+            title: '上传Excel文件批量导入学员',
+            icon: '',
+            iconRight: '',
+            circle: false,
+            style: {},
+            disabled: false,
+            tooltip: {
+              slots: {
+                default() {
+                  return '上传Excel文件,支持xlsx 格式';
+                }
+              }
+            },
+            show: true,
+            click: () => {
+              context?.openModal();
+            },
+            order: 1
+          },
+          exportAdd: {
+            text: '下载导入模板',
+            circle: false,
+            disabled: false,
+            tooltip: {
+              slots: {
+                default() {
+                  return '下载导入学员的 Excel 模板';
+                }
+              }
+            },
+            show: true,
+            click: () => {
+              window.location.href = importStudent;
+            },
+            order: 1
+          }
+        }
+      },
       columns: {
         studentName: {
           title: '姓名',

+ 58 - 2
src/views/management/student/index.vue

@@ -2,13 +2,69 @@
   <div class="h-full">
     <fs-crud ref="crudRef" v-bind="crudBinding" />
   </div>
+  <n-modal v-model:show="showModal" preset="dialog" title="Dialog">
+    <template #header>
+      <div>Excel 文件导入</div>
+    </template>
+    <div style="padding: 1rem">
+      <n-upload
+        multiple
+        directory-dnd
+        :action="uploadFile"
+        :max="1"
+        @before-upload="beforeUpload"
+        @finish="handleFinish"
+      >
+        <n-upload-dragger>
+          <div style="margin-bottom: 12px">
+            <n-icon size="48" :depth="3">
+              <archive-icon />
+            </n-icon>
+          </div>
+          <n-text style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </n-text>
+          <n-p depth="3" style="margin: 8px 0 0 0">
+            请不要上传敏感数据,比如你的银行卡号和密码,信用卡号有效期和安全码
+          </n-p>
+        </n-upload-dragger>
+      </n-upload>
+    </div>
+  </n-modal>
 </template>
 
 <script setup lang="ts">
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
+import type { UploadFileInfo } from 'naive-ui';
 import { useFs } from '@fast-crud/fast-crud';
+import { getServiceEnvConfig } from '~/.env-config';
 import createCrudOptions from './crud';
-const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
+
+const { url, proxyPattern } = getServiceEnvConfig(import.meta.env);
+
+const showModal = ref(false);
+function openModal() {
+  showModal.value = true;
+}
+function closeModal() {
+  showModal.value = false;
+}
+const context: any = {
+  openModal,
+  closeModal
+};
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions, context });
+const uploadFile = proxyPattern ? `${proxyPattern}/student/importExcel` : `${url}/student/importExcel`;
+function handleFinish() {
+  crudExpose.doRefresh();
+  closeModal();
+  window.$message?.success('上传成功');
+}
+function beforeUpload(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) {
+  if (data.file.file?.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
+    window.$message?.error('只能上传 Excel 格式的表格文件 请重新上传');
+    return false;
+  }
+  return true;
+}
 onMounted(() => {
   crudExpose.doRefresh();
 });

+ 1 - 0
vite.config.ts

@@ -48,6 +48,7 @@ export default defineConfig(configEnv => {
         'xgplayer'
       ]
     },
+    assetsInclude: ['**/*.xlsx', 'public/*'],
     build: {
       reportCompressedSize: false,
       sourcemap: false,