Commit 20fd7baf authored by hucy's avatar hucy

feat:i18n 学习

feat:文件上传组件封装
parent fe24dabc
## 2022-11-21
- i18n 学习
- 文件上传组件封装
## 2022-11-14
- canvas 绘制动态路径箭头
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -30,7 +30,7 @@ module.exports = configure(function (ctx) {
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-webpack/boot-files
boot: ['store'],
boot: ['store', 'vue-i18n'],
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
css: ['app.scss'],
......
import { boot } from 'quasar/wrappers';
import { createI18n } from 'vue-i18n';
import messages from 'src/i18n';
export default boot(({ app }) => {
// Create I18n instance
const i18n = createI18n({
// something vue-i18n options here ...
legacy: false, // you must set `false`, to use Composition API
locale: 'zh-CN', // set current locale
messages,
});
// Tell app to use the I18n instance
app.use(i18n);
});
/**
* Define the common constant
* 定义公共常数
*/
/**
* The prefix of web storage value, to ideintify the value data type.
* e.g __hcytest_object|{"key": "value"}
*/
// export const STORAGE_VALUE_PREFIX = '__hcytest_';
/**
* The key which save user last selected `Language` or `Local` to
* `SessionStorage` or `LocalStorage`
*/
export const LANG_STORAGE_KEY = 'LANG';
......@@ -4,6 +4,7 @@ const creatNotify = function (type: string, msg: string) {
Notify.create({
type,
message: msg,
position: 'top',
});
};
type IMessage = {
......
......@@ -5,6 +5,7 @@ import MyTooltip from './MyTooltip.vue';
import CanvasStaticRoute from './canvas-static-route/CanvasStaticRoute.vue';
import ChartCarouselWarp from './chart-carousel-warp/ChartCarouselWarp.vue';
import TitlePage from './title-page/TitlePage.vue';
import UpLoader from './uploader-page/UpLoader.vue';
export {
DateTimePick as ComDateTimePick,
......@@ -14,6 +15,7 @@ export {
CanvasStaticRoute as ComCanvasStaticRoute,
ChartCarouselWarp as ComChartCarouselWarp,
TitlePage as ComTitlePage,
UpLoader as ComUpLoader,
};
export default {
......@@ -24,4 +26,5 @@ export default {
CanvasStaticRoute,
ChartCarouselWarp,
TitlePage,
UpLoader,
};
<!--
* 上传器
-->
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue';
import { useMessage } from 'src/common/hooks';
import { getObjectURL } from './utils';
var XLSX = require('xlsx');
const { warn } = useMessage();
interface IDetailProps {
bgColor?: string; // 背景色
multiple?: boolean; // 多选
accept?: string; // 上传类型 'image/*'
dark?: boolean; // 启用深色背景
noShadow?: boolean; // 阴影
}
const props = withDefaults(defineProps<IDetailProps>(), {
bgColor: '#96C0CE',
multiple: false,
accept: '',
dark: false,
noShadow: false,
});
const emit = defineEmits<{
(e: 'onOk', value: any): void;
}>();
const darkDeep = '#222831';
const darkLight = '#393E46';
const imageTypeList = ['png', 'jpg', 'gif'];
const excelTypeList = ['xlsx', 'xls'];
const otherTypeList = ['pdf'];
const viewModel = ref(false);
const fileType = ref('');
const viewDialogTitle = ref('');
const state = reactive({
fileList: [] as any[],
viewTableHtml: null as any,
});
const canPreview = computed(() => (type: any) => {
const viewTypeList = imageTypeList.concat(excelTypeList, otherTypeList);
return viewTypeList.includes(type);
});
const totalSize = computed(() => {
const total = state.fileList.reduce((a, b) => a + b.size, 0);
let unit: string; // 单位
let size: number | string = 0;
if (total < 1000) {
unit = 'B';
size = total.toFixed(3);
} else {
const KB_size = total * 0.0009765625;
if (KB_size < 1000) {
unit = 'KB';
size = KB_size.toFixed(3);
} else {
const MB_size = KB_size * 0.0009765625;
if (MB_size < 1000) {
unit = 'MB';
size = MB_size.toFixed(2);
} else {
const GB_size = MB_size * 0.0009765625;
unit = 'GB';
size = GB_size.toFixed(2);
}
}
}
return size + unit;
});
function selectFile() {
let _input: HTMLInputElement = document.createElement('input'); // 创建一个隐藏的input
_input.setAttribute('type', 'file');
if (props.multiple) {
_input.setAttribute('multiple', true); // 多选
}
// _input.setAttribute('accept', 'image/*'); // 上传类型
if (props.accept) {
_input.setAttribute('accept', props.accept);
}
_input.onchange = function (e: any) {
let arr = [] as any[];
for (let i = 0; i < e.target.files.length; i++) {
const _file = e.target.files[i];
const typeArr = _file.name.split('.');
const type = typeArr.length > 1 ? typeArr[typeArr.length - 1] : '';
let unit: string; // 单位
let size: number | string = 0;
if (_file.size < 1000) {
unit = 'B';
size = _file.size.toFixed(3);
} else {
const KB_size = _file.size * 0.0009765625;
if (KB_size < 1000) {
unit = 'KB';
size = KB_size.toFixed(3);
} else {
const MB_size = KB_size * 0.0009765625;
if (MB_size < 1000) {
unit = 'MB';
size = MB_size.toFixed(2);
} else {
const GB_size = MB_size * 0.0009765625;
if (GB_size <= 2) {
unit = 'GB';
size = GB_size.toFixed(2);
} else {
const msg = '文件过大,请选择不超过2GB的文件';
warn(msg);
console.warn(msg);
return;
}
}
}
}
arr.push({
file: _file,
name: _file.name,
sizeFormat: size + unit,
fileType: type,
size: _file.size,
});
}
nextTick(() => {
state.fileList = state.fileList.concat(arr);
});
};
_input.click();
}
function removeItem(index: number) {
state.fileList.splice(index, 1);
}
function removeAll() {
state.fileList = [];
}
// 预览
function onView(data: any) {
if (excelTypeList.includes(data.fileType)) {
fileType.value = data.fileType;
viewDialogTitle.value = data.name;
viewModel.value = true;
void viewXlsx(data);
} else {
const url = getObjectURL(data.file);
window.open(url);
window.URL.revokeObjectURL(url);
}
}
async function viewXlsx(data: any) {
const file = data.file;
const _data = await file.arrayBuffer();
const wb = XLSX.read(_data);
const ws = wb.Sheets[wb.SheetNames[0]];
const ele = XLSX.utils.sheet_to_html(ws, { id: 'tabeller' });
state.viewTableHtml = ele;
}
function closeViewDialog() {
viewModel.value = false;
fileType.value = '';
state.viewTableHtml = null;
}
</script>
<template>
<div
class="com-uploader"
:class="{ 'shadow-4': !props.noShadow }"
:style="{
borderColor: props.dark ? darkDeep : props.bgColor,
backgroundColor: props.dark ? darkLight : '#ffffff',
}"
>
<div
class="uploader-title"
:style="{ backgroundColor: props.dark ? darkDeep : props.bgColor }"
>
<div class="icon">
<q-btn
v-if="state.fileList.length > 0"
style="width: 28px"
class="q-mr-xs"
dense
size="12px"
label="OK"
title="确认上传"
:style="{
backgroundColor: props.dark ? props.bgColor : '#fff',
color: props.dark ? darkLight : props.bgColor,
}"
@click="emit('onOk', state.fileList)"
/>
<q-btn
v-if="state.fileList.length > 0"
dense
flat
icon="bi-trash"
size="12px"
title="清除所有"
:style="{ color: props.dark ? props.bgColor : '#fff' }"
@click="removeAll"
/>
<span
v-if="state.fileList.length > 0"
:style="{
color: props.dark ? props.bgColor : '#fff',
}"
class="total-file-size"
>{{ totalSize }}</span
>
</div>
<div class="btn">
<q-btn
square
dense
push
icon="add"
size="12px"
title="选择文件"
label="选择文件"
:style="{
backgroundColor: props.dark ? props.bgColor : '#fff',
color: props.dark ? darkLight : props.bgColor,
}"
@click="selectFile"
/>
</div>
</div>
<div class="uploader-content">
<div class="uploader-main-content">
<div
class="file-item"
:style="{ borderColor: props.bgColor }"
v-for="(item, index) in state.fileList"
:key="index"
>
<div
class="name"
:title="item.name + ' ' + item.sizeFormat"
:style="{ color: props.bgColor }"
>
<span class="file-name">{{ item.name }}</span>
<span class="file-size">&nbsp;&nbsp;{{ item.sizeFormat }}</span>
</div>
<div class="btns">
<div>
<q-btn
v-if="canPreview(item.fileType)"
flat
dense
size="12px"
title="预览"
icon="bi-eye"
:style="{ color: props.bgColor }"
@click="onView(item)"
/>
</div>
<div>
<q-btn
flat
dense
size="12px"
title="从列表移除"
icon="bi-x-circle"
:style="{ color: props.bgColor }"
@click="removeItem(index)"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<q-dialog v-model="viewModel" persistent>
<q-card
style="min-height: 600px; min-width: 600px; max-width: 1400px"
class="column no-wrap"
>
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">{{ viewDialogTitle }}</div>
<q-space />
<q-btn icon="close" flat round dense @click="closeViewDialog" />
</q-card-section>
<q-card-section class="q-pt-none overflow-auto" style="flex: 1">
<div v-if="excelTypeList.includes(fileType)">
<div v-html="state.viewTableHtml" class="table-view"></div>
</div>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style lang="scss" scoped>
// 上传器
.com-uploader {
width: 100%;
min-width: 400px;
height: 100%;
min-height: 200px;
border-radius: 4px;
box-sizing: border-box;
border: 1px solid;
display: flex;
flex-flow: column nowrap;
// box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px,
// rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
.total-file-size {
display: inline-block;
font-size: 12px;
}
.uploader-title {
height: 46px;
padding: 8px;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
}
.uploader-content {
flex: 1;
overflow: auto;
}
.uploader-main-content {
display: grid;
grid-template-columns: 100%;
row-gap: 4px;
margin: 4px;
.file-item {
height: 40px;
box-sizing: border-box;
border-bottom: 2px solid;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.file-name {
font-weight: bold;
}
.file-size {
font-size: 12px;
}
}
.btns {
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 2px;
> div {
width: 28px;
}
}
}
}
// .dialog-box {
// // min-width: 600px;
// min-height: 600px;
// }
}
</style>
// 获取本地临时预览地址
export const getObjectURL = function (file: any) {
let url = null as any;
const _window = window as any;
if (_window.createObjectURL != undefined) {
url = _window.createObjectURL(file);
} else if (_window.URL != undefined) {
url = _window.URL.createObjectURL(file);
} else if (_window.webkitURL != undefined) {
url = _window.webkitURL.createObjectURL(file);
}
return url;
};
......@@ -2,7 +2,7 @@
const development = {
name: 'TopPMS',
productName: 'TopPMS',
storagePrefix: '__ABC__',
storagePrefix: '__hcytest_',
langs: ['en-US', 'zh-CN', 'zh-TW'],
serverUrl: { base: 'http://139.196.104.13:9000' },
//http://192.168.2.100:29010
......@@ -12,7 +12,7 @@ const development = {
const production = {
name: 'TopPMS',
productName: 'TopPMS',
storagePrefix: '__ABC__',
storagePrefix: '__hcytest_',
langs: ['en-US', 'zh-CN', 'zh-TW'],
// serverUrl: { base: '' },
base_url: '',
......
export const Langs = [
{ label: '简体中文', value: 'zh-CN' },
{ label: '繁體中文', value: 'zh-TW' },
{ label: 'English', value: 'en-US' },
{ label: '日本', value: 'ja' },
];
......@@ -10,4 +10,6 @@ export default {
refresh: 'bi-arrow-repeat', // 刷新
delete: 'bi-trash', // 删除
alarm_clock: 'bi-stopwatch', // 闹钟
export: 'fa-solid fa-file-export', // 导出
upload: 'fa-solid fa-cloud-arrow-up', // 上传
};
......@@ -50,3 +50,29 @@ a:hover {
background-color: white;
box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
}
.table-view {
position: relative;
table {
position: relative;
margin: 0 auto;
text-align: center;
background-color: #fafafa;
border: 1px solid #dadada;
border-collapse: collapse; /*表格的边框合并,如果相邻,则共用一个边框*/
border-spacing: 0; /*设置行与单元格边框的间距。当表格边框独立(即border-collapse:separate;)此属性才起作用*/
td {
padding: 4px;
border: 1px solid #dadada;
height: 30px;
min-height: 30px;
min-width: 100px;
}
th {
padding: 4px;
border: 1px solid #dadada;
height: 30px;
min-height: 30px;
}
}
}
......@@ -25,10 +25,10 @@ $accent: #9c27b0;
$dark: #1d1d1d;
$positive: #8bc24c;
$negative: #e8222d;
$info: #90c078;
$warning: #f7b32d;
$positive: #5bb318;
$negative: #d2001a;
$info: #ff8787;
$warning: #ffb200;
$--color-white: #fff;
$--color-dark: #000;
......@@ -54,3 +54,5 @@ $border-color: rgba(0, 0, 0, 0.12);
$gray-text: #000000de;
$gray-light-text: #00000098;
$header-heigyht: 50px;
export default {
hello: 'hello!',
'World Cup': 'World Cup',
};
import zhCN from './zh-CN';
import zhTW from './zh-TW';
import enUS from './en-US';
import ja from './ja';
export default {
'zh-CN': zhCN,
'zh-TW': zhTW,
'en-US': enUS,
ja: ja,
};
export default {
hello: 'こんにちは!',
'World Cup': 'ワールドカップ',
};
export default {
hello: '你好!',
'World Cup': '世界杯',
};
export default {
hello: '你好!',
'World Cup': '世界盃',
};
......@@ -45,6 +45,7 @@
</q-tabs>
<q-space />
<language-select />
<div>Quasar v{{ $q.version }}</div>
</q-toolbar>
</q-header>
......@@ -75,6 +76,7 @@ import { onBeforeRouteUpdate, useRouter } from 'vue-router';
import { usePageStore } from 'src/common/hooks';
import EssentialLink from 'components/EssentialLink.vue';
import TopLeftLoading from './TopLeftLoading.vue';
import LanguageSelect from './element/LanguageSelect.vue';
export default defineComponent({
name: 'MainLayout',
......@@ -82,6 +84,7 @@ export default defineComponent({
components: {
EssentialLink,
TopLeftLoading,
LanguageSelect,
},
setup() {
......@@ -154,6 +157,13 @@ export default defineComponent({
link: '/page6',
active: false,
},
{
title: '七',
caption: '疫情防控',
icon: require('./menuListIcons/page7.svg'),
link: '/page7',
active: false,
},
];
for (const item of lists) {
......
<!--
* 语言选择
-->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useLocalStorage } from 'src/common/hooks';
import { Langs } from 'src/config/enum';
// interface IDetailProps {
// type: string;
// data: any;
// }
// const props = withDefaults(defineProps<IDetailProps>(), {
// type: '',
// data: {},
// });
const { locale } = useI18n();
const { getItem, setItem } = useLocalStorage();
const languageLabel = ref('');
onMounted(() => {
let language = getItem('LANG');
let languageOpt = { label: '简体中文', value: 'zh-CN' };
if (language) {
languageOpt = JSON.parse(language as string);
} else {
setItem('LANG', JSON.stringify(languageOpt));
}
locale.value = languageOpt.value;
languageLabel.value = languageOpt.label;
});
function onItemClick(item: any) {
if (item.value === locale.value) return;
setItem('LANG', JSON.stringify(item));
languageLabel.value = item.label;
locale.value = item.value;
}
</script>
<template>
<div class="language-select">
<q-btn-dropdown :label="languageLabel" flat color="#fff" no-caps>
<q-list>
<q-item
clickable
v-close-popup
@click="onItemClick(item)"
v-for="item in Langs"
:key="item.value"
:class="{ 'active-sel-item': item.value === $i18n.locale }"
>
<q-item-section>
<q-item-label>{{ item.label }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</template>
<style lang="scss" scoped>
.language-select {
box-sizing: border-box;
height: $header-heigyht;
line-height: $header-heigyht;
margin-right: $padding-sm;
}
.active-sel-item {
color: $primary;
}
</style>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1668562510647" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15056" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M874.688 362.688H592.376v298.624h282.312c82.438 0 149.312-66.812 149.312-149.282 0-82.498-66.876-149.342-149.312-149.342z m0 256H635.062V405.344h239.624c58.812 0 106.624 47.876 106.624 106.688 0.002 58.78-47.81 106.656-106.622 106.656zM0 512.032c0 82.468 66.844 149.282 149.344 149.282h282.282V362.688H149.344C66.844 362.688 0 429.562 0 512.032z m388.968 106.656H149.344c-58.812 0-106.688-47.876-106.688-106.656 0-58.812 47.876-106.688 106.688-106.688h239.624v213.344z" fill="#CCD1D9" p-id="15057"></path><path d="M832 682.688c0 23.562-19.124 42.624-42.688 42.624H234.656c-23.562 0-42.656-19.062-42.656-42.624V341.344c0-23.562 19.094-42.656 42.656-42.656h554.656c23.562 0 42.688 19.094 42.688 42.656v341.344z" fill="#48CFAD" p-id="15058"></path><path d="M789.312 298.688h-42.624c23.562 0 42.624 19.094 42.624 42.656v341.344c0 23.562-19.062 42.624-42.624 42.624h42.624c23.562 0 42.688-19.062 42.688-42.624V341.344c0-23.562-19.124-42.656-42.688-42.656z" fill="#FFFFFF" opacity=".2" p-id="15059"></path><path d="M725.718 597.312H299.032v0.062c-0.124 0-0.25-0.062-0.376-0.062-11.75 0-21.312 9.562-21.312 21.376 0 11.75 9.562 21.312 21.312 21.312H725.72c11.782 0 21.344-9.562 21.344-21.312-0.002-11.812-9.564-21.376-21.346-21.376zM298.468 554.688c0.124 0 0.25-0.062 0.376-0.062v0.062h426.688c11.782 0 21.344-9.562 21.344-21.344s-9.562-21.312-21.344-21.312H298.468c-11.782 0-21.312 9.532-21.312 21.312s9.532 21.344 21.312 21.344z" fill="#37BC9B" p-id="15060"></path></svg>
\ No newline at end of file
......@@ -19,7 +19,8 @@ export default {
// import SubComponents from './element/RequestAnimationFrame.vue';
// import SubComponents from './element/equipment/IndexPage.vue';
import SubComponents from './element/canvas-router-map/CanvasRouterMap.vue'; // canvas 绘制动态路径
// import SubComponents from './element/canvas-router-map/CanvasRouterMap.vue'; // canvas 绘制动态路径
import SubComponents from './element/table-study/TableStudy.vue'; // 表格学习
</script>
<template>
<div class="page6 container-height">
......
<!--
* 表格学习
-->
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useMessage } from 'src/common/hooks';
import { ComTitlePage, ComUpLoader } from 'src/components';
import ICON from 'src/config/icons';
import { read, utils, writeFile } from 'xlsx';
// var XLSX = require('xlsx');
const { t } = useI18n();
const { warn } = useMessage();
const excelTypeList = ['xlsx', 'xls'];
const columns: any[] = [
{
name: 'name',
field: 'name',
label: '姓名',
align: 'center',
},
{
name: 'age',
field: 'age',
label: '年龄',
align: 'center',
},
{
name: 'sex',
field: 'sex',
label: '性别',
align: 'center',
},
{
name: 'salary',
field: 'salary',
label: '工资',
align: 'center',
},
{
name: 'hobby',
field: 'hobby',
label: '爱好',
align: 'center',
},
];
const uploadXlsxMsg = ref('未选择任何文件');
const state = reactive({
rows: [
{
name: '张三',
sex: '男',
age: 18,
hobby: '摸鱼',
},
{
sex: '男',
age: 18,
name: '孙火旺 名字很长很长名字很长很长',
},
{
salary: '5000.00',
name: '小美',
age: 20,
sex: '女',
},
{
name: '小李',
age: 22,
salary: '6000.00',
sex: '男',
},
],
viewTableHtml: null as any,
});
function onXport() {
const table = document.getElementById('TableToExport');
const wb = utils.table_to_book(table);
writeFile(wb, '库存表.xlsx');
}
function uploadXlsx() {
let _input: HTMLInputElement = document.createElement('input'); // 创建一个隐藏的input
_input.setAttribute('type', 'file');
_input.setAttribute(
'accept',
'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
);
_input.onchange = async function (e: any) {
const file = e.target.files[0];
const fileTypeArr = file.name.split('.');
const fileType = fileTypeArr[fileTypeArr.length - 1];
// console.log('上传文件 >>>>', fileType, file);
let unit: string; // 单位
let size: number | string = 0;
if (file.size < 1000) {
unit = 'B';
size = file.size.toFixed(3);
} else {
const KB_size = file.size * 0.0009765625;
if (KB_size < 1000) {
unit = 'KB';
size = KB_size.toFixed(3);
} else {
const MB_size = KB_size * 0.0009765625;
if (MB_size < 1000) {
unit = 'MB';
size = MB_size.toFixed(2);
} else {
const GB_size = MB_size * 0.0009765625;
if (GB_size <= 2) {
unit = 'GB';
size = GB_size.toFixed(2);
} else {
const msg = '文件过大,请选择不超过2GB的文件';
warn(msg);
console.warn(msg);
return;
}
}
}
}
if (excelTypeList.includes(fileType)) {
uploadXlsxMsg.value = `${file.name}&nbsp;&nbsp;${size}${unit}`;
const data = await file.arrayBuffer();
const wb = read(data);
const ws = wb.Sheets[wb.SheetNames[0]];
const asd = utils.sheet_to_html(ws, { id: 'tabeller' });
state.viewTableHtml = asd;
} else {
const msg = `文件格式 ${fileType} 错误`;
warn(msg);
console.warn(msg);
}
};
_input.click();
}
function uploadOk(val: any) {
console.log('uploadOk >>>>', val);
}
function exportTableData() {
// https://docs.sheetjs.com/docs/getting-started/example/
// 生成一个工作表
const worksheet = utils.json_to_sheet(state.rows);
// 创建一个新工作簿
const workbook = utils.book_new();
// 把工作表添加到工作簿,这个新的工作表被命名为 "StudentInfo"
utils.book_append_sheet(workbook, worksheet, 'StudentInfo');
// 更改标题行
// 默认标题行是row里面对象数据第一个出现的键名
// { origin: "A1" } 从单元格A1开始写入文本
utils.sheet_add_aoa(worksheet, [['姓名', '性别', '年龄', '爱好', '工资']], {
origin: 'A1',
});
// 有些名称比默认列宽长,可以通过设置 "!cols" 来设置列宽
worksheet['!cols'] = [{ wch: 40 }]; // set column A width to 40 characters
// 计算最大宽度
// const max_width = state.rows.reduce((w, r) => Math.max(w, r.name.length), 10);
// worksheet['!cols'] = [{ wch: max_width }];
// 插入新的数据行
// const ws = workbook.Sheets['StudentInfo'];
// XLSX.utils.sheet_add_aoa(
// worksheet,
// [
// ['郝美丽', '女', '40', '购物', '1000000'],
// ['甄漂亮', '女', '30', '看电视', '2000000'],
// ],
// {
// origin: -1,
// }
// );
// 创建一个 XLSX 文件并尝试保存为 学生信息.xlsx
writeFile(workbook, '学生信息.xlsx', { compression: true });
}
</script>
<template>
<div>
<com-title-page title="table学习" />
<div class="my-table q-mt-md q-gutter-sm">
<div>
<q-btn
label="导出表格数据"
square
push
:icon="ICON.export"
style="background: #4aacc5; color: white"
@click="exportTableData"
/>
</div>
<div>
<q-btn
:label="t('hello')"
square
push
style="background: #4aacc5; color: white"
/>
</div>
<div>
<q-btn
:label="t('World Cup')"
square
push
style="background: #4aacc5; color: white"
/>
</div>
<div>
<q-table :rows="state.rows" :columns="columns" row-key="name" />
</div>
</div>
<div class="upload-box">
<div style="width: 400px; height: 232px" class="q-my-md">
<com-up-loader bg-color="#FAD4D4" dark multiple @on-ok="uploadOk" />
</div>
<div style="width: 400px; height: 232px" class="q-my-md">
<com-up-loader bg-color="#A4BE7B" multiple @on-ok="uploadOk" />
</div>
<div>
<div class="upload-xlsx-box">
<div>
<q-btn
label="上传文件"
square
push
:icon="ICON.upload"
style="background: #4aacc5; color: white"
@click="uploadXlsx"
/>
</div>
<div>
<q-chip :ripple="false" square
><span v-html="uploadXlsxMsg"></span>
</q-chip>
</div>
</div>
</div>
<div class="view-table-box">
<div class="view-table-inner">
<div v-html="state.viewTableHtml" class="table-view"></div>
</div>
</div>
</div>
<div class="table-box q-mt-md q-pb-md">
<div class="q-mb-sm">
<q-btn
label="导出库存表"
square
push
:icon="ICON.export"
style="background: #4aacc5; color: white"
@click="onXport"
/>
</div>
<div>
<table id="TableToExport">
<caption>
库存表
</caption>
<thead>
<tr>
<th rowspan="3">货号</th>
<th rowspan="3">颜色</th>
<th rowspan="3">码号</th>
<th rowspan="3">上月库存数量</th>
<th rowspan="3">单价</th>
<th rowspan="3">上月库存金额</th>
<th>4月采购</th>
<th colspan="2">4月销售</th>
<th rowspan="3">本月库存数量</th>
<th rowspan="3">单价</th>
<th rowspan="3">本月库存金额</th>
</tr>
<tr>
<th>采购合计</th>
<th>4月16日</th>
<th>5月20日</th>
</tr>
<tr>
<th>数量合计</th>
<th>数量</th>
<th>数量</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="12">商品增加,直接“复制” “粘贴”即可</td>
</tr>
<tr>
<td colspan="12">
每月月初复制粘贴“上月月末库存” 数据作为 “本月月初库存” 数据
</td>
</tr>
</tfoot>
<tbody>
<tr>
<td rowspan="6">&nbsp;</td>
<td rowspan="3">&nbsp;</td>
<td>&nbsp;</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>2</td>
<td>2.00</td>
<td>4.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>6</td>
<td>&nbsp;</td>
<td>12</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>6</td>
<td>0.00</td>
<td>12.00</td>
</tr>
<tr>
<td rowspan="5">&nbsp;</td>
<td rowspan="3">&nbsp;</td>
<td>&nbsp;</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>3</td>
<td>3.00</td>
<td>9.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>9</td>
<td>&nbsp;</td>
<td>27</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>9</td>
<td>0.00</td>
<td>27.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>合计</td>
<td>&nbsp;</td>
<td>15</td>
<td>&nbsp;</td>
<td>39.00</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>15</td>
<td>&nbsp;</td>
<td>39.00</td>
</tr>
<tr>
<td rowspan="6">&nbsp;</td>
<td rowspan="3">&nbsp;</td>
<td>&nbsp;</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
<td>0</td>
<td>&nbsp;</td>
<td>0</td>
<td>4</td>
<td>4.00</td>
<td>16.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>12</td>
<td>&nbsp;</td>
<td>48.00</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>12</td>
<td>&nbsp;</td>
<td>48.00</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>合计</td>
<td>&nbsp;</td>
<td>12</td>
<td>&nbsp;</td>
<td>48.00</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>12</td>
<td>&nbsp;</td>
<td>48.00</td>
</tr>
<tr style="background-color: #dbeef4">
<td>总计</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>27</td>
<td>&nbsp;</td>
<td>87.00</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>27</td>
<td>&nbsp;</td>
<td>87.00</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.table-box {
display: flex;
flex-direction: column;
justify-content: center;
}
#TableToExport {
text-align: center; /*文本居中*/
border-collapse: collapse; /*表格的边框合并,如果相邻,则共用一个边框*/
border-spacing: 0; /*设置行与单元格边框的间距。当表格边框独立(即border-collapse:separate;)此属性才起作用*/
border: 1px solid #0d3f67;
color: #000;
caption {
font-size: 28px;
letter-spacing: 15px;
padding: 10px 0;
color: white;
background-color: #4aacc5;
border-bottom: none;
}
thead {
tr {
background-color: #dbeef4;
}
}
th {
padding: 6px 4px;
font-size: 16px;
min-width: 120px;
border: 2px solid #4aacc5;
}
td {
font-size: 16px;
padding: 6px 4px;
border: 2px solid #4aacc5;
}
tfoot > tr > td {
padding: 0;
font-size: 18px;
}
}
.view-table-box {
margin-top: $padding-sm;
box-sizing: border-box;
background-color: #dbeef4;
max-width: 1440px;
padding: 16px;
overflow: hidden;
.view-table-inner {
overflow: auto;
}
}
// 上传文件
.upload-xlsx-box {
display: flex;
flex-flow: row nowrap;
}
</style>
<!--
* 疫情防控
-->
<script lang="ts">
export default {
name: 'PAGE7',
};
</script>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
type EChartsOption = echarts.EChartsOption;
var chartMain;
const chartMainRef = ref<any>(null);
onMounted(() => {
initDom();
});
function initDom() {
chartMain = echarts.init(chartMainRef.value);
const option: EChartsOption = {
tooltip: {
trigger: 'axis',
},
legend: {
data: [
'本土感染者',
'省内感染者',
'外省来(返)蓉感染者',
'境外输入感染者',
],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 40,
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [
'2022-11-08',
'2022-11-09',
'2022-11-10',
'2022-11-11',
'2022-11-12',
'2022-11-13',
'2022-11-14',
'2022-11-15',
'2022-11-16',
'2022-11-17',
'2022-11-18',
'2022-11-19',
'2022-11-20',
],
},
yAxis: {
type: 'value',
},
series: [
{
name: '本土感染者',
type: 'line',
data: [30, 35, 50, 48, 57, 99, 133, 100, 121, 176, 211, 200, 316],
itemStyle: {
color: '#E8110F',
},
},
{
name: '省内感染者',
type: 'line',
data: [16, 22, 28, 29, 40, 79, 115, 82, 107, 157, 192, 184, 280],
itemStyle: {
color: '#FBC723',
},
},
{
name: '外省来(返)蓉感染者',
type: 'line',
data: [14, 13, 22, 19, 17, 20, 18, 18, 14, 19, 19, 16, 36],
itemStyle: {
color: '#1B6AA5',
},
},
{
name: '境外输入感染者',
type: 'line',
data: [7, 15, 26, 10, 5, 9, 9, 8, 21, 17, 17, 12, 20],
itemStyle: {
color: '#333333',
},
},
],
};
chartMain.setOption(option);
}
</script>
<template>
<div>
<div>成都市新冠疫情单日新增情况</div>
<div class="content">
<div
class="chart-main"
ref="chartMainRef"
style="width: 1200px; height: 700px"
></div>
</div>
</div>
</template>
<style lang="scss" scoped>
.content {
display: flex;
justify-content: center;
}
.chart-main {
}
</style>
......@@ -67,7 +67,17 @@ const routes: RouteRecordRaw[] = [
name: 'PAGE6',
component: () => import('../modules/page6/IndexPage.vue'),
meta: {
title: '页面6',
title: '一些js练习',
permission: ['*'],
keepalive: true,
},
},
{
path: 'page7',
name: 'PAGE7',
component: () => import('../modules/page7/IndexPage.vue'),
meta: {
title: '页面7',
permission: ['*'],
keepalive: true,
},
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment