feat: 新增数据校验,数据导入导出

This commit is contained in:
mtruning 2022-03-19 23:28:33 +08:00
parent 80c5856732
commit 285fff6add
11 changed files with 270 additions and 66 deletions

View File

@ -1,16 +1,16 @@
<template> <template>
<div v-show="load"> <div v-show="load" class="go-skeleton">
<div v-show="repeat == 1"> <div v-show="repeat == 1">
<n-skeleton v-bind="$attrs"></n-skeleton> <n-skeleton v-bind="$attrs"></n-skeleton>
</div> </div>
<div v-show="repeat == 2"> <div v-show="repeat == 2">
<n-skeleton v-bind="$attrs"></n-skeleton> <n-skeleton v-bind="$attrs"></n-skeleton>
<n-skeleton v-bind="$attrs" style="width: 60%;"></n-skeleton> <n-skeleton class="item" v-bind="$attrs" style="width: 60%;"></n-skeleton>
</div> </div>
<div v-show="repeat > 2"> <div v-show="repeat > 2">
<n-skeleton v-bind="$attrs" :repeat="repeat - 2"></n-skeleton> <n-skeleton v-bind="$attrs" :repeat="repeat - 2"></n-skeleton>
<n-skeleton v-bind="$attrs" style="width: 60%;"></n-skeleton> <n-skeleton class="item" v-bind="$attrs" style="width: 60%;"></n-skeleton>
<n-skeleton v-bind="$attrs" style="width: 50%;"></n-skeleton> <n-skeleton class="item" v-bind="$attrs" style="width: 50%;"></n-skeleton>
</div> </div>
</div> </div>
</template> </template>
@ -27,3 +27,11 @@ defineProps({
} }
}) })
</script> </script>
<style lang="scss" scoped>
@include go('skeleton') {
.item {
margin-top: 5px;
}
}
</style>

10
src/enums/fileTypeEnum.ts Normal file
View File

@ -0,0 +1,10 @@
// 文件上传时的格式映射
export enum FileTypeEnum {
// 文档
TXT = 'text/plain',
JSON = 'application/json',
// 图片
PNG = 'image/png',
JPEG = 'image/jpeg',
GIF = 'image/gif',
}

View File

