封装导出excel的服务
const Service = require('egg').Service;
const Excel = require('exceljs');
class exportExcel extends Service {
/**
* 数据并生成excel
* @param {Array} headers excel标题栏
* @param {Array} param 数据参数
* @param {string} name 文件名称
*/
async excelCommon(headers, data, name) {
let columns = [];// exceljs要求的columns
let hjRow = {};// 合计行
let titleRows = headers.length;// 标题栏行数
// 处理表头
headers.forEach(row=>{
row.forEach(col=>{
let { f, t ,w} = col;
if (f){
if (col.totalRow) hjRow[f] = true;
if (col.totalRowText) hjRow[f] = col.totalRowText;
col.style = { alignment: { vertical: 'middle', horizontal: 'center' } };
col.header = t; //文字
col.key = f; //对应的字段
col.width = w ? w : 15;//宽度
columns.push(col);
}
})
})
let workbook = new Excel.Workbook();
let sheet = workbook.addWorksheet('My Sheet', { views: [{ xSplit: 1, ySplit: 1 }] });
sheet.columns = columns;
sheet.addRows(data);
// 处理复杂表头
if (titleRows > 1) {
// 头部插入空行
for (let i = 1; i < titleRows; i++) sheet.spliceRows(1, 0, []);
headers.forEach(row=>{
row.forEach(col=>{
if (col.m1){
sheet.getCell(col.m1).value = col.t;
sheet.mergeCells(col.m1 + ":" + col.m2);
}
})
})
}
// 处理样式、日期、字典项
sheet.eachRow(function (row, rowNumber) {
// 设置行高
row.height = 25;
row.eachCell({ includeEmpty: true }, (cell)=> {
// 设置边框 黑色 细实线
const top = { style: 'thin', color: { argb: '000000' } };
const left = { style: 'thin', color: { argb: '000000' } };
const bottom = { style: 'thin', color: { argb: '000000' } };
const right = { style: 'thin', color: { argb: '000000' } };
cell.border = { top, left, bottom, right };
// 设置标题部分为粗体
if (rowNumber <= titleRows) { cell.font = { bold: true }; return; }
});
});
this.ctx.set('Content-Type', 'application/vnd.openxmlformats');
// 这个地方的空格不要更改
this.ctx.set('Content-Disposition', "attachment;filename*=UTF-8' '" + encodeURIComponent(name) + '.xlsx');
return await workbook.xlsx.writeBuffer()
}
}
module.exports = exportExcel;
controller
"use strict";
const BaseController = require("../base");
const {
ApiApplySdkVerificationToken,
ApiGetSdkVerificationResultRequest,
} = require("../../../utils/tencentcloud");
const {
applySdkVerificationTokeninputParamsConfig,
getSdkVerificationResultInputParamsConfig,
} = require("./config");
/**
* @Controller Ekyc_Management
*/
class Controller extends BaseController {
constructor(...arg) {
super(...arg);
this.modleName = "tencentcloud";
this.serviceName = "index";
}
/**
* @summary
* @description
* @router post /api/v1/ekyc/applysdkverificationtoken
* @Request body applyTokenRequest
* @response 200 baseResponse 创建成功(DTO)
* @apikey
*/
async apiApplySdkVerificationToken() {
const { ctx, service } = this;
let rule = {
needVerifyIdCard: [{ type: "boolean" }, { required: true }],
idCardType: [{ type: "string" }, { required: true }],
disableChangeOcrResult: [{ type: "boolean" }, { required: true }],
disableCheckOcrWarnings: [{ type: "boolean" }, { required: true }],
extra: [{ type: "string" }, { required: false }],
macAddr: [{ type: "string" }, { required: true }],
androidId: [{ type: "string" }, { required: true }],
appId: [{ type: "string" }, { required: true }],
ipAddr: [{ type: "string" }, { required: true }],
};
let data = ctx.request.body;
let validateResult = await this.ctx.validate(rule, data);
if (!validateResult) {
ctx.returnBody("Parameter error", 400);
return;
}
let orderNo = new Date().getTime();
data = { ...applySdkVerificationTokeninputParamsConfig, ...data };
let resp = await ApiApplySdkVerificationToken(data);
console.log("[Log] resp-->", resp);
await service[this.app.config.public][
this.modleName
].index.addVerificationTokenRequestLog({ ...data, orderNo: orderNo });
let responseTime = new Date().getTime() - orderNo;
await service[this.app.config.public][
this.modleName
].index.addVerificationTokenResponseLog({
...resp,
orderNo: orderNo,
responseTime: responseTime,
});
if (resp) {
ctx.returnBody(resp, 200);
} else {
ctx.returnBody(resp, 500);
}
}
/**
* @summary
* @description
* @router post /api/v1/ekyc/getsdkverificationresult
* @Request body getsdkresultRequest
* @response 200 baseResponse 创建成功(DTO)
* @apikey
*/
async apiGetSdkVerificationResult() {
const { ctx, service } = this;
let rule = {
sdkToken: [{ type: "string" }, { required: true }],
macAddr: [{ type: "string" }, { required: true }],
androidId: [{ type: "string" }, { required: true }],
appId: [{ type: "string" }, { required: true }],
ipAddr: [{ type: "string" }, { required: true }],
};
let data = ctx.request.body;
const validateResult = await ctx.validate(rule, data);
if (!validateResult) {
ctx.returnBody("Parameter error", 400);
return;
}
let orderNo = new Date().getTime();
data = { ...getSdkVerificationResultInputParamsConfig, ...data };
let resp = await ApiGetSdkVerificationResultRequest(data);
await service[this.app.config.public][
this.modleName
].index.addVerificationResultRequestLog({ ...data, orderNo: orderNo });
let responseTime = new Date().getTime() - orderNo;
await service[this.app.config.public][
this.modleName
].index.addVerificationResultResponseLog({
...resp,
orderNo: orderNo,
responseTime: responseTime,
});
// 查询为下面两个表进行关联
let resultCardCerify = await service[this.app.config.public][
this.modleName
].index.findVerificationResultResponseLog(resp);
// --------------------
await service[this.app.config.public][
this.modleName
].index.addCardVerifyResultLog({
...resp.CardVerifyResults,
sdkVerificationId: resultCardCerify.id,
});
await service[this.app.config.public][
this.modleName
].index.addCompareResultLog({
...resp.CompareResults,
sdkVerificationId: resultCardCerify.id,
});
// --------------------
if (resp) {
ctx.returnBody(resp, 200);
} else {
ctx.returnBody(resp, 500);
}
}
// 查询
async indexVerificationResultResponseLog() {
const { ctx, service } = this;
// 查询参数
const query = {
limit: ctx.helper.parseInt(ctx.request.body.pageSize),
offset: ctx.helper.parseInt(ctx.request.body.pageNum),
requestId: ctx.request.body.requestId ? ctx.request.body.requestId : null,
orderNo: ctx.request.body.orderNo ? ctx.request.body.orderNo : null,
result: ctx.request.body.result ? ctx.request.body.result : null,
userId: ctx.request.body.userId ? ctx.request.body.userId : null,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findListVerificationResultResponseLog(query);
ctx.returnBody(result, 200);
}
// 查询
async indexVerificationTokenResponseLog() {
const { ctx, service } = this;
// 查询参数
const query = {
limit: ctx.helper.parseInt(ctx.request.body.pageSize),
offset: ctx.helper.parseInt(ctx.request.body.pageNum),
orderNo: ctx.request.body.orderNo ? ctx.request.body.orderNo : null,
sdkToken: ctx.request.body.sdkToken ? ctx.request.body.sdkToken : null,
requestId: ctx.request.body.requestId ? ctx.request.body.requestId : null,
};
console.log("===================进来了==============");
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findListVerificationTokenResponseLog(query);
ctx.returnBody(result, 200);
}
// 查询
async indexVerificationResultRequestLog() {
const { ctx, service } = this;
// 查询参数
const query = {
limit: ctx.helper.parseInt(ctx.request.body.pageSize),
offset: ctx.helper.parseInt(ctx.request.body.pageNum),
orderNo: ctx.request.body.orderNo ? ctx.request.body.orderNo : null,
sdkToken: ctx.request.body.sdkToken ? ctx.request.body.sdkToken : null,
action: ctx.request.body.action ? ctx.request.body.action : null,
region: ctx.request.body.region ? ctx.request.body.region : null,
userId: ctx.request.body.userId ? ctx.request.body.userId : null,
appId: ctx.request.body.appId ? ctx.request.body.appId : null,
androidId: ctx.request.body.androidId ? ctx.request.body.androidId : null,
ipAddr: ctx.request.body.ipAddr ? ctx.request.body.ipAddr : null,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findListVerificationResultRequestLog(query);
ctx.returnBody(result, 200);
}
// 查询
async indexVerificationTokenRequestLog() {
const { ctx, service } = this;
// 查询参数
const query = {
limit: ctx.helper.parseInt(ctx.request.body.pageSize),
offset: ctx.helper.parseInt(ctx.request.body.pageNum),
orderNo: ctx.request.body.orderNo ? ctx.request.body.orderNo : null,
action: ctx.request.body.action ? ctx.request.body.action : null,
region: ctx.request.body.region ? ctx.request.body.region : null,
needVerifyIdCard: ctx.request.body.needVerifyIdCard
? ctx.request.body.needVerifyIdCard
: "",
idCardType: ctx.request.body.idCardType
? ctx.request.body.idCardType
: "",
appId: ctx.request.body.appId ? ctx.request.body.appId : null,
androidId: ctx.request.body.androidId ? ctx.request.body.androidId : null,
ipAddr: ctx.request.body.ipAddr ? ctx.request.body.ipAddr : null,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findListVerificationTokenRequestLog(query);
ctx.returnBody(result, 200);
}
// 查询
async indexUserLoginLog() {
const { ctx, service } = this;
// 查询参数
const query = {
limit: ctx.helper.parseInt(ctx.request.body.pageSize),
offset: ctx.helper.parseInt(ctx.request.body.pageNum),
userId: ctx.request.body.userId ? ctx.request.body.userId : null,
appId: ctx.request.body.appId ? ctx.request.body.appId : null,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findListIndexUserLoginLog(query);
ctx.returnBody(result, 200);
}
// 查询appid
async getAppidList() {
const { ctx, service } = this;
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findGetAppidList();
ctx.returnBody(result, 200);
}
/**
* @ summary
* @ description
* @ router get /api/v1/ekyc/getResultList
* @ response 200 baseResponse 创建成功(DTO)
* @ apikey
*/
async getResultList() {
const { ctx, service } = this;
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findGetResultList();
ctx.returnBody(result, 200);
}
/**
* @summary
* @description
* @router get /api/v1/ekyc/getCardVerifyResultLog
* @request query string requestId
* @response 200 baseResponse 创建成功(DTO)
* @apikey
*/
async getCardVerifyResultLog() {
const { ctx, service } = this;
const query = {
requestId: ctx.query.requestId,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findGetCardVerifyResultLog(query);
ctx.returnBody(result, 200);
}
/**
* @summary
* @description
* @router get /api/v1/ekyc/getCompareResultLog
* @request query string requestId
* @response 200 baseResponse 创建成功(DTO)
* @apikey
*/
async getCompareResultLog() {
const { ctx, service } = this;
const query = {
requestId: ctx.query.requestId,
};
const result = await service[this.app.config.public][this.modleName][
this.serviceName
].findGetCompareResultLog(query);
ctx.returnBody(result, 200);
}
/**
* @summary
* @description
* @router get /api/v1/ekyc/userLoginDownExcel
* @response 200 baseResponse 创建成功(DTO)
* @apikey
*/
async userLoginDownExcel() {
const { ctx, service } = this;
const params = ctx.query;
//两层数组,一层是行,一层是列,允许多行
let headers = [
[
{ t: "id", f: "id", totalRow: true },
{ t: "userId", f: "userId", totalRow: true },
{ t: "loginTime", f: "loginTime", totalRow: true },
{ t: "appId", f: "appId", totalRow: true },
],
];
// 注:f的字段名需要跟数据库的字段一致
let data = await this.app.model.UserLoginLog.findAll({
where: {},
raw: true,
});
console.log("[Log] data-->", data);
let res = await service[this.app.config.public].exportExcel.excelCommon(
headers,
data,
"userLogin"
);
console.log("res--------: ", res);
ctx.body = res;
}
async applySdkVerifTokenRequestExcel() {
const { ctx, service } = this;
const params = ctx.query;
//两层数组,一层是行,一层是列,允许多行
let headers = [
[
{ t: "id", f: "id", totalRow },
{ t: "action", f: "action", totalRow },
{ t: "version", f: "version", totalRow },
{ t: "region", f: "region", totalRow },
{ t: "needVerifyIdCard", f: "needVerifyIdCard", totalRow },
{ t: "idCardType", f: "idCardType", totalRow },
{ t: "createTime", f: "createTime", totalRow },
{ t: "orderNo", f: "orderNo", totalRow },
{ t: "macAddr", f: "macAddr", totalRow },
{ t: "androidId", f: "androidId", totalRow },
{ t: "appId", f: "appId", totalRow },
{ t: "ipAddr", f: "ipAddr", totalRow },
{ t: "userId", f: "userId", totalRow },
],
];
// 注:f的字段名需要跟数据库的字段一致
let data = await this.app.model.VerificationTokenRequestLog.findAll({
where: {},
raw: true,
});
console.log("[Log] data-->", data);
let res = await service[this.app.config.public].exportExcel.excelCommon(
headers,
data,
"VerificationTokenRequestLog"
);
console.log("res--------: ", res);
ctx.body = res;
}
async applySdkVerifTokenResponseExcel() {
const { ctx, service } = this;
const params = ctx.query;
//两层数组,一层是行,一层是列,允许多行
let headers = [
[
{ t: "id", f: "id", totalRow: true },
{ t: "sdkToken", f: "sdkToken", totalRow: true },
{ t: "requestId", f: "requestId", totalRow: true },
{ t: "createTime", f: "createTime", totalRow: true },
{ t: "orderNo", f: "orderNo", totalRow: true },
{ t: "responseTime", f: "responseTime", totalRow: true },
{ t: "userId", f: "userId", totalRow: true },
],
];
// 注:f的字段名需要跟数据库的字段一致
let data = await this.app.model.VerificationTokenResponseLog.findAll({
where: {},
raw: true,
});
console.log("[Log] data-->", data);
let res = await service[this.app.config.public].exportExcel.excelCommon(
headers,
data,
"VerificationTokenRequestLog"
);
console.log("res--------: ", res);
ctx.body = res;
}
async getSdkVerifResultRequestExcel() {
const { ctx, service } = this;
const params = ctx.query;
//两层数组,一层是行,一层是列,允许多行
let headers = [
[
{ t: "id", f: "id", totalRow: true },
{ t: "action", f: "action", totalRow: true },
{ t: "version", f: "version", totalRow: true },
{ t: "region", f: "region", totalRow: true },
{ t: "sdkToken", f: "sdkToken", totalRow: true },
{ t: "userId", f: "userId", totalRow: true },
{ t: "createTime", f: "createTime", totalRow: true },
{ t: "orderNo", f: "orderNo", totalRow: true },
{ t: "macAddr", f: "macAddr", totalRow: true },
{ t: "androidId", f: "androidId", totalRow: true },
{ t: "appId", f: "appId", totalRow: true },
{ t: "ipAddr", f: "ipAddr", totalRow: true },
],
];
// 注:f的字段名需要跟数据库的字段一致
let data = await this.app.model.VerificationResultRequestLog.findAll({
where: {},
raw: true,
});
console.log("[Log] data-->", data);
let res = await service[this.app.config.public].exportExcel.excelCommon(
headers,
data,
"VerificationTokenRequestLog"
);
console.log("res--------: ", res);
ctx.body = res;
}
async getSdkVerifResultResponseExcel() {
const { ctx, service } = this;
const params = ctx.query;
//两层数组,一层是行,一层是列,允许多行
let headers = [
[
{ t: "id", f: "id", totalRow: true },
{ t: "result", f: "result", totalRow: true },
{ t: "description", f: "description", totalRow: true },
{ t: "chargeCount", f: "chargeCount", totalRow: true },
{ t: "extra", f: "extra", totalRow: true },
{ t: "requestId", f: "requestId", totalRow: true },
{ t: "userId", f: "userId", totalRow: true },
{ t: "createTime", f: "createTime", totalRow: true },
{ t: "orderNo", f: "orderNo", totalRow: true },
{ t: "responseTime", f: "responseTime", totalRow: true },
],
];
// 注:f的字段名需要跟数据库的字段一致
let data = await this.app.model.VerificationResultResponseLog.findAll({
where: {},
raw: true,
});
console.log("[Log] data-->", data);
let res = await service[this.app.config.public].exportExcel.excelCommon(
headers,
data,
"VerificationTokenRequestLog"
);
console.log("res--------: ", res);
ctx.body = res;
}
}
module.exports = Controller;
router
router.get(
`/api/${app.config.public}/ekyc/userLoginDownExcel`,
controller[app.config.public].tencentcloud.index.userLoginDownExcel
);
router.get(
`/api/${app.config.public}/ekyc/applySdkVerifTokenRequestExcel`,
controller[app.config.public].tencentcloud.index
.applySdkVerifTokenRequestExcel
);
router.get(
`/api/${app.config.public}/ekyc/applySdkVerifTokenResponseExcel`,
controller[app.config.public].tencentcloud.index
.applySdkVerifTokenResponseExcel
);
router.get(
`/api/${app.config.public}/ekyc/getSdkVerifResultRequestExcel`,
controller[app.config.public].tencentcloud.index
.getSdkVerifResultRequestExcel
);
router.get(
`/api/${app.config.public}/ekyc/getSdkVerifResultResponseExcel`,
controller[app.config.public].tencentcloud.index
.getSdkVerifResultResponseExcel
);
使用
重点用downFile, 注意config配置: responseType: ‘blob’
request.js
import axios from "axios";
import store from "@/store";
import { Message, MessageBox } from "element-ui";
import { getToken } from "@/utils/auth";
axios.defaults.withCredentials = true; // 跨域访问需要发送cookie时一定要加这句
let httpTime = 0;
// 添加请求拦截器,在发送请求之前做些什么
axios.interceptors.request.use(
function(config) {
httpTime++;
return config;
},
function(error) {
return Promise.reject(error);
}
);
// 添加响应拦截器
axios.interceptors.response.use(
function(response) {
// 对响应数据做点什么,允许在数据返回客户端前,修改响应的数据
return response.data;
},
function(error) {
return Promise.reject(error);
}
);
// 封装数据返回失败提示函数
function errorState(error) {
let offset = 1;
let message = "";
switch (error.response.status) {
case 400:
for (const key in error.response.data.error) {
message = error.response.data.error[key].message || "参数错误";
Message({
type: "error",
offset: 40 * offset,
message
});
offset++;
}
break;
case 401:
message = "token失效";
MessageBox.confirm(
"The login status has expired. You can continue to stay on this page or log in again",
"System prompt",
{
confirmButtonText: "Log in again",
cancelButtonText: "Cancel",
type: "warning"
}
).then(() => {
store.dispatch("LogOut").then(() => {
location.reload(); // 为了重新实例化vue-router对象 避免bug
});
});
break;
case 403:
message = "Access denied";
Message({
type: "error",
message
});
break;
case 500:
message = error.response.data.message || "Server Exception";
Message({
type: "error",
message
});
break;
default:
Message({
type: "error",
message: "Server Exception"
});
message = "Server Exception";
}
}
// 封装axios
function HttpRequest(
url,
method = "GET",
params = {},
isLoading = true,
responseType = null
) {
method = method.toUpperCase();
if (isLoading && method === "GET" && !store.state.app.loading) {
store.state.app.loading = true;
}
// 设置token
const token = getToken();
const httpDefault = {
baseURL: process.env.VUE_APP_BASE_API,
method,
url,
headers: {
withCredentials: true,
// token: token,
Authorization: token,
vision: "1.0.0",
operating_system: "Web",
"Content-Type": "application/json; charset=utf-8"
},
params: method === "GET" || method === "DELETE" ? params : null,
data: method === "POST" || method === "PUT" ? params : null,
timeout: 10000
};
if (responseType) {
httpDefault.responseType = responseType;
}
return new Promise((resolve, reject) => {
axios(httpDefault)
.then(response => {
resolve(response);
})
.catch(response => {
errorState(response);
reject(response);
})
.then(function() {
// always executed
httpTime--;
// console.log(httpTime, url)
if (!httpTime) {
store.state.app.loading = false;
}
});
});
}
export default HttpRequest;
API
export function exportExcel() {
let responseType = "blob";
return HttpRequest(
"/ekyc/userLoginDownExcel",
"get",
{},
false,
responseType
);
}
vue
<template>
<div class="app-container">
<el-button type="primary" @click="exportfnjinjie">导出-简单版</el-button>
<div class="head">
<div class="select">
<span>APPID:</span>
<el-select
v-model="form.value"
@change="selectModel1($event)"
placeholder="Please select appid"
>
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</div>
<div class="block">
<!-- <span class="demonstration">默认</span> -->
<el-date-picker
v-model="form.value2"
@change="selectModel2($event)"
type="datetimerange"
range-separator="to"
start-placeholder="StartTime"
end-placeholder="EndTime"
>
</el-date-picker>
</div>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
style="margin-left: 50px"
@click="handleQuery"
>Search</el-button
>
</div>
<!-- <a class="toBlog" :href="`${origin}/index.html#/front/technique/${id}/all`" target="_blank">我的博客</a> -->
<div class="home">
<div class="echarts">
<div class="count">Count</div>
<span class="title">TokenChart</span>
<div id="echart" class="detail"></div>
<div class="id">User Id</div>
</div>
<div class="echarts">
<div class="count">Count</div>
<span class="title">LivenessChart</span>
<div id="echart1" class="detail"></div>
<div class="id">User Id</div>
</div>
<div class="echarts">
<div class="count">Count</div>
<span class="title">FaceMatchChart</span>
<div id="echart2" class="detail"></div>
<div class="id">User Id</div>
</div>
<div class="echarts">
<div class="count">Count</div>
<span class="title">OcrResultChart</span>
<div id="echart3" class="detail"></div>
<div class="id">User Id</div>
</div>
</div>
</div>
</template>
<script>
import * as echarts from "echarts";
import {
getindexApiTokenChart,
getindexApiFaceMatchChart,
getindexApiOcrResultChart,
getindexApiLivenessChart,
getAppidList,
exportExcel
} from "@/api/admin/system/service";
export default {
data() {
return {
value: true,
origin: "",
id: "",
TokenData: "",
options: [],
LivenessChart: "",
FaceMatchChart: "",
OcrResultChart: "",
form: {
value: "",
startTime: "",
endTime: ""
}
};
},
mounted() {
this.origin = window.location.origin;
this.id = window.btoa(this.$store.state.user.userInfo.user.id);
let data = {
appId: "",
startTime: "",
endTime: ""
};
// getindexApiTokenChart(data).then((res) => {
// this.TokenData = res.data;
// this.getChart();
// });
getAppidList().then(res => {
console.log("==============");
this.options = res.data;
this.form.value = res.data[0];
this.handleQuery();
});
this.getChart();
this.getChart1();
this.getChart2();
this.getChart3();
},
methods: {
exportfnjinjie() {
exportExcel().then(res => {
console.log("[Log] exportExcel-->", res);
downFile(res, "file.xlsx");
});
// 可以将此方法封装
function downFile(content, filename) {
// 创建隐藏的可下载链接
var eleLink = document.createElement("a");
eleLink.download = filename;
eleLink.style.display = "none";
// 字符内容转变成blob地址
var blob = new Blob([content]);
console.log("[Log] blob-->", blob);
eleLink.href = URL.createObjectURL(blob);
document.body.appendChild(eleLink);
// 触发点击
eleLink.click();
// 然后移除
document.body.removeChild(eleLink);
}
}
}
};
</script>
<style scoped lang="scss">
.search {
margin-left: 50px;
}
.head {
margin-left: 50px;
display: flex;
.block {
margin-left: 50px;
}
}
.home {
margin-top: 50px;
width: 100%;
display: flex;
justify-content: space-around;
.toBlog {
color: teal;
}
.echarts {
width: 24%;
height: 400px;
// box-shadow: 2px 2px 2px 2px rgb(189, 188, 188);
border: 2px solid #e0e6f1;
border-radius: 10px;
display: flex;
justify-content: center;
position: relative;
.detail {
min-width: 300px;
margin-top: 50px;
width: 90%;
height: 90%;
// background-color: blue;
}
.count {
transform: rotate(-90deg);
left: -10px;
top: 180px;
position: absolute;
font-size: 18px;
// font-weight: bold;
}
.title {
position: absolute;
display: inline-block;
left: 10px;
height: 50px;
line-height: 50px;
width: 50px;
// background-color: red;
font-size: 20px;
font-weight: bold;
}
.id {
position: absolute;
bottom: 35px;
font-size: 18px;
}
}
}
</style>