Selaa lähdekoodia

修改页面问题

zs 8 kuukautta sitten
vanhempi
commit
b37eae50c0

+ 148 - 0
package-lock.json

@@ -20,6 +20,7 @@
         "@midwayjs/redis": "^3.16.0",
         "@midwayjs/swagger": "^3.16.1",
         "@midwayjs/typeorm": "^3.16.0",
+        "@midwayjs/upload": "^3.16.8",
         "@midwayjs/validate": "^3.12.0",
         "bcryptjs": "^2.4.3",
         "crypto-js": "^4.2.0",
@@ -1491,6 +1492,18 @@
         "node": ">=12"
       }
     },
+    "node_modules/@midwayjs/upload": {
+      "version": "3.16.8",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/upload/-/upload-3.16.8.tgz",
+      "integrity": "sha512-OTxUg/QI40yyTKzxLt5dtlvryAN+aszVpbm7GbPt4fOPONEf2QG0zXXn697Yi7/kLKuZpbKlj7f+L6JMcuSesQ==",
+      "dependencies": {
+        "file-type": "16.5.4",
+        "raw-body": "2.5.2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/@midwayjs/validate": {
       "version": "3.16.1",
       "resolved": "https://registry.npmmirror.com/@midwayjs/validate/-/validate-3.16.1.tgz",
@@ -1615,6 +1628,11 @@
         "node": ">=6"
       }
     },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+    },
     "node_modules/@types/accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmmirror.com/@types/accepts/-/accepts-1.3.7.tgz",
@@ -4404,6 +4422,22 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-type": {
+      "version": "16.5.4",
+      "resolved": "https://registry.npmmirror.com/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "dependencies": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+      }
+    },
     "node_modules/fill-range": {
       "version": "7.1.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
@@ -7335,6 +7369,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/pg": {
       "version": "8.11.5",
       "resolved": "https://registry.npmmirror.com/pg/-/pg-8.11.5.tgz",
@@ -7843,6 +7889,21 @@
         "node": ">= 6"
       }
     },
+    "node_modules/readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "dependencies": {
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/readdir-glob": {
       "version": "1.1.3",
       "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz",
@@ -8514,6 +8575,22 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/superagent": {
       "version": "8.1.2",
       "resolved": "https://registry.npmmirror.com/superagent/-/superagent-8.1.2.tgz",
@@ -8744,6 +8821,22 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/traverse": {
       "version": "0.3.9",
       "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz",
@@ -10644,6 +10737,15 @@
       "resolved": "https://registry.npmmirror.com/@midwayjs/typeorm/-/typeorm-3.16.0.tgz",
       "integrity": "sha512-EpBPuco5cBBAdgcLkAGQZGfppaK54Rc48URo5N0gw5PY024GwPOVd517S7PXTiY4v2ZVdlTT4nqnJPqwJmVCcQ=="
     },
+    "@midwayjs/upload": {
+      "version": "3.16.8",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/upload/-/upload-3.16.8.tgz",
+      "integrity": "sha512-OTxUg/QI40yyTKzxLt5dtlvryAN+aszVpbm7GbPt4fOPONEf2QG0zXXn697Yi7/kLKuZpbKlj7f+L6JMcuSesQ==",
+      "requires": {
+        "file-type": "16.5.4",
+        "raw-body": "2.5.2"
+      }
+    },
     "@midwayjs/validate": {
       "version": "3.16.1",
       "resolved": "https://registry.npmmirror.com/@midwayjs/validate/-/validate-3.16.1.tgz",
@@ -10747,6 +10849,11 @@
         "defer-to-connect": "^1.0.1"
       }
     },
+    "@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+    },
     "@types/accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmmirror.com/@types/accepts/-/accepts-1.3.7.tgz",
