import { App } from '../../../types' import { Context } from 'koa' import * as request from 'request-promise' import template from '../../template' import * as Excel from 'exceljs' import DbClient from '../../client/db' import i18n from '../../../service/i18n' import * as XLSX from 'xlsx' import * as jschardet from 'jschardet' import * as iconv from 'iconv-lite' interface Header { type: string, value: string } export default abstract class Base { protected app: App protected ctx: Context protected headers: string[] protected example: string[] protected dbClient: DbClient protected NATIVE: string = 'native' protected abstract headerArray: Header[] constructor (app: App, ctx: Context) { this.app = app this.ctx = ctx this.dbClient = new DbClient(this.app, this.ctx) if (this.ctx.state.lang) { i18n.setLocale(this.ctx.state.lang) } } abstract buildHeaders (data?, type?): Promise | this abstract import (objArray, options): Promise buildExample (data?, type?): Promise | this { return this } protected translate (objectArray: Header[]) { return objectArray.map((item) => i18n.__(item.value)) } async downloadFile(url: string, fileType: string) { const maxSize: number = 5 * 1024 * 1024 if (!url || !fileType) { throw new Error('params missing') } let res = await request({ method: 'head', url: url, encoding: null, resolveWithFullResponse: true, // 文件有重定向,需开启 followRedirect: true, // gzip开启不会返回content-length gzip: false }) if (res.statusCode !== 200) { throw new Error(`wrong statusCode ${res.statusCode}`) } const contentLength: number = Number(res.headers['content-length']) if (!isFinite(contentLength) || maxSize < contentLength) { throw new Error('invalid downloadUrl') } res = await request({ method: 'get', url: url, encoding: null, resolveWithFullResponse: true, followRedirect: true }) if (res.statusCode !== 200) { throw new Error(`wrong statusCode ${res.statusCode}`) } return res.body } parse(data: Buffer, fileType: string): Object[] { const array: Header[] = this.headerArray const parsedData: Object[] = [] let workbook: XLSX.WorkBook if (fileType.toLowerCase() === 'csv') { const {encoding} = jschardet.detect(data) workbook = XLSX.read(iconv.decode(data, encoding), {type: 'string'}) } else { workbook = XLSX.read(data) } const worksheet: any = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { header: 1 }) const tmpContentDeleted = worksheet[0].length > 1 const titleIndex = tmpContentDeleted ? 0 : 1 // 标题行为第二行 if (worksheet[titleIndex].length !== array.length) { throw Error('ParamError: _scenariofieldId') } // 去除模板及标题行 worksheet.slice(titleIndex + 1).forEach((item: string[]) => { const result = {} item.forEach((element, i) => { result[array[i].value] = element }) parsedData.push(result) }) return parsedData } async buildWorkbook(TplValue: string) { const workbook: Excel.Workbook = new Excel.Workbook() const sheetName = TplValue === 'basictestcase' ? i18n.__('basictestcaseimport') : 'template' const sheet: Excel.Worksheet = workbook.addWorksheet(sheetName) const columnsArray: Object[] = [] let headerTpl: string = template[TplValue] if (this.ctx.state.lang === 'en') { headerTpl = template[TplValue + 'en'] } /* 文件解析中 依赖 填写规则内容 只有一项 如果对上述依赖要修改, 请同步 parser 解析中对 填写规则内容 的判断 */ for (let i = 0; i < this.headers.length; i++) { // 第一行默认是模板 if (i === 0) { columnsArray.push({ header: headerTpl, width: 15, style: { alignment: { vertical: 'top', wrapText: true } } }) } else { // 格式化每一列 style columnsArray.push({ width: 15, style: { alignment: { vertical: 'top', wrapText: true } } }) } } sheet.columns = columnsArray sheet.mergeCells('A1:Q1') sheet.addRow(this.headers) if (this.example != null) sheet.addRow(this.example) sheet.getRow(1).height = 260 sheet.getCell('A1').font = { size: 12, bold: true } return workbook } }