精简代码,删除几个非必须依赖xlsx、fullcalendar、lodash.get、lodash.pick等

pull/170/head
zhangdaiscott 2022-06-22 15:36:52 +08:00
parent 5aec20bfa0
commit 9f83a9ea29
15 changed files with 5 additions and 820 deletions

View File

@ -36,12 +36,6 @@
"dependencies": {
"@jeecg/online": "1.0.1",
"@iconify/iconify": "^2.0.4",
"@fullcalendar/core": "5.10.1",
"@fullcalendar/daygrid": "5.10.1",
"@fullcalendar/interaction": "5.10.1",
"@fullcalendar/resource-timeline": "5.10.1",
"@fullcalendar/timegrid": "5.10.1",
"@fullcalendar/vue3": "5.10.1",
"@vueuse/core": "^6.6.2",
"@zxcvbn-ts/core": "^1.0.0-beta.0",
"ant-design-vue": "2.2.8",
@ -59,8 +53,6 @@
"intro.js": "^4.2.2",
"js-cookie": "^2.2.1",
"lodash-es": "^4.17.21",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.3.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
@ -87,7 +79,6 @@
"vxe-table": "4.1.0",
"vxe-table-plugin-antd": "^3.0.3",
"xe-utils": "^3.3.1",
"xlsx": "^0.17.3",
"vue-json-pretty": "^2.0.4"
},
"devDependencies": {
@ -188,11 +179,6 @@
"include": [
"@ant-design/colors",
"@ant-design/icons-vue",
"@fullcalendar/core/vdom",
"@fullcalendar/daygrid",
"@fullcalendar/interaction",
"@fullcalendar/timegrid",
"@fullcalendar/vue3",
"@jeecg/online",
"@vueuse/core",
"@vueuse/shared",
@ -211,7 +197,10 @@
"codemirror/addon/hint/show-hint.js",
"codemirror/addon/selection/active-line.js",
"codemirror/mode/clike/clike.js",
"codemirror/mode/css/css",
"codemirror/mode/css/css.js",
"codemirror/mode/htmlmixed/htmlmixed",
"codemirror/mode/javascript/javascript",
"codemirror/mode/javascript/javascript.js",
"codemirror/mode/markdown/markdown.js",
"codemirror/mode/python/python.js",
@ -289,14 +278,14 @@
"vite-plugin-theme/es/colorUtils",
"vue",
"vue-i18n",
"vue-json-pretty",
"vue-print-nb-jeecg/src/printarea",
"vue-router",
"vue-types",
"vuedraggable",
"vxe-table",
"vxe-table-plugin-antd",
"xe-utils",
"xlsx"
"xe-utils"
]
}
}

View File

@ -1,8 +0,0 @@
import { withInstall } from '/@/utils';
import impExcel from './src/ImportExcel.vue';
import expExcelModal from './src/ExportExcelModal.vue';
export const ImpExcel = withInstall(impExcel);
export const ExpExcelModal = withInstall(expExcelModal);
export * from './src/typing';
export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel';

View File

@ -1,48 +0,0 @@
import xlsx from 'xlsx';
import type { WorkBook } from 'xlsx';
import type { JsonToSheet, AoAToSheet } from './typing';
const { utils, writeFile } = xlsx;
const DEF_FILE_NAME = 'excel-list.xlsx';
export function jsonToSheetXlsx<T = any>({ data, header, filename = DEF_FILE_NAME, json2sheetOpts = {}, write2excelOpts = { bookType: 'xlsx' } }: JsonToSheet<T>) {
const arrData = [...data];
if (header) {
arrData.unshift(header);
json2sheetOpts.skipHeader = true;
}
const worksheet = utils.json_to_sheet(arrData, json2sheetOpts);
/* add worksheet to workbook */
const workbook: WorkBook = {
SheetNames: [filename],
Sheets: {
[filename]: worksheet,
},
};
/* output format determined by filename */
writeFile(workbook, filename, write2excelOpts);
/* at this point, out.xlsb will have been downloaded */
}
export function aoaToSheetXlsx<T = any>({ data, header, filename = DEF_FILE_NAME, write2excelOpts = { bookType: 'xlsx' } }: AoAToSheet<T>) {
const arrData = [...data];
if (header) {
arrData.unshift(header);
}
const worksheet = utils.aoa_to_sheet(arrData);
/* add worksheet to workbook */
const workbook: WorkBook = {
SheetNames: [filename],
Sheets: {
[filename]: worksheet,
},
};
/* output format determined by filename */
writeFile(workbook, filename, write2excelOpts);
/* at this point, out.xlsb will have been downloaded */
}

