[update] filetable update

This commit is contained in:
peterlu14
2023-01-12 17:37:54 +08:00
parent 4d48e82997
commit 3dfef28777
11 changed files with 377 additions and 256 deletions
@@ -0,0 +1,27 @@
<template>
<div>
<div>
X-Axis
<va-select
v-model="channelStore.xAxisSelected"
:text-by="'name'"
:options="channelStore.xAxisOptions"
:no-options-text="'Select a file first.'"
></va-select>
</div>
<div>
Y-Axis
<va-select
v-model="channelStore.yAxisSelected"
:text-by="'name'"
:options="channelStore.xAxisOptions"
:no-options-text="'Select a file first.'"
></va-select>
</div>
</div>
</template>
<script setup lang="ts">
import { useChannelStore } from '@/stores/data-analysis/channel'
const channelStore = useChannelStore()
</script>
+3 -13
View File
@@ -17,11 +17,7 @@
<va-slider v-model="slopModeModel" label="1D window" track-label-visible :min="1" :max="50" />
</div>
<div v-else></div>
<div>
<p>{{ channel.xAxis }}</p>
<p>{{ channel.yAxis }}</p>
<p>{{ fileView.filesSelected }}</p>
</div>
<div></div>
</va-card-content>
<va-button class="mt-2" @click="filterData">Start</va-button>
@@ -38,12 +34,6 @@
import { useFileViewStore } from '@/stores/data-analysis/file-view'
import { Ref, ref, defineEmits, defineExpose } from 'vue'
// define emit
// const emit = defineEmits({
// 'filter-data': ({ mode, data_id, data_channel, data }) => {
// return { mode, data_id, data_channel, data }
// },
// })
const emit = defineEmits(['filter-data'])
const channel = useChannelStore()
@@ -79,8 +69,8 @@
// console.log(return_data)
emit('filter-data', {
mode: simpleSelectModel.value.id,
data_id: fileView.filesSelected,
data_channel: [channel.xAxis.id, channel.yAxis.id],
data_id: fileView.filesSelected.map((ele: any) => ele.idType),
data_channel: [channel.xAxisSelected.id, channel.yAxisSelected.id],
data: { persentage: VTModeModel.value, window: slopModeModel.value },
})
}
+22 -9
View File
@@ -1,25 +1,38 @@
<template>
<va-card class="">
<div class="h-200px">
<va-card class="h-200px overflow-auto">
<va-card-content>
<va-button @click="showModal">FILE</va-button>
<p>Meta: {{ fileView.filesSelected }}</p>
<p>X: {{ channel.xAxis }}</p>
<p>Y: {{ channel.yAxis }}</p>
<p>X: {{ channelStore.xAxisSelected.name }} Y: {{ channelStore.yAxisSelected.name }}</p>
<va-list class="py-2" fit>
<va-list-label> </va-list-label>
<template v-for="(file, i) in selectedFiles" :key="'item' + file.id">
<va-list-item>
<va-list-item-section>
<va-list-item-label>
{{ file.name }}
</va-list-item-label>
</va-list-item-section>
</va-list-item>
<va-list-separator v-if="i < selectedFiles.length - 1" :key="'separator' + i" class="my-1" fit />
</template>
</va-list>
<Suspense>
<FileModal ref="file_modal_ref"></FileModal>
</Suspense>
</div>
</va-card-content>
</va-card>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, reactive } from 'vue'
import { useChannelStore } from '@/stores/data-analysis/channel'
import { useFileViewStore } from '@/stores/data-analysis/file-view'
import FileModal from '@/components/data-analysis/FileModal.vue'
const channel = useChannelStore()
const fileView = useFileViewStore()
const channelStore = useChannelStore()
const fileViewStore = useFileViewStore()
const selectedFiles = reactive(fileViewStore.filesSelected)
/* File Modal Handling */
let file_modal_ref = ref<InstanceType<typeof FileModal> | null>(null)
+83 -185
View File
@@ -1,212 +1,110 @@
<template>
<div>
<va-modal v-model="show" :fixed-layout="true" size="large" @before-open="initLoad">
<!-- File Container-->
<div class="h-[75vh] w-[50vw]">
<!-- Control Bar -->
<div class="d-flex align-center">
<va-input v-model="filter" placeholder="Filter..." class="mr-3" style="flex: 0 200px" clearable />
<va-checkbox v-model="isFilterCaseSensitive" class="mr-3" label="Case sensitive" />
<va-button-group class="mr-3">
<va-button @click="changeFileView('time')">Time</va-button>
<va-button @click="changeFileView('folder')">Folder</va-button>
<va-button @click="changeFileView('project')">Project</va-button>
</va-button-group>
<va-button-group>
<va-button @click="expandAll(true)">Expand</va-button>
<va-button @click="expandAll(false)">Close</va-button>
</va-button-group>
</div>
<!-- File Window-->
<div class="grid grid-cols-4 gap-6">
<!-- File View-->
<div class="h-full col-span-3">
<va-tree-view
v-model:checked="selectedNodes"
v-model:expanded="expanedNodes"
:nodes="treeNodes"
class="customizable-content"
:filter="filter"
:filter-method="customFilterMethod"
:track-by="'idType'"
:value-by="'idType'"
selectable
>
<template #content="node">
<div class="align-center">
<va-icon v-if="node.type === 'Folder' && node.expanded === false" class="fas fa-folder mr-2" />
<va-icon v-if="node.type === 'Folder' && node.expanded === true" class="fas fa-folder-open mr-2" />
<va-icon v-if="node.type === 'MetaFile'" class="fas fa-file mr-2" />
<template v-if="node.type !== 'MetaFile'">{{ node.name }}</template>
<template v-if="node.type === 'MetaFile'">
{{ node.name }}
</template>
</div>
</template>
</va-tree-view>
</div>
<!-- Channel & Group setting-->
<div class="h-full col-span-1">
<div>
X-Axis
<va-select v-model="channel.xAxis" :text-by="'name'" :options="options"></va-select>
</div>
<div>
Y-Axis
<va-select v-model="channel.yAxis" :text-by="'name'" :options="options"></va-select>
</div>
</div>
</div>
<slideout v-model="showSlideOut" title="The title" :size="'120%'" dock="left" resizable>
<ChannelSelector></ChannelSelector>
<div class="pa-1 d-flex align-center">
<!-- <va-input v-model="filter" placeholder="Filter..." class="mr-3" style="flex: 0 200px" clearable />
<va-checkbox v-model="isFilterCaseSensitive" class="mr-3" label="Case sensitive" /> -->
<va-button-group class="mr-1">
<va-button @click="changeViewFormat(0)">Table</va-button>
<va-button disabled @click="changeViewFormat(1)">Tree</va-button>
</va-button-group>
<va-button-group class="mr-3">
<va-button @click="changeFileView('time')">Time</va-button>
<va-button @click="changeFileView('folder')">Folder</va-button>
<va-button @click="changeFileView('project')">Project</va-button>
</va-button-group>
<va-button-group>
<va-button @click="expandAll(true)">Expand</va-button>
<va-button @click="expandAll(false)">Close</va-button>
</va-button-group>
</div>
<!-- <div v-if="loading">Loading...</div> -->
<va-inner-loading :loading="loading">
<div>
<TableFormat v-if="viewFormat === 0" ref="table_format_ref" :columns="columns" :rows="rows"></TableFormat>
<TreeFormat v-if="viewFormat === 1" ref="table_format_ref" :columns="columns" :rows="rows"></TreeFormat>
</div>
</va-modal>
</div>
</va-inner-loading>
</slideout>
</template>
<script setup lang="ts">
import { Ref, ref, computed, watch } from 'vue'
import { FileView, Project, MetaFile, Folder, Time } from '@/utils/file'
import configTable from '@/data/config-table/index'
import { useChannelStore } from '@/stores/data-analysis/channel'
import { ref, reactive } from 'vue'
import { useFileViewStore } from '@/stores/data-analysis/file-view'
import TableFormat from './TableFormat.vue'
import TreeFormat from './TreeFormat.vue'
import ChannelSelector from './ChannelSelector.vue'
// emit
const emit = defineEmits({
filesSelected: ({ metas, channel }) => {
return { metas, channel }
},
})
function emitFilesSelected(meta: string[], channel: { x: object; y: object }) {
emit('filesSelected', { meta, channel })
}
// filter function
const customFilterMethod = computed(() => {
return (node: Project | MetaFile, filterText: string) => {
// console.log('customFilterMethod', node, filterText, key, node.name.includes(filterText))
return node.name.includes(filterText)
}
})
// handling show modal
let show = ref(false)
const showModal = function () {
show.value = !show.value
}
// handling fileView
const fileViewStore = useFileViewStore()
const fileView = new FileView()
const fileView = reactive(fileViewStore.fileView)
const table_format_ref = ref<InstanceType<typeof TableFormat> | null>(null)
const rows: any = reactive([])
const columns = reactive([
{
label: 'Name',
field: 'name',
},
{
label: 'TotalTime',
field: 'time',
type: 'number',
},
{
label: 'Size',
field: 'size',
type: 'number',
},
])
// handling tree-view
const filter = ref('')
const isFilterCaseSensitive = ref(false)
const selectedNodes: Ref<(never | string)[]> = ref([])
const expanedNodes: Ref<(never | string)[]> = ref([])
const treeNodes: Ref<(never | Project | Folder | Time | MetaFile)[]> = ref([])
const loading = ref(true)
const showSlideOut = ref(false)
const showModal = function () {
showSlideOut.value = !showSlideOut.value
initLoad()
console.log('fileView', fileView.getChildren())
console.log('rows', rows)
setTimeout(() => {
loading.value = false
}, 1500)
}
const initLoad = async function () {
if (treeNodes.value.length === 0) {
await initLoadFileView()
initLoadNodes()
}
rows.length = 0
rows.push(...fileView.getChildren())
// if (rows.length < fileView.children.length) {
// rows.push(...fileView.children.slice(rows.length, fileView.children.length - 1))
// }
// for (const index in rows) {
// console.log(index, rows[index].children.length, fileView.children[index].children.length)
// if (rows[index].children.length < fileView.children[index].children.length) {
// rows[index].children.push(
// ...fileView.children[index].children.slice(
// rows[index].children.length,
// fileView.children[index].children.length - 1,
// ),
// )
// }
// }
}
const initLoadFileView = async function () {
await fileView.appendChildren(1)
const viewFormat = ref(0)
const changeViewFormat = function (view: number) {
viewFormat.value = view
}
const initLoadNodes = function () {
const files = fileView.getChildren()
if (files.length > 0) {
treeNodes.value.push(...files)
expanedNodes.value.push(treeNodes.value[0].idType)
}
}
const reset = function () {
resetFileView()
resetNodes()
}
const resetFileView = function () {
fileView.reset()
}
const resetNodes = function () {
selectedNodes.value.length = 0
expanedNodes.value.length = 0
treeNodes.value.length = 0
}
// handling modal header
const changeFileView = async function (view: string) {
// reset fileView
fileView.changeView(view)
await fileView.appendChildren(1)
// reset treeNodes & expandedNodes & selectedNodes
treeNodes.value.length = 0
treeNodes.value.push(...fileView.getChildren())
expanedNodes.value.length = 0
expanedNodes.value.push(treeNodes.value[0].idType)
// selectedNodes.value.length = 0
initLoad()
}
const expandAll = function (expandOrNot: boolean) {
expanedNodes.value.length = 0
if (!expandOrNot) return
for (const node of treeNodes.value) {
expanedNodes.value.push(node.idType)
}
if (expandOrNot === true) table_format_ref.value?.expandAll()
if (expandOrNot === false) table_format_ref.value?.collapseAll()
}
// handling channel options
const channel = useChannelStore()
let options: any[] = []
// watch selectedNodes to decide axis options
watch(selectedNodes, (newSelectedNodes, oldSelectedNodes) => {
// update fileViewStore.filesSelected
fileViewStore.filesSelected.length = 0
fileViewStore.filesSelected.push(...newSelectedNodes.filter((x) => x.includes('MetaFile')))
// 新的要filter掉舊的沒有的
const newNodes = newSelectedNodes.filter((x) => x.includes('MetaFile') && !oldSelectedNodes.includes(x))
const MetFileList = []
for (const node of newNodes) {
const meta = fileView.getMetaFile(node)
MetFileList.push(meta)
}
console.log('MetFileList', MetFileList)
if (MetFileList[0]) {
// console.log(MetFileList[0].device.library_name, MetFileList[0].parameter.MODE)
// console.log(configTable.getConfig(MetFileList[0].device.library_name))
const config: any = configTable.getModeConfig(MetFileList[0].device.library_name, MetFileList[0].parameter.MODE)
const channel = Object.values(config.channels)
const channelOptions = channel.map((v: any, idx) => {
v.id = idx
return v
})
console.log('channelOptions', channelOptions)
options.length = 0
options.push(...channelOptions)
}
})
defineExpose({
emitFilesSelected,
show,
showModal,
changeFileView,
expandAll,
reset,
initLoad,
treeNodes,
expanedNodes,
selectedNodes,
})
</script>
<style lang="scss"></style>
@@ -0,0 +1,82 @@
<template>
<VueGoodTable
ref="vue_good_table_refs"
:columns="columns"
:rows="rows"
:select-options="{ enabled: true, selectAllByGroup: true }"
:search-options="{ enabled: true }"
:group-options="{
enabled: true,
collapsable: true,
rowKey: 'id',
}"
@selected-rows-change="selectionChanged"
>
</VueGoodTable>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, watchEffect, toRef } from 'vue'
import { useFileViewStore } from '@/stores/data-analysis/file-view'
import { useChannelStore } from '@/stores/data-analysis/channel'
import configTable from '@/data/config-table/index'
const props = defineProps({
columns: {
type: Array,
default: () => {
return []
},
},
rows: {
type: Array,
default: () => {
return []
},
},
})
const fileViewStore = useFileViewStore()
const channelStore = useChannelStore()
const vue_good_table_refs = ref()
// const vue_good_table_refs = ref<InstanceType<typeof VueGoodTable> | null>(null)
const expandAll = function () {
vue_good_table_refs.value.expandAll()
}
const collapseAll = function () {
vue_good_table_refs.value.collapseAll()
}
const selectionChanged = function (params: any) {
fileViewStore.filesSelected.length = 0
fileViewStore.filesSelected.push(...params.selectedRows)
if (fileViewStore.filesSelected.length > 0) {
const config: any = configTable.getModeConfig(
fileViewStore.filesSelected[0].device.library_name,
fileViewStore.filesSelected[0].parameter.MODE,
)
const channel = Object.values(config.channels)
const channelOptions = channel.map((v: any, idx) => {
v.id = idx
return v
})
channelStore.xAxisOptions.length = 0
channelStore.xAxisOptions.push(...channelOptions)
channelStore.yAxisOptions.length = 0
channelStore.yAxisOptions.push(...channelOptions)
}
console.log('vue_good_table_refs.value', vue_good_table_refs.value)
}
onMounted(() => {
vue_good_table_refs.value.toggleSelectGroup = function (event: { checked: any }, headerRow: { children: any[] }) {
console.log(event, headerRow)
headerRow.children.forEach((row: { [x: string]: any }) => {
row['vgtSelected'] = event.checked
})
}
})
defineExpose({ selectionChanged, expandAll, collapseAll })
</script>
<style></style>
@@ -0,0 +1,55 @@
<template>
<va-tree-view
v-model:checked="selectedNodes"
v-model:expanded="expanedNodes"
:nodes="rows"
class="customizable-content"
:filter="filter"
:filter-method="customFilterMethod"
:track-by="'idType'"
:value-by="'idType'"
selectable
>
<template #content="node">
<div class="align-center">
<va-icon v-if="node.type === 'Folder' && node.expanded === false" class="fas fa-folder mr-2" />
<va-icon v-if="node.type === 'Folder' && node.expanded === true" class="fas fa-folder-open mr-2" />
<va-icon v-if="node.type === 'MetaFile'" class="fas fa-file mr-2" />
<template v-if="node.type !== 'MetaFile'">{{ node.name }}</template>
<template v-if="node.type === 'MetaFile'">
{{ node.name }}
</template>
</div>
</template>
</va-tree-view>
</template>
<script setup lang="ts">
import { Ref, ref, computed } from 'vue'
defineProps({
columns: {
type: Array,
default: () => {
return []
},
},
rows: {
type: Array,
default: () => {
return []
},
},
})
const filter = ref('')
const selectedNodes: Ref<(never | string)[]> = ref([])
const expanedNodes: Ref<(never | string)[]> = ref([])
// filter function
const customFilterMethod = computed(() => {
return (node: any, filterText: string) => {
// console.log('customFilterMethod', node, filterText, key, node.name.includes(filterText))
return node.name.includes(filterText)
}
})
</script>
+18 -6
View File
@@ -23,20 +23,32 @@ const getMetaWithTypeFolder = async function (
return result
}
const getMetaByTime = async function (time: string): Promise<AxiosResponse<any, any>> {
const url = `api/file/meta/get?time=${time}`
const getMetaByTime = async function (time: string, offset?: number, limit?: number): Promise<AxiosResponse<any, any>> {
let url = `api/file/meta/get?time=${time}`
if (offset !== undefined) url += `&offset=${offset}`
if (limit !== undefined) url += `&limit=${limit}`
console.log('url', url)
const result = await axios.get(url)
return result
}
const getMetaGroupByTime = async function (): Promise<AxiosResponse<any, any>> {
const url = `api/file/meta/get?group=time`
const getMetaGroupByTime = async function (offset?: number, limit?: number): Promise<AxiosResponse<any, any>> {
let url = `api/file/meta/get?group=time`
if (offset !== undefined) url += `&offset=${offset}`
if (limit !== undefined) url += `&limit=${limit}`
const result = await axios.get(url)
return result
}
const getMetaByProject = async function (uuid: string): Promise<AxiosResponse<any, any>> {
const url = `api/file/meta/get?type=project&project=${uuid}`
const getMetaByProject = async function (
uuid: string,
offset?: number,
limit?: number,
): Promise<AxiosResponse<any, any>> {
let url = `api/file/meta/get?type=project&project=${uuid}`
if (offset !== undefined) url += `&offset=${offset}`
if (limit !== undefined) url += `&limit=${limit}`
const result = await axios.get(url)
return result
}
@@ -32,6 +32,7 @@
const url = `http://192.168.2.1:3000/api/analysis/get/csv?name=${name}`
const a = document.createElement('a')
a.href = url
a.target = '_blank'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
+8 -4
View File
@@ -9,14 +9,18 @@ interface AxisType {
}
interface ChannelState {
xAxis: AxisType | Record<string, never>
yAxis: AxisType | Record<string, never>
xAxisSelected: AxisType | Record<string, never>
yAxisSelected: AxisType | Record<string, never>
xAxisOptions: Array<string>
yAxisOptions: Array<string>
}
export const useChannelStore = defineStore('data-analysis-channel', {
state: (): ChannelState => ({
xAxis: {},
yAxis: {},
xAxisSelected: {},
yAxisSelected: {},
xAxisOptions: [],
yAxisOptions: [],
}),
getters: {},
actions: {},
+3 -7
View File
@@ -1,9 +1,9 @@
import { defineStore } from 'pinia'
import { FileView } from '@/utils/file'
import { FileView, MetaFile } from '@/utils/file'
interface FileViewState {
fileView: FileView
filesSelected: (string | never)[]
filesSelected: (MetaFile | never)[]
}
export const useFileViewStore = defineStore('data-analysis-file-view', {
@@ -14,9 +14,5 @@ export const useFileViewStore = defineStore('data-analysis-file-view', {
// could also be defined as
// state: () => ({ count: 0 })
getters: {},
actions: {
setFilesSelect(fileSelect: string[]) {
this.filesSelected.push(...fileSelect)
},
},
actions: {},
})
+75 -32
View File
@@ -10,51 +10,64 @@ class FileView {
viewBy: string
offset: number
limit: number
max: number
totalFiles: number
constructor() {
this.children = []
this.viewBy = 'time'
this.offset = 0
this.limit = 20
this.max = 0
this.totalFiles = 0
this.appendChildren()
}
init() {
this.children.length = 0
this.offset = 0
this.limit = 20
}
reset() {
this.children.length = 0
this.offset = 0
this.limit = 20
this.max = 0
this.totalFiles = 0
}
changeView(viewBy: string) {
this.reset()
this.init()
this.viewBy = viewBy
}
async appendChildren(id: number) {
async appendChildren(id?: number) {
if (this.viewBy === 'folder') {
// append children folder
const folderResult = await this.getCollectionsFromServer(id)
for (const _data of folderResult.rows) {
const folder = new Folder(_data.id, _data.name)
// console.log('folder', folder)
await folder.init()
this.children.push(folder)
if (id) {
const folderResult = await this.getCollectionsFromServer(id)
for (const _data of folderResult.rows) {
const folder = new Folder(_data.id, _data.name)
// console.log('folder', folder)
await folder.init()
this.children.push(folder)
}
}
} else if (this.viewBy == 'time') {
const timeGroup = await this.getTimeGroupFromServer()
for (const time in timeGroup) {
const timeInstance = new Time(time, timeGroup[time]?.date)
await timeInstance.appendChildren()
this.children.push(timeInstance)
this.max = timeGroup.counts[0].GroupCount
if (this.offset < this.max) {
for (const time in timeGroup.rows) {
const timeInstance = new Time(time, timeGroup.rows[time]?.date, Number(timeGroup.rows[time]?.count))
// while (timeInstance.offset < timeInstance.max) {
// await timeInstance.appendChildreWithLimit()
// }
this.children.push(timeInstance)
}
for (const child of this.children) {
await (child as Time).appendChildreWithLimit()
}
this.offset += this.max
}
} else if (this.viewBy == 'project') {
const projectGroup = await this.getProjectGroupFromServer(['id', 'name', 'uuid', 'created_at'])
for (const project of projectGroup) {
for (const project of projectGroup.rows) {
const projectInstance = new Project(project.id, project.name, project.uuid, project.created_at)
await projectInstance.appendChildren()
this.children.push(projectInstance)
@@ -73,8 +86,8 @@ class FileView {
return result.data
}
async getTimeGroupFromServer() {
const result = await api.meta.getMetaGroupByTime()
async getTimeGroupFromServer(offset?: number, limit?: number) {
const result = await api.meta.getMetaGroupByTime(offset, limit)
return result.data
}
@@ -84,7 +97,7 @@ class FileView {
}
getChildren() {
return this.children
return JSON.parse(JSON.stringify(this.children))
}
getMetaFile(idType: string) {
@@ -105,21 +118,25 @@ class Time {
children: Array<Time | MetaFile>
offset: number
limit: number
maxFiles: number
max: number
loading: boolean
expanded: boolean
constructor(id: string, date: string) {
constructor(id: string, date: string, max: number) {
this.id = id
this.type = 'Time'
this.idType = 'Time-' + id
this.name = date
this.children = []
this.offset = 0
this.limit = 20
this.maxFiles = 0
this.limit = 1000
this.max = max
this.loading = false
this.expanded = false
}
async getFilesFromServer(id: string) {
const result = await api.meta.getMetaByTime(id)
async getFilesFromServer(id: string, offset?: number, limit?: number) {
const result = await api.meta.getMetaByTime(id, offset, limit)
return result.data
}
@@ -143,7 +160,33 @@ class Time {
_metaFile.created_at,
)
this.children.push(meta)
// console.log('time', meta)
}
}
async appendChildreWithLimit() {
if (this.offset < this.max) {
console.log('appendChildreWithLimit', this.offset, this.limit)
const metaFile = await this.getFilesFromServer(this.name, this.offset, this.limit)
for (const _metaFile of metaFile.rows) {
const channels =
_metaFile.channels === null || _metaFile.channels === '' ? _metaFile.channels : JSON.parse(_metaFile.channels)
const meta = new MetaFile(
_metaFile.id,
_metaFile.uuid,
_metaFile.name,
channels,
_metaFile.parent,
_metaFile.device,
_metaFile.parameter_set,
_metaFile.raw_data,
_metaFile.mini_data,
_metaFile.size,
_metaFile.time_duration,
_metaFile.created_at,
)
this.children.push(meta)
}
this.offset += this.limit
}
}
@@ -185,8 +228,8 @@ class Project {
this.maxFiles = 0
}
async getFilesFromServer(id: string) {
const result = await api.meta.getMetaByProject(id)
async getFilesFromServer(id: string, offset?: number, limit?: number) {
const result = await api.meta.getMetaByProject(id, offset, limit)
return result.data
}
@@ -393,4 +436,4 @@ interface Device {
status: number
}
export { FileView, Project, MetaFile, Folder, Time }
export { FileView, Project, MetaFile, Folder, Time, Device }