@ -2,8 +2,9 @@ import { echartOptionProfixHandle, publicConfig } from '@/packages/public'
import { BarCommonConfig } from './index' import { BarCommonConfig } from './index'
import { CreateComponentType } from '@/packages/index.d' import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis', 'dataset'] export const includes = ['legend', 'xAxis', 'yAxis']
export const option = { export const option = {
tooltip: { tooltip: {
@ -25,6 +26,7 @@ export const option = {
show: true, show: true,
type: 'value' type: 'value'
}, },
dataset: { ...dataJson },
series: [ series: [
{ {
type: 'bar', type: 'bar',

View File

@ -43,6 +43,7 @@ use([
]) ])
const option = computed(() => { const option = computed(() => {
return setData(mergeTheme(props.chartConfig.option, props.themeSetting, includes), dataJson) // TODO dataset
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
}) })
</script> </script>

View File

@ -69,7 +69,9 @@ import {
AlignVerticalTop as AlignVerticalTopIcon, AlignVerticalTop as AlignVerticalTopIcon,
AlignHorizontalCenter as AlignHorizontalCenterIcon, AlignHorizontalCenter as AlignHorizontalCenterIcon,
AlignHorizontalRight as AlignHorizontalRightIcon, AlignHorizontalRight as AlignHorizontalRightIcon,
AlignVerticalBottom as AlignVerticalBottomIcon AlignVerticalBottom as AlignVerticalBottomIcon,
DocumentAdd as DocumentAddIcon,
DocumentDownload as DocumentDownloadIcon,
} from '@vicons/carbon' } from '@vicons/carbon'
const ionicons5 = { const ionicons5 = {
@ -205,7 +207,11 @@ const carbon = {
AlignVerticalTopIcon, AlignVerticalTopIcon,
AlignHorizontalCenterIcon, AlignHorizontalCenterIcon,
AlignHorizontalRightIcon, AlignHorizontalRightIcon,
AlignVerticalBottomIcon AlignVerticalBottomIcon,
// 添加文件
DocumentAddIcon,
// 下载文件
DocumentDownloadIcon
} }
// https://www.xicons.org/#/ 还有很多 // https://www.xicons.org/#/ 还有很多

View File

@ -7,6 +7,7 @@ import {
NH2, NH2,
NH3, NH3,
NH4, NH4,
NCode,
NText, NText,
NTime, NTime,
NEllipsis, NEllipsis,
@ -101,6 +102,7 @@ const naive = create({
NH2, NH2,
NH3, NH3,
NH4, NH4,
NCode,
NText, NText,
NTime, NTime,
NEllipsis, NEllipsis,

View File

@ -2,7 +2,7 @@ import { CreateComponentType } from '@/packages/index.d'
import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d' import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
import type { import type {
ChartColorsNameType, ChartColorsNameType,
GlobalThemeJsonType GlobalThemeJsonType,
} from '@/settings/chartThemes/index' } from '@/settings/chartThemes/index'
// 编辑画布属性 // 编辑画布属性
@ -13,7 +13,7 @@ export enum EditCanvasTypeEnum {
SCALE = 'scale', SCALE = 'scale',
USER_SCALE = 'userScale', USER_SCALE = 'userScale',
LOCK_SCALE = 'lockScale', LOCK_SCALE = 'lockScale',
IS_DRAG = 'isDrag' IS_DRAG = 'isDrag',
} }
// 编辑区域 // 编辑区域
@ -46,7 +46,7 @@ export enum EditCanvasConfigEnum {
CHART_THEME_SETTING = 'chartThemeSetting', CHART_THEME_SETTING = 'chartThemeSetting',
BACKGROUND = 'background', BACKGROUND = 'background',
BACKGROUND_IAMGE = 'backgroundImage', BACKGROUND_IAMGE = 'backgroundImage',
SELECT_COLOR = 'selectColor' SELECT_COLOR = 'selectColor',
} }
export interface EditCanvasConfigType { export interface EditCanvasConfigType {
@ -80,7 +80,7 @@ export enum EditCanvasTypeEnum {
START_X = 'startX', START_X = 'startX',
START_Y = 'startY', START_Y = 'startY',
X = 'x', X = 'x',
Y = 'y' Y = 'y',
} }
// 鼠标位置 // 鼠标位置
@ -118,15 +118,15 @@ export enum ChartEditStoreEnum {
// 以下需要存储 // 以下需要存储
EDIT_CANVAS_CONFIG = 'editCanvasConfig', EDIT_CANVAS_CONFIG = 'editCanvasConfig',
REQUEST_CONFIG = 'requestConfig', REQUEST_CONFIG = 'requestConfig',
COMPONENT_LIST = 'componentList' COMPONENT_LIST = 'componentList',
} }
// 数据相关 // 数据相关
export enum RequestDataTypeEnum { export enum RequestDataTypeEnum {
// 静态数据 // 静态数据
STATIC, STATIC = 0,
// 请求数据 // 请求数据
AJAX AJAX = 1,
} }
// 数据配置 // 数据配置

43
src/utils/file.ts Normal file
View File

@ -0,0 +1,43 @@
/**
* *
* @param { File } file
*/
export const readFile = (file: File) => {
return new Promise((resolve: Function) => {
try {
const reader = new FileReader()
reader.onload = (evt: ProgressEvent<FileReader>) => {
if (evt.target) {
resolve(evt.target.result)
}
}
reader.readAsText(file)
} catch (error) {
window['$message'].error('文件读取失败!')
}
})
}
/**
*
* @param { string } content
* @param { ?string } filename
* @param { ?string } fileSuffix
*/
export const downloadFile = (
content: string,
filename = new Date().getDate().toString(),
fileSuffix?: string
) => {
const ele = document.createElement('a') // 创建下载链接
ele.download = `${filename}.${fileSuffix}` //设置下载的名称
ele.style.display = 'none' // 隐藏的可下载链接
// 字符内容转变成blob地址
const blob = new Blob([content])
ele.href = URL.createObjectURL(blob)
// 绑定点击时间
document.body.appendChild(ele)
ele.click()
// 然后移除
document.body.removeChild(ele)
}

View File

@ -6,3 +6,4 @@ export * from '@/utils/style'
export * from '@/utils/plugin' export * from '@/utils/plugin'
export * from '@/utils/componets' export * from '@/utils/componets'
export * from '@/utils/type' export * from '@/utils/type'
export * from '@/utils/file'

View File

@ -0,0 +1,19 @@
import { RequestDataTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
// 匹配结果
export enum DataResultEnum {
NULL = 0,
SUCCESS = 1,
FAILURE = 2,
}
export enum SelcetOptionsLableEnum {
STATIC = '静态数据',
AJAX = '动态请求',
}
export interface SelectOptionsType {
label: SelcetOptionsLableEnum
value: RequestDataTypeEnum
disabled?: boolean
}

View File

@ -1,33 +1,33 @@
<template> <template>
<div class="go-chart-configurations-data"> <div class="go-chart-configurations-data" v-if="targetData">
<setting-item-box v-if="targetData" name="请求方式" :alone="true"> <setting-item-box name="请求方式" :alone="true">
<n-select <n-select
v-model:value="targetData.data.requestDataType" v-model:value="targetData.data.requestDataType"
:options="selcetOpeions" :options="selectOptions"
@on-update="updateHandle" @on-update="selectHandle"
/> />
</setting-item-box> </setting-item-box>
<n-timeline> <n-timeline>
<n-timeline-item type="info" title="数据结构"> <n-timeline-item type="info" title="数据映射">
<n-table striped> <n-table striped>
<thead> <thead>
<tr> <tr>
<th>字段</th> <th v-for="item in tableTitle" :key="item">{{ item }}</th>
<th>映射</th>
<th>状态</th>
</tr> </tr>
</thead> </thead>
<tbody> <go-skeleton :repeat="3" :load="tableLoad" style="width: 300px;"></go-skeleton>
<tr v-for="(item, index) in dataStructure" :key="index"> <tbody v-show="!tableLoad">
<tr v-for="(item, index) in getDimensionsAndSource" :key="index">
<td>{{ item.field }}</td> <td>{{ item.field }}</td>
<td>{{ item.mapping }}</td> <td>{{ item.mapping }}</td>
<td> <td>
<n-space> <n-space v-if="item.result === 0">
<n-badge <n-badge dot type="success"></n-badge>
dot <n-text></n-text>
:type="item.result ? 'success' : 'error'" </n-space>
></n-badge> <n-space v-else>
<n-text>匹配{{ item.result ? '成功' : '失败' }}</n-text> <n-badge dot :type="item.result === 1 ? 'success' : 'error'"></n-badge>
<n-text>匹配{{ item.result === 1 ? '成功' : '失败' }}</n-text>
</n-space> </n-space>
</td> </td>
</tr> </tr>
@ -35,60 +35,172 @@
</n-table> </n-table>
</n-timeline-item> </n-timeline-item>
<n-timeline-item type="success" title="静态数据"> <n-timeline-item type="success" title="静态数据">
<n-code <n-space vertical>
v-for="(item, index) in code" <n-space class="source-btn-box">
:key="index" <n-upload
:code="item.data" v-model:file-list="uploadFileListRef"
language="json" :show-file-list="false"
></n-code> :customRequest="customRequest"
@before-upload="beforeUpload"
>
<n-button class="sourceBtn-item">
<template #icon>
<n-icon>
<document-add-icon />
</n-icon>
</template>
导入json / txt
</n-button>
</n-upload>
<n-button class="sourceBtn-item" @click="download">
<template #icon>
<n-icon>
<document-download-icon />
</n-icon>
</template>
下载
</n-button>
</n-space>
<n-card>
<n-code :code="getSource" language="json"></n-code>
</n-card>
</n-space>
</n-timeline-item> </n-timeline-item>
</n-timeline> </n-timeline>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, toRaw } from 'vue' import { ref, toRefs, computed, watch, nextTick } from 'vue'
import { SettingItemBox } from '@/components/ChartItemSetting/index' import { SettingItemBox } from '@/components/ChartItemSetting/index'
import { RequestDataTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { RequestDataTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useTargetData } from '../hooks/useTargetData.hook' import { useTargetData } from '../hooks/useTargetData.hook'
import { UploadCustomRequestOptions } from 'naive-ui'
import { FileTypeEnum } from '@/enums/fileTypeEnum'
import { readFile, downloadFile } from '@/utils'
import { DataResultEnum, SelcetOptionsLableEnum, SelectOptionsType } from './index.d'
import { icon } from '@/plugins'
const { DocumentAddIcon, DocumentDownloadIcon } = icon.carbon
const uploadFileListRef = ref()
const { targetData } = useTargetData() const { targetData } = useTargetData()
const source = ref()
const dimensions = ref()
const code = toRaw((targetData.value.option as any).series) //
const tableTitle = ['字段', '映射', '状态']
const selcetOpeions = [ //
const selectOptions: SelectOptionsType[] = [
{ {
label: '静态数据', label: SelcetOptionsLableEnum.STATIC,
value: RequestDataTypeEnum.STATIC value: RequestDataTypeEnum.STATIC
}, },
{ {
label: '动态请求', label: SelcetOptionsLableEnum.AJAX,
value: RequestDataTypeEnum.AJAX value: RequestDataTypeEnum.AJAX,
disabled: true,
} }
] ]
const dataStructure = ref([
{
//
field: 'x',
//
mapping: 'xData',
//
result: true
},
{
//
field: 'y',
//
mapping: 'yData',
//
result: true
}
])
const updateHandle = (value: any) => { //
console.log(value) const getSource = computed(() => {
return JSON.stringify(source.value)
})
watch(() => targetData.value?.option?.dataset, (newData) => {
if (newData) {
source.value = newData.source
dimensions.value = newData.dimensions
}
}, {
immediate: true
})
//
const matchingHandle = (mapping: string) => {
for (let i = 0; i < source.value.length; i++) {
let res = DataResultEnum.FAILURE
if (source.value[i][mapping] !== undefined) {
return DataResultEnum.SUCCESS
}
return res
}
return DataResultEnum.SUCCESS
}
//
const getDimensionsAndSource = computed(() => {
//
return dimensions.value.map((item: string, index: number) => {
return index === 0 ?
{
//
field: '通用标识',
//
mapping: item,
//
result: DataResultEnum.NULL
} : {
field: `数据项-${index}`,
mapping: item,
result: matchingHandle(item)
}
})
})
//
const tableLoad = computed(() => {
return !getDimensionsAndSource.value || getDimensionsAndSource.value.length === 0
})
//
const selectHandle = () => { }
//@ts-ignore
const beforeUpload = ({ file }) => {
uploadFileListRef.value = []
const type = file.file.type
if (type !== FileTypeEnum.JSON && type !== FileTypeEnum.TXT) {
window['$message'].warning('仅支持上传 【JSON】 格式文件,请重新上传!')
return false
}
return true
}
//
const customRequest = (options: UploadCustomRequestOptions) => {
const { file } = options
nextTick(() => {
if (file.file) {
readFile(file.file).then((fileData: any) => {
targetData.value.option.dataset = JSON.parse(fileData)
});
} else {
window['$message'].error('导入数据失败,请稍后重试或联系管理员!')
}
})
}
//
const download = () => {
window['$message'].success('正在下载文件...')
downloadFile(getSource.value, undefined, 'json')
} }
</script> </script>
<style lang="scss" scoped></style> <style>
</style>
<style lang="scss" scoped>
@include go("chart-configurations-data") {
@include deep() {
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
}
.source-btn-box {
margin-top: 10px !important;
}
}
</style>