View File

@ -1,81 +0,0 @@
<template>
<BasicModal v-bind="$attrs" :title="t('component.excel.exportModalTitle')" @ok="handleOk" @register="registerModal">
<BasicForm :labelWidth="100" :schemas="schemas" :showActionButtonGroup="false" @register="registerForm" />
</BasicModal>
</template>
<script lang="ts">
import type { ExportModalResult } from './typing';
import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const schemas: FormSchema[] = [
{
field: 'filename',
component: 'Input',
label: t('component.excel.fileName'),
rules: [{ required: true }],
},
{
field: 'bookType',
component: 'Select',
label: t('component.excel.fileType'),
defaultValue: 'xlsx',
rules: [{ required: true }],
componentProps: {
options: [
{
label: 'xlsx',
value: 'xlsx',
key: 'xlsx',
},
{
label: 'html',
value: 'html',
key: 'html',
},
{
label: 'csv',
value: 'csv',
key: 'csv',
},
{
label: 'txt',
value: 'txt',
key: 'txt',
},
],
},
},
];
export default defineComponent({
components: { BasicModal, BasicForm },
emits: ['success', 'register'],
setup(_, { emit }) {
const [registerForm, { validateFields }] = useForm();
const [registerModal, { closeModal }] = useModalInner();
async function handleOk() {
const res = (await validateFields()) as ExportModalResult;
const { filename, bookType } = res;
emit('success', {
filename: `${filename.split('.').shift()}.${bookType}`,
bookType,
});
closeModal();
}
return {
schemas,
handleOk,
registerForm,
registerModal,
t,
};
},
});
</script>

View File

@ -1,151 +0,0 @@
<template>
<div>
<input ref="inputRef" type="file" v-show="false" accept=".xlsx, .xls" @change="handleInputClick" />
<div @click="handleUpload">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import XLSX from 'xlsx';
import { dateUtil } from '/@/utils/dateUtil';
import type { ExcelData } from './typing';
export default defineComponent({
name: 'ImportExcel',
props: {
// Date
dateFormat: {
type: String,
},
// +08:00
// https://github.com/SheetJS/sheetjs/issues/1470#issuecomment-501108554
timeZone: {
type: Number,
default: 8,
},
},
emits: ['success', 'error'],
setup(props, { emit }) {
const inputRef = ref<HTMLInputElement | null>(null);
const loadingRef = ref<Boolean>(false);
/**
* @description: 第一行作为头部
*/
function getHeaderRow(sheet: XLSX.WorkSheet) {
if (!sheet || !sheet['!ref']) return [];
const headers: string[] = [];
// A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}}
const range = XLSX.utils.decode_range(sheet['!ref']);
const R = range.s.r;
/* start in the first row */
for (let C = range.s.c; C <= range.e.c; ++C) {
/* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C; // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
headers.push(hdr);
}
return headers;
}
/**
* @description: 获得excel数据
*/
function getExcelData(workbook: XLSX.WorkBook) {
const excelData: ExcelData[] = [];
const { dateFormat, timeZone } = props;
for (const sheetName of workbook.SheetNames) {
const worksheet = workbook.Sheets[sheetName];
const header: string[] = getHeaderRow(worksheet);
let results = XLSX.utils.sheet_to_json(worksheet, {
raw: true,
dateNF: dateFormat, //Not worked
}) as object[];
results = results.map((row: object) => {
for (let field in row) {
if (row[field] instanceof Date) {
if (timeZone === 8) {
row[field].setSeconds(row[field].getSeconds() + 43);
}
if (dateFormat) {
row[field] = dateUtil(row[field]).format(dateFormat);
}
}
}
return row;
});
excelData.push({
header,
results,
meta: {
sheetName,
},
});
}
return excelData;
}
/**
* @description: 读取excel数据
*/
function readerData(rawFile: File) {
loadingRef.value = true;
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async (e) => {
try {
const data = e.target && e.target.result;
const workbook = XLSX.read(data, { type: 'array', cellDates: true });
// console.log(workbook);
/* DO SOMETHING WITH workbook HERE */
const excelData = getExcelData(workbook);
emit('success', excelData);
resolve('');
} catch (error) {
reject(error);
emit('error');
} finally {
loadingRef.value = false;
}
};
reader.readAsArrayBuffer(rawFile);
});
}
async function upload(rawFile: File) {
const inputRefDom = unref(inputRef);
if (inputRefDom) {
// fix can't select the same excel
inputRefDom.value = '';
}
await readerData(rawFile);
}
/**
* @description: 触发选择文件管理器
*/
function handleInputClick(e: Event) {
const files = e && (e.target as HTMLInputElement).files;
const rawFile = files && files[0]; // only setting files[0]
if (!rawFile) return;
upload(rawFile);
}
/**
* @description: 点击上传按钮
*/
function handleUpload() {
const inputRefDom = unref(inputRef);
inputRefDom && inputRefDom.click();
}
return { handleUpload, handleInputClick, inputRef };
},
});
</script>

