Commit b5c25409 by User

表格搜索区域和数据源、配置获取优化

parent 74076394
# backend service base url, test environment
VITE_SERVICE_BASE_URL='http://localhost:80'
VITE_SERVICE_BASE_URL='http://datav.kivii.org'
# other backend service base url, test environment
VITE_OTHER_SERVICE_BASE_URL= `{
......
<script setup lang="ts">
import { inject } from 'vue';
import { computed, inject } from 'vue';
defineOptions({
name: 'SearchForm'
});
......@@ -19,150 +19,114 @@ const toggleExpand = () => {
const openSettings = () => {
tableState.showSettingDrawer.value = true;
};
</script>
<template>
<NCard class="search-card" :bordered="false" content-style="padding: 0 12px 8px;margin-top:10px">
<NGrid :cols="6" x-gap="8" y-gap="8">
<!-- 默认显示前5个表单项 -->
<NGridItem v-for="item in formItems.slice(0, 5)" :key="item.key">
<NFormItem :label="item.label" label-placement="left">
<NInput
v-if="item.type === 'input'"
v-model:value="searchForm[item.key]"
:placeholder="item.placeholder"
></NInput>
<NSelect
v-if="item.type === 'select'"
v-model:value="searchForm[item.key]"
:options="item.options"
:placeholder="item.placeholder"
></NSelect>
<NDatePicker
v-if="item.type === 'date'"
v-model:value="searchForm[item.key]"
type="daterange"
:placeholder="item.placeholder"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
// 计算是否需要显示展开按钮(字段超过5个时才显示)
const showExpandButton = computed(() => {
return formItems.value && formItems.value.length > 5;
});
<!-- 未展开时的操作按钮 -->
<NGridItem v-if="!isExpanded">
<div class="operation-buttons">
<NSpace :size="8" align="center" :wrap="false">
<NButton type="primary" size="small" @click="() => handleCreate(null)">新建</NButton>
<NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton>
// 计算当前显示的字段数量
const visibleFields = computed(() => {
if (!formItems.value) return [];
return isExpanded.value ? formItems.value : formItems.value.slice(0, 5);
});
<NButton size="small" @click="toggleExpand">
<template #icon>
<NIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path d="M18 6.41L16.59 5L12 9.58L7.41 5L6 6.41l6 6z" fill="currentColor"></path>
<path d="M18 13l-1.41-1.41L12 16.17l-4.59-4.58L6 13l6 6z" fill="currentColor"></path>
</svg>
</NIcon>
</template>
</NButton>
// 计算第一行显示的字段(如果折叠且超过5个字段,第一行显示5个字段,操作按钮在下一行)
const firstRowFields = computed(() => {
if (!formItems.value) return [];
<NButton size="small" title="刷新数据" @click="fetchData">
<template #icon>
<NIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill="currentColor"
></path>
</svg>
</NIcon>
</template>
</NButton>
<NButton size="small" @click="openSettings">
<template #icon>
<NIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
opacity=".3"
d="M19.28 8.6l-.7-1.21l-1.27.51l-1.06.43l-.91-.7c-.39-.3-.8-.54-1.23-.71l-1.06-.43l-.16-1.13L12.7 4h-1.4l-.19 1.35l-.16 1.13l-1.06.44c-.41.17-.82.41-1.25.73l-.9.68l-1.05-.42l-1.27-.52l-.7 1.21l1.08.84l.89.7l-.14 1.13c-.03.3-.05.53-.05.73s.02.43.05.73l.14 1.13l-.89.7l-1.08.84l.7 1.21l1.27-.51l1.06-.43l.91.7c.39.3.8.54 1.23.71l1.06.43l.16 1.13l.19 1.36h1.39l.19-1.35l.16-1.13l1.06-.43c.41-.17.82-.41 1.25-.73l.9-.68l1.04.42l1.27.51l.7-1.21l-1.08-.84l-.89-.7l.14-1.13c.04-.31.05-.52.05-.73c0-.21-.02-.43-.05-.73l-.14-1.13l.89-.7l1.1-.84zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4s4 1.79 4 4s-1.79 4-4 4z"
fill="currentColor"
></path>
<path
d="M19.43 12.98c.04-.32.07-.64.07-.98c0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.566.566 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.42.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73c0 .21-.02.43-.05.73l-.14 1.13l.89.7l1.08.84l-.7 1.21l-1.27-.51l-1.04-.42l-.9.68c-.43.32-.84.56-1.25.73l-1.06.43l-.16 1.13l-.2 1.35h-1.4l-.19-1.35l-.16-1.13l-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7l-1.06.43l-1.27.51l-.7-1.21l1.08-.84l.89-.7l-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13l-.89-.7l-1.08-.84l.7-1.21l1.27.51l1.04.42l.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43l.16-1.13l.2-1.35h1.39l.19 1.35l.16 1.13l1.06.43c.43.18.83.41 1.23.71l.91.7l1.06-.43l1.27-.51l.7 1.21l-1.07.85l-.89.7l.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4s4-1.79 4-4s-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2s2 .9 2 2s-.9 2-2 2z"
fill="currentColor"
></path>
</svg>
</NIcon>
</template>
</NButton>
</NSpace>
</div>
</NGridItem>
// 如果字段数量 <= 5,或者已展开,正常显示
if (formItems.value.length <= 5 || isExpanded.value) {
return visibleFields.value;
}
<!-- 展开后的额外表单项 -->
<template v-if="isExpanded">
<NGridItem v-for="item in formItems.slice(5)" :key="item.key">
<NFormItem :label="item.label" label-placement="left">
<NInput
v-if="item.type === 'input'"
v-model:value="searchForm[item.key]"
:placeholder="item.placeholder"
></NInput>
<NSelect
v-if="item.type === 'select'"
v-model:value="searchForm[item.key]"
:options="item.options"
:placeholder="item.placeholder"
></NSelect>
<NDatePicker
v-if="item.type === 'date'"
v-model:value="searchForm[item.key]"
type="daterange"
:placeholder="item.placeholder"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
// 如果字段数量 > 5 且折叠状态,第一行显示5个字段
return formItems.value.slice(0, 5);
});
<!-- 展开后的操作按钮,独占一行并靠左对齐 -->
<NGridItem :span="6">
<div class="operation-buttons-expanded">
<NSpace :size="8" align="center" :wrap="false">
<NButton size="small" @click="() => handleCreate(null)">新建</NButton>
<NButton size="small" @click="handleSearch">查询</NButton>
// 计算是否需要在第一行显示操作按钮
const showActionsInFirstRow = computed(() => {
return false; // 操作按钮总是单独占一行
});
</script>
<template>
<NCard class="search-card" :bordered="false" content-style="padding: 12px;">
<!-- 顶部配置按钮区域 -->
<div class="top-actions">
<NSpace :size="8" align="center">
<NButton type="primary" size="small" @click="() => handleCreate(null)">新建</NButton>
<NButton size="small" title="刷新数据" @click="fetchData">
<template #icon>
<NIcon>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
<path
d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill="currentColor"
></path>
</svg>
</NIcon>
</template>
</NButton>
<NButton size="small" @click="openSettings">
<template #icon>
<NIcon>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
<path
opacity=".3"
d="M19.28 8.6l-.7-1.21l-1.27.51l-1.06.43l-.91-.7c-.39-.3-.8-.54-1.23-.71l-1.06-.43l-.16-1.13L12.7 4h-1.4l-.19 1.35l-.16 1.13l-1.06.44c-.41.17-.82.41-1.25.73l-.9.68l-1.05-.42l-1.27-.52l-.7 1.21l1.08.84l.89.7l-.14 1.13c-.03.3-.05.53-.05.73s.02.43.05.73l.14 1.13l-.89.7l-1.08.84l.7 1.21l1.27-.51l1.06-.43l.91.7c.39.3.8.54 1.23.71l1.06.43l.16 1.13l.19 1.36h1.39l.19-1.35l.16-1.13l1.06-.43c.41-.17.82-.41 1.25-.73l.9-.68l1.04.42l1.27.51l.7-1.21l-1.08-.84l-.89-.7l.14-1.13c.04-.31.05-.52.05-.73c0-.21-.02-.43-.05-.73l-.14-1.13l.89-.7l1.1-.84zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4s4 1.79 4 4s-1.79 4-4 4z"
fill="currentColor"
></path>
<path
d="M19.43 12.98c.04-.32.07-.64.07-.98c0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.566.566 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.42.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73c0 .21-.02.43-.05.73l-.14 1.13l.89.7l1.08.84l-.7 1.21l-1.27-.51l-1.04-.42l-.9.68c-.43.32-.84.56-1.25.73l-1.06.43l-.16 1.13l-.2 1.35h-1.4l-.19-1.35l-.16-1.13l-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7l-1.06.43l-1.27.51l-.7-1.21l1.08-.84l.89-.7l-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13l-.89-.7l-1.08-.84l.7-1.21l1.27.51l1.04.42l.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43l.16-1.13l.2-1.35h1.39l.19 1.35l.16 1.13l1.06.43c.43.18.83.41 1.23.71l.91.7l1.06-.43l1.27-.51l.7 1.21l-1.07.85l-.89.7l.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4s4-1.79 4-4s-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2s2 .9 2 2s-.9 2-2 2z"
fill="currentColor"
></path>
</svg>
</NIcon>
</template>
</NButton>
</NSpace>
</div>
<!-- 搜索表单区域 -->
<div class="search-form-area">
<NGrid :cols="24" x-gap="12" y-gap="12">
<!-- 第一行:搜索字段 + 操作按钮(如果需要) -->
<template v-for="item in firstRowFields" :key="item.key">
<NGridItem :span="4">
<NFormItem :label="item.label" label-placement="left">
<NInput
v-if="item.type === 'input'"
v-model:value="searchForm[item.key]"
:placeholder="item.placeholder"
size="small"
></NInput>
<NSelect
v-if="item.type === 'select'"
v-model:value="searchForm[item.key]"
:options="item.options"
:placeholder="item.placeholder"
size="small"
></NSelect>
<NDatePicker
v-if="item.type === 'date'"
v-model:value="searchForm[item.key]"
type="daterange"
:placeholder="item.placeholder"
size="small"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
</template>
<!-- 第一行的操作按钮(当折叠且字段超过5个时) -->
<NGridItem v-if="showActionsInFirstRow" :span="8">
<div class="action-buttons">
<NSpace :size="8">
<NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton>
<NButton size="small" @click="toggleExpand">
<template #icon>
<NIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 16 16"
>
<g fill="none">
<path
d="M4.26 8.3a.75.75 0 1 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 4.773L4.26 8.3zm0 4a.75.75 0 0 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 8.773L4.26 12.3z"
fill="currentColor"
></path>
</g>
</svg>
</NIcon>
</template>
</NButton>
<NButton size="small" title="刷新数据" @click="fetchData">
<NButton v-if="showExpandButton" size="small" @click="toggleExpand">
<template #icon>
<NIcon>
<svg
......@@ -170,15 +134,82 @@ const openSettings = () => {
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill="currentColor"
></path>
<path d="M18 6.41L16.59 5L12 9.58L7.41 5L6 6.41l6 6z" fill="currentColor"></path>
<path d="M18 13l-1.41-1.41L12 16.17l-4.59-4.58L6 13l6 6z" fill="currentColor"></path>
</svg>
</NIcon>
</template>
</NButton>
<NButton size="small" @click="openSettings">
</NSpace>
</div>
</NGridItem>
<!-- 展开后的额外字段(如果已展开且有超过5个字段) -->
<template v-if="isExpanded && formItems && formItems.length > 5">
<template v-for="item in formItems.slice(5)" :key="item.key">
<NGridItem :span="4">
<NFormItem :label="item.label" label-placement="left">
<NInput
v-if="item.type === 'input'"
v-model:value="searchForm[item.key]"
:placeholder="item.placeholder"
size="small"
></NInput>
<NSelect
v-if="item.type === 'select'"
v-model:value="searchForm[item.key]"
:options="item.options"
:placeholder="item.placeholder"
size="small"
></NSelect>
<NDatePicker
v-if="item.type === 'date'"
v-model:value="searchForm[item.key]"
type="daterange"
:placeholder="item.placeholder"
size="small"
clearable
></NDatePicker>
</NFormItem>
</NGridItem>
</template>
<!-- 展开状态下的操作按钮 -->
<NGridItem :span="4">
<div class="action-buttons">
<NSpace :size="8">
<NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton>
<NButton size="small" @click="toggleExpand">
<template #icon>
<NIcon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 16 16"
>
<g fill="none">
<path
d="M4.26 8.3a.75.75 0 1 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 4.773L4.26 8.3zm0 4a.75.75 0 0 1-1.02-1.1l4.25-4a.75.75 0 0 1 1.02 0l4.25 4a.75.75 0 1 1-1.02 1.1L8 8.773L4.26 12.3z"
fill="currentColor"
></path>
</g>
</svg>
</NIcon>
</template>
</NButton>
</NSpace>
</div>
</NGridItem>
</template>
<!-- 折叠状态下的操作按钮(当字段超过5个且折叠时) -->
<NGridItem v-if="!isExpanded && showExpandButton" :span="4">
<div class="action-buttons">
<NSpace :size="8">
<NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton>
<NButton size="small" @click="toggleExpand">
<template #icon>
<NIcon>
<svg
......@@ -186,15 +217,8 @@ const openSettings = () => {
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
opacity=".3"
d="M19.28 8.6l-.7-1.21l-1.27.51l-1.06.43l-.91-.7c-.39-.3-.8-.54-1.23-.71l-1.06-.43l-.16-1.13L12.7 4h-1.4l-.19 1.35l-.16 1.13l-1.06.44c-.41.17-.82.41-1.25.73l-.9.68l-1.05-.42l-1.27-.52l-.7 1.21l1.08.84l.89.7l-.14 1.13c-.03.3-.05.53-.05.73s.02.43.05.73l.14 1.13l-.89.7l-1.08.84l.7 1.21l1.27-.51l1.06-.43l.91.7c.39.3.8.54 1.23.71l1.06.43l.16 1.13l.19 1.36h1.39l.19-1.35l.16-1.13l1.06-.43c.41-.17.82-.41 1.25-.73l.9-.68l1.04.42l1.27.51l.7-1.21l-1.08-.84l-.89-.7l.14-1.13c.04-.31.05-.52.05-.73c0-.21-.02-.43-.05-.73l-.14-1.13l.89-.7l1.1-.84zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4s4 1.79 4 4s-1.79 4-4 4z"
fill="currentColor"
></path>
<path
d="M19.43 12.98c.04-.32.07-.64.07-.98c0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.566.566 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.42.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73c0 .21-.02.43-.05.73l-.14 1.13l.89.7l1.08.84l-.7 1.21l-1.27-.51l-1.04-.42l-.9.68c-.43.32-.84.56-1.25.73l-1.06.43l-.16 1.13l-.2 1.35h-1.4l-.19-1.35l-.16-1.13l-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7l-1.06.43l-1.27.51l-.7-1.21l1.08-.84l.89-.7l-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13l-.89-.7l-1.08-.84l.7-1.21l1.27.51l1.04.42l.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43l.16-1.13l.2-1.35h1.39l.19 1.35l.16 1.13l1.06.43c.43.18.83.41 1.23.71l.91.7l1.06-.43l1.27-.51l.7 1.21l-1.07.85l-.89.7l.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4s4-1.79 4-4s-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2s2 .9 2 2s-.9 2-2 2z"
fill="currentColor"
></path>
<path d="M18 6.41L16.59 5L12 9.58L7.41 5L6 6.41l6 6z" fill="currentColor"></path>
<path d="M18 13l-1.41-1.41L12 16.17l-4.59-4.58L6 13l6 6z" fill="currentColor"></path>
</svg>
</NIcon>
</template>
......@@ -202,8 +226,18 @@ const openSettings = () => {
</NSpace>
</div>
</NGridItem>
</template>
</NGrid>
<!-- 字段数量 <= 5 时的操作按钮 -->
<NGridItem v-if="!showExpandButton" :span="4">
<div class="action-buttons">
<NSpace :size="8">
<NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton>
</NSpace>
</div>
</NGridItem>
</NGrid>
</div>
</NCard>
</template>
......@@ -212,22 +246,16 @@ const openSettings = () => {
margin-bottom: 8px;
}
.operation-buttons {
.top-actions {
display: flex;
align-items: center;
height: 100%;
padding-bottom: 2px;
}
.operation-buttons-expanded {
display: flex;
align-items: center;
padding: 8px 0 0 0;
justify-content: flex-end;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--n-border-color);
}
:deep(.n-space) {
flex-wrap: nowrap;
.search-form-area {
margin-top: 8px;
}
:deep(.n-form-item) {
......@@ -235,29 +263,24 @@ const openSettings = () => {
}
:deep(.n-form-item-label) {
width: 120px;
width: 100px;
justify-content: flex-end;
padding: 0;
margin-right: 4px;
text-align: right;
white-space: normal;
padding-right: 8px;
font-size: 14px;
color: var(--n-text-color-2);
align-items: flex-start;
display: flex;
height: 28px;
padding-top: 5px;
}
:deep(.n-form-item-label label) {
display: inline-block;
text-align: right;
width: 100%;
}
:deep(.n-form-item-label label:after) {
content: '';
display: block;
text-align: left;
line-height: 1;
margin: 0;
}
:deep(.n-form-item-blank) {
min-height: 28px;
justify-content: flex-end;
}
:deep(.n-input) {
......@@ -275,4 +298,15 @@ const openSettings = () => {
:deep(.n-form-item-feedback-wrapper) {
display: none;
}
:deep(.n-card__content) {
padding: 12px !important;
}
.action-buttons {
margin-left: 0px; /* 100px label宽度 + 8px padding-right */
height: 28px;
display: flex;
align-items: center;
}
</style>
......@@ -196,9 +196,9 @@ const applySettings = (settings: any) => {
});
}
// 如果有转换规则,自动应用转换
// 如果有转换规则,自动应用转换(静默模式,避免在数据未加载时显示错误)
if (settings.transformSettings.transformRules?.length > 0) {
tableMethods.applyTransformations();
tableMethods.applyTransformations(true);
}
}
};
......@@ -208,10 +208,11 @@ const handleGetSettings = async () => {
try {
// 在请求前检查必要的配置参数
if (!uiConfig.GetUrl || !uiConfig.Type) {
// console.warn('缺少必要的配置参数,无法获取配置');
return;
console.log('📋 缺少必要的配置参数,无法获取配置,需要调用字段匹配接口');
return false; // 返回false表示没有获取到配置
}
console.log('📋 尝试从 GetUrl 获取配置:', uiConfig.GetUrl);
const response = await axios.post(uiConfig.GetUrl, {
Type: uiConfig.Type,
InternalCode: uiConfig.InternalCode,
......@@ -220,15 +221,18 @@ const handleGetSettings = async () => {
if (response.data?.Parameters) {
settings.value = JSON.parse(response.data.Parameters);
// console.log('获取到的配置信息:', settings.value);
console.log('✅ 获取到的配置信息:', settings.value);
applySettings(settings.value);
// window.$message?.success('配置信息获取成功');
console.log('✅ 配置信息获取成功,跳过字段匹配接口');
return true; // 返回true表示成功获取到配置
} else {
// console.log('没有获取到配置参数,使用默认配置');
console.log('⚠️ 没有获取到配置参数,需要调用字段匹配接口');
return false; // 返回false表示没有获取到配置
}
} catch (error) {
// console.error('获取配置失败:', error);
window.$message?.error('获取配置失败');
console.error('❌ 获取配置失败:', error);
// window.$message?.error('获取配置失败');
return false; // 返回false表示获取配置失败
}
};
......
<script setup lang="ts">
/* eslint-disable no-underscore-dangle */
import { computed, defineProps, h, nextTick, onActivated, onMounted, provide, ref, watch, withDefaults } from 'vue';
import { computed, defineProps, h, onActivated, onMounted, provide, ref, watch, withDefaults } from 'vue';
import SearchForm from './codet/components/SearchForm.vue';
import DataTable from './codet/components/DataTable.vue';
import TableSettingsDrawer from './codet/components/TableSettingsDrawer.vue';
......@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue'
import AddFieldDrawer from './codet/components/AddFieldDrawer.vue';
import DataTransform from './codet/components/DataTransform.vue';
import CreateDialog from './codet/components/CreateDialog.vue';
// 使用封装的axios实例 - 已在main.ts中全局注册,支持代理
// 通过window对象使用axios - 已在main.ts中全局注册
const axios = (window as any).$axios;
// 定义组件名称,避免与HTML元素冲突
......@@ -32,7 +32,15 @@ interface Props {
}
const props = withDefaults(defineProps<Props>(), {
uiConfig: () => ({})
uiConfig: () => ({
Type: 'Kivii.Finances.Entities.Invoice',
InternalCode: '发票管理(tableField)',
IsDefault: true,
InitQuery: '/Restful/Kivii.Finances.Entities.Invoice/Query.json',
GetUrl: '/Restful/Kivii.Basic.Entities.UiConfig/Get.json',
SetUrl: '/Restful/Kivii.Basic.Entities.UiConfig/Set.json',
CreateVueUrl: '/codet/invoiceContent.vue'
})
});
// 分页配置
const pagination = ref({
......@@ -168,6 +176,11 @@ const generateColumns = (data: any): TableColumn[] => {
const processField = (obj: any, prefix = '') => {
Object.entries(obj).forEach(([key, value]) => {
// 过滤掉 Metadata 字段和包含 Kvid 的字段
if (key === 'Metadata' || key.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
// 处理数组类型字段
if (Array.isArray(value)) {
const columnKey = prefix + key;
......@@ -205,6 +218,11 @@ const generateColumns = (data: any): TableColumn[] => {
// 处理嵌套对象
else if (value && typeof value === 'object' && !Array.isArray(value)) {
Object.entries(value as object).forEach(([nestedKey, _]) => {
// 也过滤嵌套对象中的 Metadata 和 Kvid 字段
if (nestedKey === 'Metadata' || nestedKey.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
const columnKey = `${key}.${nestedKey}`;
// 查找原有列的配置
const existingColumn = rawColumns.value.find(col => col.key === columnKey);
......@@ -415,11 +433,6 @@ const newFilterLabel = ref('');
const newFilterValue = ref('');
// 添加获取所有设置的方法
const getAllTableSettings = async () => {
if (tableSettingsDrawerRef.value) {
await tableSettingsDrawerRef.value.handleGetSettings();
}
};
// 处理新建
const handleCreate = (row?: any) => {
......@@ -486,30 +499,27 @@ const buildSearchParams = () => {
};
// 获取数据
// 修正后的fetchData函数
async function fetchData() {
console.log('🚀 fetchData 被调用,axios 可用:', Boolean(axios));
loading.value = true;
try {
const searchParams = buildSearchParams();
// 自定义axios请求配置,使用自定义参数序列化函数
console.log('📡 发送请求到 /table.json');
const response = await axios.get('/codet/test.json', {
// ✅ 使用 window.$api.request 获取数据
const { data: response, error } = await window.$api.request({
method: 'POST',
url: props.uiConfig.InitQuery,
params: searchParams,
paramsSerializer: (params: any) => {
// 自定义参数序列化,避免数组被转换为key[]=value格式
const queryParams: string[] = [];
for (const key in params) {
if (params[key] !== undefined && params[key] !== null) {
if (Array.isArray(params[key])) {
// 只在数组不为空时添加参数
if (params[key].length > 0) {
// 数组类型使用JSON.stringify处理,并将参数名变为复数形式
queryParams.push(`${encodeURIComponent(`${key}s`)}=${encodeURIComponent(JSON.stringify(params[key]))}`);
}
} else {
// 非数组类型参数保持原样
queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
}
}
......@@ -518,17 +528,24 @@ async function fetchData() {
return queryParams.join('&');
}
});
console.log('✅ 请求成功,响应:', response.status, response.data?.Results?.length || 0, '条数据');
if (response.data?.Results?.length) {
// ✅ 处理第一个请求的错误
if (error) {
console.error('❌ 数据请求失败:', error);
tableData.value = [];
pagination.value.itemCount = 0;
return;
}
// ✅ 正确访问返回的数据
console.log('✅ 数据请求成功:', response);
// 检查数据结构 - response就是业务数据,不需要再访问.data
if (response?.Results?.length) {
console.log('📊 获取到数据:', response.Results.length, '条');
// 保存当前的filter状态
const currentFilterState: Record<
string,
{
filter: boolean;
filterOptions?: { label: string; value: string }[];
filterOptionValues?: string[] | null;
}
> = rawColumns.value.reduce((acc: Record<string, any>, col) => {
const currentFilterState: Record<string, any> = rawColumns.value.reduce((acc: Record<string, any>, col) => {
if (col.filter && col.filterOptionValues) {
acc[col.key] = {
filter: col.filter,
......@@ -539,18 +556,19 @@ async function fetchData() {
return acc;
}, {});
// 先获取字段配置
// ✅ 获取字段配置用于生成基础列
let fieldConfigs = [];
try {
const entityResponse = await axios.get(`/Server/Entity/${props.uiConfig.Type}`);
if (entityResponse.data?.Results) {
fieldConfigs = entityResponse.data.Results;
}
} catch (error) {
// console.error('获取字段配置失败:', error);
const { data: entityData, error: entityError } = await window.$api.get(`/Server/Entity/${props.uiConfig.Type}`);
if (entityError) {
console.warn('⚠️ 获取字段配置失败:', entityError);
// 不阻断主流程,使用空配置继续
} else if (entityData?.Results) {
fieldConfigs = entityData.Results;
console.log('✅ 字段配置获取成功:', fieldConfigs.length, '个字段');
}
// 生成列配置时传入字段配置
// 生成列配置 - 使用response.Results而不是response.data.Results
const generateColumnsWithConfig = (data: any, configs: any[]): TableColumn[] => {
if (!data) return [];
const newColumns: TableColumn[] = [];
......@@ -568,11 +586,14 @@ async function fetchData() {
const processField = (obj: any, prefix = '') => {
Object.entries(obj).forEach(([key, value]) => {
// 查找字段配置
// 过滤掉 Metadata 字段和包含 Kvid 的字段
if (key === 'Metadata' || key.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
const fieldConfig = configs.find(field => field.Name === key);
const displayName = fieldConfig?.DisplayName || key;
// 处理数组类型字段
if (Array.isArray(value)) {
const columnKey = prefix + key;
const existingColumn = rawColumns.value.find(col => col.key === columnKey);
......@@ -582,9 +603,7 @@ async function fetchData() {
key: columnKey,
width: 200,
minWidth: 150,
ellipsis: existingColumn?.ellipsis ?? {
tooltip: true
},
ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
resizable: true,
sortable: false,
render: (row: any) => {
......@@ -604,10 +623,13 @@ async function fetchData() {
return '';
}
});
}
// 处理嵌套对象
else if (value && typeof value === 'object' && !Array.isArray(value)) {
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
Object.entries(value as object).forEach(([nestedKey, _]) => {
// 也过滤嵌套对象中的 Metadata 和 Kvid 字段
if (nestedKey === 'Metadata' || nestedKey.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
const columnKey = `${key}.${nestedKey}`;
const existingColumn = rawColumns.value.find(col => col.key === columnKey);
const nestedFieldConfig = configs.find(field => field.Name === nestedKey);
......@@ -618,9 +640,7 @@ async function fetchData() {
key: columnKey,
width: key.includes('Time') ? 200 : 150,
minWidth: 100,
ellipsis: existingColumn?.ellipsis ?? {
tooltip: true
},
ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
resizable: true,
sortable: existingColumn?.sortable ?? false,
...(existingColumn?.filter
......@@ -643,9 +663,7 @@ async function fetchData() {
}
});
});
}
// 处理普通字段
else {
} else {
const columnKey = prefix + key;
const existingColumn = rawColumns.value.find(col => col.key === columnKey);
......@@ -654,9 +672,7 @@ async function fetchData() {
key: columnKey,
width: key.includes('Time') ? 200 : 150,
minWidth: 100,
ellipsis: existingColumn?.ellipsis ?? {
tooltip: true
},
ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
resizable: true,
sortable: existingColumn?.sortable ?? false,
...(existingColumn?.filter
......@@ -683,13 +699,12 @@ async function fetchData() {
return newColumns;
};
// 使用新的生成函数生成列配置
rawColumns.value = generateColumnsWithConfig(response.data.Results[0], fieldConfigs);
// 确保所有列在rawColumns更新后默认可见
// ✅ 生成基础列配置(无论是否有保存的配置都需要基础列)
console.log('📋 生成基础列配置');
rawColumns.value = generateColumnsWithConfig(response.Results[0], fieldConfigs);
visibleColumns.value = rawColumns.value.map(col => col.key);
// 恢复之前的筛选状态
// 恢复筛选状态
if (Object.keys(currentFilterState).length > 0) {
rawColumns.value.forEach(col => {
const savedState = currentFilterState[col.key];
......@@ -701,24 +716,29 @@ async function fetchData() {
});
}
tableData.value = response.data.Results;
pagination.value.itemCount = response.data.Total || 0;
// ✅ 尝试应用保存的配置来覆盖基础列设置
if (tableSettingsDrawerRef.value?.handleGetSettings) {
console.log('📋 尝试应用保存的配置');
await tableSettingsDrawerRef.value.handleGetSettings();
}
// ✅ 使用正确的数据结构设置表格数据
tableData.value = response.Results;
pagination.value.itemCount = response.Total || 0;
// 移除多余的判断,简化配置加载逻辑
// 标记配置已加载(用于其他逻辑判断)
if (!isConfigLoaded.value) {
// console.log('首次加载配置');
isConfigLoaded.value = true;
nextTick(() => {
getAllTableSettings();
});
}
} else {
// 如果没有数据,清空表格数据并设置总数为0
// 没有数据的情况
console.log('📝 没有获取到数据');
tableData.value = [];
pagination.value.itemCount = response.data?.Total || 0;
pagination.value.itemCount = response?.Total || 0;
}
} catch (error) {
console.error('❌ 请求失败:', error);
// ✅ 这个catch现在只处理真正的异常情况
console.error('❌ fetchData执行过程中发生异常:', error);
tableData.value = [];
pagination.value.itemCount = 0;
} finally {
......@@ -1007,7 +1027,10 @@ const updateFieldVisibility = (field: any, visible: boolean) => {
// 获取可选择的列
const availableColumns = computed(() => {
const existingKeys = searchFields.value.map(f => f.key);
return rawColumns.value.filter(col => !existingKeys.includes(col.key));
return rawColumns.value.filter(col => {
// 过滤掉已存在的字段、Metadata 字段和包含 Kvid 的字段
return !existingKeys.includes(col.key) && col.key !== 'Metadata' && !col.key.toLowerCase().includes('kvid');
});
});
// 在 script setup 部分添加以下响应式变量
......@@ -1146,10 +1169,15 @@ const generateFilterOptions = (column: TableColumn) => {
const selectedField = ref('');
const availableFields = computed(() => {
return rawColumns.value.map((column: TableColumn) => ({
label: column.title,
value: column.key
}));
return rawColumns.value
.filter(column => {
// 过滤掉 Metadata 字段和包含 Kvid 的字段
return column.key !== 'Metadata' && !column.key.toLowerCase().includes('kvid');
})
.map((column: TableColumn) => ({
label: column.title,
value: column.key
}));
});
const tagMappings = ref<TagMapping[]>([{ value: '', label: '', color: '' }]);
const transformRules = ref<TransformRule[]>([]);
......@@ -1233,9 +1261,12 @@ const updateTransformedColumnRenders = () => {
});
};
const applyTransformations = () => {
const applyTransformations = (silent = false) => {
if (!tableData.value || tableData.value.length === 0) {
window.$message?.error('没有可转换的数据');
// 如果是静默模式(配置恢复时),不显示错误信息
if (!silent) {
window.$message?.error('没有可转换的数据');
}
return;
}
......@@ -1250,7 +1281,9 @@ const applyTransformations = () => {
// window.$message?.success('数据转换成功应用到显示');
} catch (error: any) {
// console.error('转换过程中出错:', error);
window.$message?.error(`转换过程中出错: ${error.message}`);
if (!silent) {
window.$message?.error(`转换过程中出错: ${error.message}`);
}
}
};
......@@ -1355,16 +1388,8 @@ onMounted(() => {
WangEditor: Boolean((window as any).$WangEditor)
});
// 在组件挂载时就确保配置加载完成
// 在组件挂载时加载数据(配置获取已在fetchData中处理)
fetchData();
Promise.resolve()
.then(() => fetchData())
.then(() => {
nextTick(() => {
// console.log('组件挂载完成,初始化配置');
// getAllTableSettings();
});
});
});
onActivated(() => {
......
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