@@ -12845,6 +12952,16 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-type": {
+      "version": "16.5.4",
+      "resolved": "https://registry.npmmirror.com/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "requires": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      }
+    },
     "fill-range": {
       "version": "7.1.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
@@ -15078,6 +15195,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="
+    },
     "pg": {
       "version": "8.11.5",
       "resolved": "https://registry.npmmirror.com/pg/-/pg-8.11.5.tgz",
@@ -15441,6 +15563,14 @@
         "util-deprecate": "^1.0.1"
       }
     },
+    "readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "requires": {
+        "readable-stream": "^3.6.0"
+      }
+    },
     "readdir-glob": {
       "version": "1.1.3",
       "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz",
@@ -15934,6 +16064,15 @@
       "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
       "dev": true
     },
+    "strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      }
+    },
     "superagent": {
       "version": "8.1.2",
       "resolved": "https://registry.npmmirror.com/superagent/-/superagent-8.1.2.tgz",
@@ -16115,6 +16254,15 @@
       "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
       "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
     },
+    "token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      }
+    },
     "traverse": {
       "version": "0.3.9",
       "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz",

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "@midwayjs/redis": "^3.16.0",
     "@midwayjs/swagger": "^3.16.1",
     "@midwayjs/typeorm": "^3.16.0",
+    "@midwayjs/upload": "^3.16.8",
     "@midwayjs/validate": "^3.12.0",
     "bcryptjs": "^2.4.3",
     "crypto-js": "^4.2.0",

+ 6 - 3
src/config/config.default.ts

@@ -6,8 +6,11 @@ export default {
   koa: {
     port: 7001,
   },
-  qichacha:{
+  upload: {
+    realdir: 'upload',
+  },
+  qichacha: {
     key: 'd52e7d621b974b06a3f8a90645223c47',
-    secretKey:'230A5D226DFBD80F3BE73E42971EF837'
-  }
+    secretKey: '230A5D226DFBD80F3BE73E42971EF837',
+  },
 } as MidwayConfig;

+ 2 - 0
src/configuration.ts

@@ -13,6 +13,7 @@ import { DefaultErrorFilter } from './filter/defaultError.filter';
 import { CheckTokenMiddleware } from './middleware/checkToken.middleware';
 import { ResponseMiddleware } from './middleware/response.middleware';
 import * as axios from '@midwayjs/axios';
+import * as upload from '@midwayjs/upload';
 @Configuration({
   imports: [
     koa,
@@ -21,6 +22,7 @@ import * as axios from '@midwayjs/axios';
     redis,
     orm,
     axios,
+    upload,
     {
       component: info,
       enabledEnvironment: ['local'],

+ 67 - 0
src/controller/platform/matchPath.controller.ts

@@ -0,0 +1,67 @@
+import { MatchPathService } from '../../service/platform/matchPath.service';
+import { CVO_matchPath, FVO_matchPath, QVO_matchPath, UVAO_matchPath } from '../../interface/platform/matchPath.interface';
+import { ApiResponse, ApiTags, ApiQuery } from '@midwayjs/swagger';
+import { Validate } from '@midwayjs/validate';
+import { Controller, Inject, Get, Param, Post, Body, Del, Query } from '@midwayjs/core';
+import { omit, pick } from 'lodash';
+import { ServiceError, ErrorCode } from '../../error/service.error';
+import { BaseController } from '../../frame/BaseController';
+import { ServiceUtilService } from '../../service/serviceUtil.service';
+const namePrefix = '赛事流程';
+@ApiTags(['赛事流程'])
+@Controller('/matchPath', { tagName: namePrefix })
+export class MatchPathController implements BaseController {
+  @Inject()
+  service: MatchPathService;
+  @Inject()
+  serviceUtil: ServiceUtilService;
+  @Get('/')
+  @ApiTags('列表查询')
+  @ApiQuery({ name: 'query' })
+  @ApiResponse({ type: QVO_matchPath })
+  async index(@Query() query: object) {
+    const qobj = omit(query, ['skip', 'limit']);
+    const others: any = pick(query, ['skip', 'limit']);
+    others.order = { order_num: 'ASC' };
+    const result = await this.service.query(qobj, others);
+    return result;
+  }
+
+  @Get('/:id')
+  @ApiTags('单查询')
+  @ApiResponse({ type: FVO_matchPath })
+  async fetch(@Param('id') id: number) {
+    const data = await this.service.fetch({ id });
+    const result = new FVO_matchPath(data);
+    return result;
+  }
+
+  @Post('/', { routerName: `创建${namePrefix}` })
+  @ApiTags('创建数据')
+  @Validate()
+  @ApiResponse({ type: CVO_matchPath })
+  async create(@Body() data: object) {
+    const dbData = await this.service.create(data);
+    const result = new CVO_matchPath(dbData);
+    return result;
+  }
+
+  @Post('/:id', { routerName: `修改${namePrefix}` })
+  @ApiTags('修改数据')
+  @Validate()
+  @ApiResponse({ type: UVAO_matchPath })
+  async update(@Param('id') id: number, @Body() data: object) {
+    if (!id) throw new ServiceError(ErrorCode.ID_NOT_FOUND);
+    const result = await this.service.update({ id }, data);
+    return result;
+  }
+
+  @Del('/:id', { routerName: `删除${namePrefix}` })
+  @ApiTags('删除数据')
+  @Validate()
+  async delete(@Param('id') id: number) {
+    if (!id) throw new ServiceError(ErrorCode.ID_NOT_FOUND);
+    const result = await this.service.delete({ id });
+    return result;
+  }
+}

+ 9 - 4
src/controller/util.controller.ts

@@ -1,4 +1,4 @@
-import { Controller, Post, Inject } from '@midwayjs/core';
+import { Controller, Body, Fields, Files, Post, Inject } from '@midwayjs/core';
 import { Context } from '@midwayjs/koa';
 import { ApiTags } from '@midwayjs/swagger';
 import { UtilService } from '../service/util.service';
@@ -12,11 +12,16 @@ export class UtilController {
   @Inject()
   ctx: Context;
 
+  @Post('/toExport')
+  async toExport(@Body() data: object) {
+    const result = await this.service.toExport(data);
+    return result;
+  }
+
   @Post('/toImport')
-  async toImport() {
-    const stream = await this.ctx.getFileStream();
+  async upload(@Files() files, @Fields() fields) {
     const workbook = new Excel.Workbook();
-    await workbook.xlsx.read(stream);
+    await workbook.xlsx.read(files);
     const result = [];
     const sheetList = [];
     workbook.eachSheet(sheet => {

+ 20 - 0
src/entity/platform/matchPath.entity.ts

@@ -0,0 +1,20 @@
+import { Entity, Column } from 'typeorm';
+import { BaseModel } from '../../frame/BaseModel';
+//赛事流程
+@Entity('matchPath')
+export class MatchPath extends BaseModel {
+  @Column({ type: 'integer', nullable: true, comment: '赛事id' })
+  match: number;
+  @Column({ type: 'character varying', nullable: true, comment: '流程名称' })
+  name: string;
+  @Column({ type: 'jsonb', nullable: true, comment: '报名人员', default: {} })
+  signPerson: object;
+  @Column({ type: 'character varying', nullable: true, comment: '时间' })
+  time: string;
+  @Column({ type: 'integer', nullable: true, comment: '显示顺序' })
+  order_num: number;
+  @Column({ type: 'text', nullable: true, comment: '简介' })
+  brief: string;
+  @Column({ type: 'character varying', nullable: true, comment: '是否启用', default: '0' })
+  is_use: string;
+}

+ 89 - 0
src/interface/platform/matchPath.interface.ts

@@ -0,0 +1,89 @@
+import { Rule, RuleType } from '@midwayjs/validate';
+import { ApiProperty } from '@midwayjs/swagger';
+import { SearchBase } from '../../frame/SearchBase';
+import { dealVO } from '../../frame/VOBase';
+export class FVO_matchPath {
+  constructor(data: object) {
+    dealVO(this, data);
+  }
+  @ApiProperty({ description: '数据id' })
+  id: number = undefined;
+  @ApiProperty({ description: '赛事id' })
+  'match': number = undefined;
+  @ApiProperty({ description: '名称' })
+  'name': string = undefined;
+  @ApiProperty({ description: '时间' })
+  'time': string = undefined;
+  @ApiProperty({ description: '报名人员' })
+  'signPerson': object = undefined;
+  @ApiProperty({ description: '显示顺序' })
+  'order_num': number = undefined;
+  @ApiProperty({ description: '简介' })
+  'brief': string = undefined;
+  @ApiProperty({ description: '是否公开' })
+  'is_use': string = undefined;
+}
+
+export class QDTO_matchPath extends SearchBase {
+  @ApiProperty({ description: '赛事id' })
+  'match': number = undefined;
+  @ApiProperty({ description: '名称' })
+  'name': string = undefined;
+  @ApiProperty({ description: '时间' })
+  'time': string = undefined;
+  @ApiProperty({ description: '显示顺序' })
+  'order_num': number = undefined;
+  @ApiProperty({ description: '是否公开' })
+  'is_use': string = undefined;
+}
+
+export class QVO_matchPath extends FVO_matchPath {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class CDTO_matchPath {
+  @ApiProperty({ description: '赛事id' })
+  @Rule(RuleType['number']().empty(''))
+  'match': number = undefined;
+  @ApiProperty({ description: '名称' })
+  @Rule(RuleType['string']().empty(''))
+  'name': string = undefined;
+  @ApiProperty({ description: '时间' })
+  @Rule(RuleType['string']().empty(''))
+  'time': string = undefined;
+  @ApiProperty({ description: '报名人员' })
+  @Rule(RuleType['object']().empty(''))
+  'signPerson': object = undefined;
+  @ApiProperty({ description: '是否公开' })
+  @Rule(RuleType['string']().empty(''))
+  'is_use': string = undefined;
+  @ApiProperty({ description: '简介' })
+  @Rule(RuleType['string']().empty(''))
+  'brief': string = undefined;
+  @ApiProperty({ description: '显示顺序' })
+  @Rule(RuleType['number']().empty(''))
+  'order_num': number = undefined;
+}
+
+export class CVO_matchPath extends FVO_matchPath {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}
+
+export class UDTO_matchPath extends CDTO_matchPath {
+  @ApiProperty({ description: '数据id' })
+  @Rule(RuleType['number']().empty(''))
+  id: number = undefined;
+}
+
+export class UVAO_matchPath extends FVO_matchPath {
+  constructor(data: object) {
+    super(data);
+    dealVO(this, data);
+  }
+}

+ 14 - 0
src/service/platform/matchPath.service.ts

@@ -0,0 +1,14 @@
+import { MatchPath } from '../../entity/platform/matchPath.entity';
+import { Provide } from '@midwayjs/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { BaseServiceV2 } from '../../frame/BaseServiceV2';
+@Provide()
+export class MatchPathService extends BaseServiceV2 {
+  @InjectEntityModel(MatchPath)
+  model: Repository<MatchPath>;
+  getQueryColumnsOpera() {
+    const obj = {};
+    return obj;
+  }
+}

+ 2 - 2
src/service/platform/sign.service.ts

@@ -16,8 +16,8 @@ export class SignService extends BaseServiceV2 {
 
   // 报名检查
   async createExamine(data) {
-    const { user, match } = data;
-    const result = await this.model.findOne({ where: { user: Equal(user), match: Equal(match) } });
+    const { match, phone, card } = data;
+    const result = await this.model.findOne({ where: { phone: Equal(phone), card: Equal(card), match: Equal(match) } });
     if (result) throw new ServiceError(ErrorCode.SERVICE_REPEAT);
   }
 

+ 82 - 1
src/service/util.service.ts

@@ -1,4 +1,4 @@
-import { Inject, Provide } from '@midwayjs/core';
+import { Inject, Provide, Config } from '@midwayjs/core';
 import { get } from 'lodash';
 import { Context } from '@midwayjs/koa';
 import { InjectEntityModel } from '@midwayjs/typeorm';
@@ -11,7 +11,11 @@ import { Company } from '../entity/users/company.entity';
 import { Supply } from '../entity/platform/supply.entity';
 import { Project } from '../entity/platform/project.entity';
 import { Demand } from '../entity/platform/demand.entity';
+import { UserService } from './system/user.service';
 const mappings = require('../../public/importMapping');
+import * as Excel from 'exceljs';
+import * as Path from 'path';
+import * as fs from 'fs';
 /**
  * 工具类服务,为其他地方提供一些通用的函数
  */
@@ -19,6 +23,9 @@ const mappings = require('../../public/importMapping');
 export class UtilService {
   @Inject()
   ctx: Context;
+  @Inject()
+  userService: UserService;
+
   @InjectEntityModel(Achievement)
   achievementModel: Repository<Achievement>;
   @InjectEntityModel(Project)
@@ -32,6 +39,80 @@ export class UtilService {
   @Inject()
   dictDataService: DictDataService;
 
+  @Config('upload.realdir')
+  uploadDir;
+
+  // 导出
+  async toExport(query) {
+    const { table, config, user } = query;
+    const nowDate = new Date().getTime();
+    const filename = `产学研用导出-${table}-${nowDate}.xlsx`;
+    const path = 'D:\\temp\\cxyy\\';
+    if (!path) {
+      throw new ServiceError('服务端没有设置存储路径');
+    }
+    if (!fs.existsSync(path)) {
+      // 如果不存在文件夹,就创建
+      this.mkdir(path);
+    }
+    try {
+      let info = {};
+      if (user) info = { where: { user: Equal(user) } };
+      const targetModel = this[`${table}Model`];
+      const data = await targetModel.find(info);
+      const workbook = new Excel.Workbook();
+      const sheet = workbook.addWorksheet('sheet1');
+      // 根据前端传回来的设置和顺序,将表头先塞进去
+      const meta = config.map(i => i.label);
+      sheet.addRow(meta);
+      let arr = [];
+      for (const d of data) {
+        arr = [];
+        for (const c of config) {
+          const { mark, model, code } = c;
+          if (mark === 'tags') {
+            if (d[model]) d[model] = arr.push(d[model].join(','));
+            else arr.push(d[model]);
+          } else if (mark === 'area') {
+            if (d[model]) d[model] = arr.push(d[model].join('-'));
+            else arr.push(d[model]);
+          } else if (mark === 'dict') {
+            const req = await this.dictDataService.query({ code, is_use: '0' }, {});
+            if (req.data) {
+              const selected = req.data.find(f => f.value === d[model]);
+              if (selected) {
+                d[model] = get(selected, 'label');
+                arr.push(d[model]);
+              } else arr.push(d[model]);
+            } else arr.push(d[model]);
+          } else if (mark === 'time') {
+            if (d.start_time && d.end_time) {
+              arr.push((d[model] = `${d.start_time}-${d.end_time}`));
+            } else arr.push(d[model]);
+          } else arr.push(d[model]);
+        }
+        sheet.addRow(arr);
+      }
+      // 生成excel
+      const filepath = `${path}${filename}`;
+      if (data.length <= 0) return;
+      await workbook.xlsx.writeFile(filepath);
+      return `/files/cxyy/export/${filename}`;
+    } catch (error) {
+      console.log(error);
+    }
+  }
+  // 创建文件夹
+  mkdir(dirname) {
+    if (fs.existsSync(dirname)) {
+      return true;
+    }
+    if (this.mkdir(Path.dirname(dirname))) {
+      fs.mkdirSync(dirname);
+      return true;
+    }
+  }
+
   randomStr(len = 6) {
     return Math.random().toString(36).slice(-len);
   }