View File

@ -1,27 +0,0 @@
import type { JSON2SheetOpts, WritingOptions, BookType } from 'xlsx';
export interface ExcelData<T = any> {
header: string[];
results: T[];
meta: { sheetName: string };
}
export interface JsonToSheet<T = any> {
data: T[];
header?: T;
filename?: string;
json2sheetOpts?: JSON2SheetOpts;
write2excelOpts?: WritingOptions;
}
export interface AoAToSheet<T = any> {
data: T[][];
header?: T[];
filename?: string;
write2excelOpts?: WritingOptions;
}
export interface ExportModalResult {
filename: string;
bookType: BookType;
}

View File

@ -466,14 +466,6 @@ const comp: AppRouteModule = {
title: t('routes.demo.feat.copy'),
},
},
{
path: 'fullCalendar',
name: 'fullCalendarDemo',
component: () => import('/@/views/demo/fullcalendar/index.vue'),
meta: {
title: t('routes.demo.feat.fullCalendar'),
},
},
{
path: 'codemirror',
name: 'codemirrorDemo',

View File

@ -129,51 +129,6 @@ const feat: AppRouteModule = {
title: t('routes.demo.feat.errorLog'),
},
},
{
path: 'excel',
name: 'Excel',
redirect: '/feat/excel/customExport',
component: getParentLayout('Excel'),
meta: {
// icon: 'mdi:microsoft-excel',
title: t('routes.demo.excel.excel'),
},
children: [
{
path: 'customExport',
name: 'CustomExport',
component: () => import('/@/views/demo/excel/CustomExport.vue'),
meta: {
title: t('routes.demo.excel.customExport'),
},
},
{
path: 'jsonExport',
name: 'JsonExport',
component: () => import('/@/views/demo/excel/JsonExport.vue'),
meta: {
title: t('routes.demo.excel.jsonExport'),
},
},
{
path: 'arrayExport',
name: 'ArrayExport',
component: () => import('/@/views/demo/excel/ArrayExport.vue'),
meta: {
title: t('routes.demo.excel.arrayExport'),
},
},
{
path: 'importExcel',
name: 'ImportExcel',
component: () => import('/@/views/demo/excel/ImportExcel.vue'),
meta: {
title: t('routes.demo.excel.importExcel'),
},
},
],
},
{
path: 'testTab/:id',
name: 'TestTab',

View File

@ -1,37 +0,0 @@
<template>
<PageWrapper title="导出示例" content="根据数组格式的数据进行导出">
<BasicTable title="基础表格" :columns="columns" :dataSource="data">
<template #toolbar>
<a-button @click="aoaToExcel"> </a-button>
</template>
</BasicTable>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable } from '/@/components/Table';
import { aoaToSheetXlsx } from '/@/components/Excel';
import { arrHeader, arrData, columns, data } from './data';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { BasicTable, PageWrapper },
setup() {
function aoaToExcel() {
// dataheader
aoaToSheetXlsx({
data: arrData,
header: arrHeader,
filename: '二维数组方式导出excel.xlsx',
});
}
return {
aoaToExcel,
columns,
data,
};
},
});
</script>

View File

@ -1,44 +0,0 @@
<template>
<PageWrapper title="导出示例" content="可以选择导出格式">
<BasicTable title="基础表格" :columns="columns" :dataSource="data">
<template #toolbar>
<a-button @click="openModal"> </a-button>
</template>
</BasicTable>
<ExpExcelModal @register="register" @success="defaultHeader" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable } from '/@/components/Table';
import { jsonToSheetXlsx, ExpExcelModal, ExportModalResult } from '/@/components/Excel';
import { columns, data } from './data';
import { useModal } from '/@/components/Modal';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { BasicTable, ExpExcelModal, PageWrapper },
setup() {
function defaultHeader({ filename, bookType }: ExportModalResult) {
// Object.keys(data[0])header
jsonToSheetXlsx({
data,
filename,
write2excelOpts: {
bookType,
},
});
}
const [register, { openModal }] = useModal();
return {
defaultHeader,
columns,
data,
register,
openModal,
};
},
});
</script>

View File

@ -1,51 +0,0 @@
<template>
<PageWrapper title="excel数据导入示例">
<ImpExcel @success="loadDataSuccess" dateFormat="YYYY-MM-DD">
<a-button class="m-3"> 导入Excel </a-button>
</ImpExcel>
<BasicTable v-for="(table, index) in tableListRef" :key="index" :title="table.title" :columns="table.columns" :dataSource="table.dataSource" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { ImpExcel, ExcelData } from '/@/components/Excel';
import { BasicTable, BasicColumn } from '/@/components/Table';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { BasicTable, ImpExcel, PageWrapper },
setup() {
const tableListRef = ref<
{
title: string;
columns?: any[];
dataSource?: any[];
}[]
>([]);
function loadDataSuccess(excelDataList: ExcelData[]) {
tableListRef.value = [];
console.log(excelDataList);
for (const excelData of excelDataList) {
const {
header,
results,
meta: { sheetName },
} = excelData;
const columns: BasicColumn[] = [];
for (const title of header) {
columns.push({ title, dataIndex: title });
}
tableListRef.value.push({ title: sheetName, dataSource: results, columns });
}
}
return {
loadDataSuccess,
tableListRef,
};
},
});
</script>

View File

@ -1,58 +0,0 @@
<template>
<PageWrapper title="导出示例" content="根据JSON格式的数据进行导出">
<BasicTable title="基础表格" :columns="columns" :dataSource="data">
<template #toolbar>
<a-button @click="defaultHeader"> </a-button>
<a-button @click="customHeader"> </a-button>
</template>
</BasicTable>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable } from '/@/components/Table';
import { jsonToSheetXlsx } from '/@/components/Excel';
import { columns, data } from './data';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { BasicTable, PageWrapper },
setup() {
function defaultHeader() {
// Object.keys(data[0])header
jsonToSheetXlsx({
data,
filename: '使用key作为默认头部.xlsx',
});
}
function customHeader() {
jsonToSheetXlsx({
data,
header: {
id: 'ID',
name: '姓名',
age: '年龄',
no: '编号',
address: '地址',
beginTime: '开始时间',
endTime: '结束时间',
},
filename: '自定义头部.xlsx',
json2sheetOpts: {
//
header: ['name', 'id'],
},
});
}
return {
defaultHeader,
customHeader,
columns,
data,
};
},
});
</script>

View File

@ -1,59 +0,0 @@
import { BasicColumn } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: 'ID',
dataIndex: 'id',
width: 80,
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
},
{
title: '年龄',
dataIndex: 'age',
width: 80,
},
{
title: '编号',
dataIndex: 'no',
width: 80,
},
{
title: '地址',
dataIndex: 'address',
},
{
title: '开始时间',
dataIndex: 'beginTime',
},
{
title: '结束时间',
dataIndex: 'endTime',
},
];
export const data: any[] = (() => {
const arr: any[] = [];
for (let index = 0; index < 40; index++) {
arr.push({
id: `${index}`,
name: `${index} John Brown`,
age: `${index + 10}`,
no: `${index}98678`,
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park',
beginTime: new Date().toLocaleString(),
endTime: new Date().toLocaleString(),
});
}
return arr;
})();
// ["ID", "姓名", "年龄", "编号", "地址", "开始时间", "结束时间"]
export const arrHeader = columns.map((column) => column.title);
// [["ID", "姓名", "年龄", "编号", "地址", "开始时间", "结束时间"],["0", "0 John Brown", "10", "098678"]]
export const arrData = data.map((item) => {
return Object.keys(item).map((key) => item[key]);
});

View File

@ -1,21 +0,0 @@
import { EventInput } from '@fullcalendar/vue3';
let eventGuid = 0;
let todayStr = new Date().toISOString().replace(/T.*$/, ''); // YYYY-MM-DD of today
export const INITIAL_EVENTS: EventInput[] = [
{
id: createEventId(),
title: 'All-day event',
start: todayStr,
},
{
id: createEventId(),
title: 'Timed event',
start: todayStr + 'T12:00:00',
},
];
export function createEventId() {
return String(eventGuid++);
}

View File

@ -1,166 +0,0 @@
<script lang="ts">
import { defineComponent } from 'vue';
import '@fullcalendar/core/vdom'; // solve problem with Vite
import FullCalendar, { CalendarOptions, EventApi, DateSelectArg, EventClickArg } from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import { INITIAL_EVENTS, createEventId } from './event-utils';
const Demo = defineComponent({
components: {
FullCalendar,
},
data() {
return {
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin, // needed for dateClick
],
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay',
},
initialView: 'dayGridMonth',
initialEvents: INITIAL_EVENTS, // alternatively, use the `events` setting to fetch from a feed
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: this.handleDateSelect,
eventClick: this.handleEventClick,
eventsSet: this.handleEvents,
/* you can update a remote database when these fire:
eventAdd:
eventChange:
eventRemove:
*/
} as CalendarOptions,
currentEvents: [] as EventApi[],
};
},
methods: {
handleWeekendsToggle() {
this.calendarOptions.weekends = !this.calendarOptions.weekends; // update a property
},
handleDateSelect(selectInfo: DateSelectArg) {
let title = prompt('Please enter a new title for your event');
let calendarApi = selectInfo.view.calendar;
calendarApi.unselect(); // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay,
});
}
},
handleEventClick(clickInfo: EventClickArg) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove();
}
},
handleEvents(events: EventApi[]) {
this.currentEvents = events;
},
},
});
export default Demo;
</script>
<template>
<div class="demo-app">
<div class="demo-app-sidebar">
<div class="demo-app-sidebar-section">
<h2>Instructions</h2>
<ul>
<li>Select dates and you will be prompted to create a new event</li>
<li>Drag, drop, and resize events</li>
<li>Click an event to delete it</li>
</ul>
</div>
<div class="demo-app-sidebar-section">
<label>
<input type="checkbox" :checked="calendarOptions.weekends" @change="handleWeekendsToggle" />
toggle weekends
</label>
</div>
<div class="demo-app-sidebar-section">
<h2>All Events ({{ currentEvents.length }})</h2>
<ul>
<li v-for="event in currentEvents" :key="event.id">
<b>{{ event.startStr }}</b>
<i>{{ event.title }}</i>
</li>
</ul>
</div>
</div>
<div class="demo-app-main">
<FullCalendar class="demo-app-calendar" :options="calendarOptions">
<template v-slot:eventContent="arg">
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
</div>
</div>
</template>
<style lang="css" scoped>
h2 {
margin: 0;
font-size: 16px;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
}
li {
margin: 1.5em 0;
padding: 0;
}
b {
/* used for event dates/times */
margin-right: 3px;
}
.demo-app {
display: flex;
min-height: 100%;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
.demo-app-sidebar {
width: 300px;
line-height: 1.5;
background: #eaf9ff;
border-right: 1px solid #d3e2e8;
}
.demo-app-sidebar-section {
padding: 2em;
}
.demo-app-main {
flex-grow: 1;
padding: 3em;
}
.fc {
/* the calendar root */
max-width: 1100px;
margin: 0 auto;
}
</style>