Commit b5c25409 by User

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

parent 74076394
# backend service base url, test environment # 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 # other backend service base url, test environment
VITE_OTHER_SERVICE_BASE_URL= `{ VITE_OTHER_SERVICE_BASE_URL= `{
......
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { computed, inject } from 'vue';
defineOptions({ defineOptions({
name: 'SearchForm' name: 'SearchForm'
}); });
...@@ -19,44 +19,114 @@ const toggleExpand = () => { ...@@ -19,44 +19,114 @@ const toggleExpand = () => {
const openSettings = () => { const openSettings = () => {
tableState.showSettingDrawer.value = true; tableState.showSettingDrawer.value = true;
}; };
// 计算是否需要显示展开按钮(字段超过5个时才显示)
const showExpandButton = computed(() => {
return formItems.value && formItems.value.length > 5;
});
// 计算当前显示的字段数量
const visibleFields = computed(() => {
if (!formItems.value) return [];
return isExpanded.value ? formItems.value : formItems.value.slice(0, 5);
});
// 计算第一行显示的字段(如果折叠且超过5个字段,第一行显示5个字段,操作按钮在下一行)
const firstRowFields = computed(() => {
if (!formItems.value) return [];
// 如果字段数量 <= 5,或者已展开,正常显示
if (formItems.value.length <= 5 || isExpanded.value) {
return visibleFields.value;
}
// 如果字段数量 > 5 且折叠状态,第一行显示5个字段
return formItems.value.slice(0, 5);
});
// 计算是否需要在第一行显示操作按钮
const showActionsInFirstRow = computed(() => {
return false; // 操作按钮总是单独占一行
});
</script> </script>
<template> <template>
<NCard class="search-card" :bordered="false" content-style="padding: 0 12px 8px;margin-top:10px"> <NCard class="search-card" :bordered="false" content-style="padding: 12px;">
<NGrid :cols="6" x-gap="8" y-gap="8"> <!-- 顶部配置按钮区域 -->
<!-- 默认显示前5个表单项 --> <div class="top-actions">
<NGridItem v-for="item in formItems.slice(0, 5)" :key="item.key"> <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"> <NFormItem :label="item.label" label-placement="left">
<NInput <NInput
v-if="item.type === 'input'" v-if="item.type === 'input'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
></NInput> ></NInput>
<NSelect <NSelect
v-if="item.type === 'select'" v-if="item.type === 'select'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
:options="item.options" :options="item.options"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
></NSelect> ></NSelect>
<NDatePicker <NDatePicker
v-if="item.type === 'date'" v-if="item.type === 'date'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
type="daterange" type="daterange"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
clearable clearable
></NDatePicker> ></NDatePicker>
</NFormItem> </NFormItem>
</NGridItem> </NGridItem>
</template>
<!-- 未展开时的操作按钮 --> <!-- 第一行的操作按钮(当折叠且字段超过5个时) -->
<NGridItem v-if="!isExpanded"> <NGridItem v-if="showActionsInFirstRow" :span="8">
<div class="operation-buttons"> <div class="action-buttons">
<NSpace :size="8" align="center" :wrap="false"> <NSpace :size="8">
<NButton type="primary" size="small" @click="() => handleCreate(null)">新建</NButton>
<NButton type="primary" size="small" @click="handleSearch">查询</NButton> <NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton> <NButton size="small" @click="resetSearch">重置</NButton>
<NButton v-if="showExpandButton" size="small" @click="toggleExpand">
<NButton size="small" @click="toggleExpand">
<template #icon> <template #icon>
<NIcon> <NIcon>
<svg <svg
...@@ -70,79 +140,45 @@ const openSettings = () => { ...@@ -70,79 +140,45 @@ const openSettings = () => {
</NIcon> </NIcon>
</template> </template>
</NButton> </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> </NSpace>
</div> </div>
</NGridItem> </NGridItem>
<!-- 展开后的额外表单项 --> <!-- 展开后的额外字段(如果已展开且有超过5个字段) -->
<template v-if="isExpanded"> <template v-if="isExpanded && formItems && formItems.length > 5">
<NGridItem v-for="item in formItems.slice(5)" :key="item.key"> <template v-for="item in formItems.slice(5)" :key="item.key">
<NGridItem :span="4">
<NFormItem :label="item.label" label-placement="left"> <NFormItem :label="item.label" label-placement="left">
<NInput <NInput
v-if="item.type === 'input'" v-if="item.type === 'input'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
></NInput> ></NInput>
<NSelect <NSelect
v-if="item.type === 'select'" v-if="item.type === 'select'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
:options="item.options" :options="item.options"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
></NSelect> ></NSelect>
<NDatePicker <NDatePicker
v-if="item.type === 'date'" v-if="item.type === 'date'"
v-model:value="searchForm[item.key]" v-model:value="searchForm[item.key]"
type="daterange" type="daterange"
:placeholder="item.placeholder" :placeholder="item.placeholder"
size="small"
clearable clearable
></NDatePicker> ></NDatePicker>
</NFormItem> </NFormItem>
</NGridItem> </NGridItem>
</template>
<!-- 展开后的操作按钮,独占一行并靠左对齐 --> <!-- 展开状态下的操作按钮 -->
<NGridItem :span="6"> <NGridItem :span="4">
<div class="operation-buttons-expanded"> <div class="action-buttons">
<NSpace :size="8" align="center" :wrap="false"> <NSpace :size="8">
<NButton size="small" @click="() => handleCreate(null)">新建</NButton> <NButton type="primary" size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="handleSearch">查询</NButton>
<NButton size="small" @click="resetSearch">重置</NButton> <NButton size="small" @click="resetSearch">重置</NButton>
<NButton size="small" @click="toggleExpand"> <NButton size="small" @click="toggleExpand">
<template #icon> <template #icon>
...@@ -162,23 +198,18 @@ const openSettings = () => { ...@@ -162,23 +198,18 @@ const openSettings = () => {
</NIcon> </NIcon>
</template> </template>
</NButton> </NButton>
<NButton size="small" title="刷新数据" @click="fetchData"> </NSpace>
<template #icon> </div>
<NIcon> </NGridItem>
<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> </template>
</NButton>
<NButton size="small" @click="openSettings"> <!-- 折叠状态下的操作按钮(当字段超过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> <template #icon>
<NIcon> <NIcon>
<svg <svg
...@@ -186,15 +217,8 @@ const openSettings = () => { ...@@ -186,15 +217,8 @@ const openSettings = () => {
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path <path d="M18 6.41L16.59 5L12 9.58L7.41 5L6 6.41l6 6z" fill="currentColor"></path>
opacity=".3" <path d="M18 13l-1.41-1.41L12 16.17l-4.59-4.58L6 13l6 6z" fill="currentColor"></path>
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> </svg>
</NIcon> </NIcon>
</template> </template>
...@@ -202,8 +226,18 @@ const openSettings = () => { ...@@ -202,8 +226,18 @@ const openSettings = () => {
</NSpace> </NSpace>
</div> </div>
</NGridItem> </NGridItem>
</template>
<!-- 字段数量 <= 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> </NGrid>
</div>
</NCard> </NCard>
</template> </template>
...@@ -212,22 +246,16 @@ const openSettings = () => { ...@@ -212,22 +246,16 @@ const openSettings = () => {
margin-bottom: 8px; margin-bottom: 8px;
} }
.operation-buttons { .top-actions {
display: flex; 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; justify-content: flex-end;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--n-border-color);
} }
:deep(.n-space) { .search-form-area {
flex-wrap: nowrap; margin-top: 8px;
} }
:deep(.n-form-item) { :deep(.n-form-item) {
...@@ -235,29 +263,24 @@ const openSettings = () => { ...@@ -235,29 +263,24 @@ const openSettings = () => {
} }
:deep(.n-form-item-label) { :deep(.n-form-item-label) {
width: 120px; width: 100px;
justify-content: flex-end; justify-content: flex-end;
padding: 0; padding-right: 8px;
margin-right: 4px; font-size: 14px;
text-align: right; color: var(--n-text-color-2);
white-space: normal; align-items: flex-start;
display: flex;
height: 28px;
padding-top: 5px;
} }
:deep(.n-form-item-label label) { :deep(.n-form-item-label label) {
display: inline-block; line-height: 1;
text-align: right; margin: 0;
width: 100%;
}
:deep(.n-form-item-label label:after) {
content: '';
display: block;
text-align: left;
} }
:deep(.n-form-item-blank) { :deep(.n-form-item-blank) {
min-height: 28px; min-height: 28px;
justify-content: flex-end;
} }
:deep(.n-input) { :deep(.n-input) {
...@@ -275,4 +298,15 @@ const openSettings = () => { ...@@ -275,4 +298,15 @@ const openSettings = () => {
:deep(.n-form-item-feedback-wrapper) { :deep(.n-form-item-feedback-wrapper) {
display: none; 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> </style>
...@@ -196,9 +196,9 @@ const applySettings = (settings: any) => { ...@@ -196,9 +196,9 @@ const applySettings = (settings: any) => {
}); });
} }
// 如果有转换规则,自动应用转换 // 如果有转换规则,自动应用转换(静默模式,避免在数据未加载时显示错误)
if (settings.transformSettings.transformRules?.length > 0) { if (settings.transformSettings.transformRules?.length > 0) {
tableMethods.applyTransformations(); tableMethods.applyTransformations(true);
} }
} }
}; };
...@@ -208,10 +208,11 @@ const handleGetSettings = async () => { ...@@ -208,10 +208,11 @@ const handleGetSettings = async () => {
try { try {
// 在请求前检查必要的配置参数 // 在请求前检查必要的配置参数
if (!uiConfig.GetUrl || !uiConfig.Type) { if (!uiConfig.GetUrl || !uiConfig.Type) {
// console.warn('缺少必要的配置参数,无法获取配置'); console.log('📋 缺少必要的配置参数,无法获取配置,需要调用字段匹配接口');
return; return false; // 返回false表示没有获取到配置
} }
console.log('📋 尝试从 GetUrl 获取配置:', uiConfig.GetUrl);
const response = await axios.post(uiConfig.GetUrl, { const response = await axios.post(uiConfig.GetUrl, {
Type: uiConfig.Type, Type: uiConfig.Type,
InternalCode: uiConfig.InternalCode, InternalCode: uiConfig.InternalCode,
...@@ -220,15 +221,18 @@ const handleGetSettings = async () => { ...@@ -220,15 +221,18 @@ const handleGetSettings = async () => {
if (response.data?.Parameters) { if (response.data?.Parameters) {
settings.value = JSON.parse(response.data.Parameters); settings.value = JSON.parse(response.data.Parameters);
// console.log('获取到的配置信息:', settings.value); console.log('✅ 获取到的配置信息:', settings.value);
applySettings(settings.value); applySettings(settings.value);
// window.$message?.success('配置信息获取成功'); console.log('✅ 配置信息获取成功,跳过字段匹配接口');
return true; // 返回true表示成功获取到配置
} else { } else {
// console.log('没有获取到配置参数,使用默认配置'); console.log('⚠️ 没有获取到配置参数,需要调用字段匹配接口');
return false; // 返回false表示没有获取到配置
} }
} catch (error) { } catch (error) {
// console.error('获取配置失败:', error); console.error('❌ 获取配置失败:', error);
window.$message?.error('获取配置失败'); // window.$message?.error('获取配置失败');
return false; // 返回false表示获取配置失败
} }
}; };
......
<script setup lang="ts"> <script setup lang="ts">
/* eslint-disable no-underscore-dangle */ /* 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 SearchForm from './codet/components/SearchForm.vue';
import DataTable from './codet/components/DataTable.vue'; import DataTable from './codet/components/DataTable.vue';
import TableSettingsDrawer from './codet/components/TableSettingsDrawer.vue'; import TableSettingsDrawer from './codet/components/TableSettingsDrawer.vue';
...@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue' ...@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue'
import AddFieldDrawer from './codet/components/AddFieldDrawer.vue'; import AddFieldDrawer from './codet/components/AddFieldDrawer.vue';
import DataTransform from './codet/components/DataTransform.vue'; import DataTransform from './codet/components/DataTransform.vue';
import CreateDialog from './codet/components/CreateDialog.vue'; import CreateDialog from './codet/components/CreateDialog.vue';
// 使用封装的axios实例 - 已在main.ts中全局注册,支持代理 // 通过window对象使用axios - 已在main.ts中全局注册
const axios = (window as any).$axios; const axios = (window as any).$axios;
// 定义组件名称,避免与HTML元素冲突 // 定义组件名称,避免与HTML元素冲突
...@@ -32,7 +32,15 @@ interface Props { ...@@ -32,7 +32,15 @@ interface Props {
} }
const props = withDefaults(defineProps<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({ const pagination = ref({
...@@ -168,6 +176,11 @@ const generateColumns = (data: any): TableColumn[] => { ...@@ -168,6 +176,11 @@ const generateColumns = (data: any): TableColumn[] => {
const processField = (obj: any, prefix = '') => { const processField = (obj: any, prefix = '') => {
Object.entries(obj).forEach(([key, value]) => { Object.entries(obj).forEach(([key, value]) => {
// 过滤掉 Metadata 字段和包含 Kvid 的字段
if (key === 'Metadata' || key.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
// 处理数组类型字段 // 处理数组类型字段
if (Array.isArray(value)) { if (Array.isArray(value)) {
const columnKey = prefix + key; const columnKey = prefix + key;
...@@ -205,6 +218,11 @@ const generateColumns = (data: any): TableColumn[] => { ...@@ -205,6 +218,11 @@ const generateColumns = (data: any): TableColumn[] => {
// 处理嵌套对象 // 处理嵌套对象
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, _]) => { Object.entries(value as object).forEach(([nestedKey, _]) => {
// 也过滤嵌套对象中的 Metadata 和 Kvid 字段
if (nestedKey === 'Metadata' || nestedKey.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
const columnKey = `${key}.${nestedKey}`; const columnKey = `${key}.${nestedKey}`;
// 查找原有列的配置 // 查找原有列的配置
const existingColumn = rawColumns.value.find(col => col.key === columnKey); const existingColumn = rawColumns.value.find(col => col.key === columnKey);
...@@ -415,11 +433,6 @@ const newFilterLabel = ref(''); ...@@ -415,11 +433,6 @@ const newFilterLabel = ref('');
const newFilterValue = ref(''); const newFilterValue = ref('');
// 添加获取所有设置的方法 // 添加获取所有设置的方法
const getAllTableSettings = async () => {
if (tableSettingsDrawerRef.value) {
await tableSettingsDrawerRef.value.handleGetSettings();
}
};
// 处理新建 // 处理新建
const handleCreate = (row?: any) => { const handleCreate = (row?: any) => {
...@@ -486,30 +499,27 @@ const buildSearchParams = () => { ...@@ -486,30 +499,27 @@ const buildSearchParams = () => {
}; };
// 获取数据 // 获取数据
// 修正后的fetchData函数
async function fetchData() { async function fetchData() {
console.log('🚀 fetchData 被调用,axios 可用:', Boolean(axios));
loading.value = true; loading.value = true;
try { try {
const searchParams = buildSearchParams(); const searchParams = buildSearchParams();
// 自定义axios请求配置,使用自定义参数序列化函数 // ✅ 使用 window.$api.request 获取数据
console.log('📡 发送请求到 /table.json'); const { data: response, error } = await window.$api.request({
const response = await axios.get('/codet/test.json', { method: 'POST',
url: props.uiConfig.InitQuery,
params: searchParams, params: searchParams,
paramsSerializer: (params: any) => { paramsSerializer: (params: any) => {
// 自定义参数序列化,避免数组被转换为key[]=value格式
const queryParams: string[] = []; const queryParams: string[] = [];
for (const key in params) { for (const key in params) {
if (params[key] !== undefined && params[key] !== null) { if (params[key] !== undefined && params[key] !== null) {
if (Array.isArray(params[key])) { if (Array.isArray(params[key])) {
// 只在数组不为空时添加参数
if (params[key].length > 0) { if (params[key].length > 0) {
// 数组类型使用JSON.stringify处理,并将参数名变为复数形式
queryParams.push(`${encodeURIComponent(`${key}s`)}=${encodeURIComponent(JSON.stringify(params[key]))}`); queryParams.push(`${encodeURIComponent(`${key}s`)}=${encodeURIComponent(JSON.stringify(params[key]))}`);
} }
} else { } else {
// 非数组类型参数保持原样
queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`); queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
} }
} }
...@@ -518,17 +528,24 @@ async function fetchData() { ...@@ -518,17 +528,24 @@ async function fetchData() {
return queryParams.join('&'); return queryParams.join('&');
} }
}); });
console.log('✅ 请求成功,响应:', response.status, response.data?.Results?.length || 0, '条数据');
if (response.data?.Results?.length) { // ✅ 处理第一个请求的错误
// 保存当前的filter状态 if (error) {
const currentFilterState: Record< console.error('❌ 数据请求失败:', error);
string, tableData.value = [];
{ pagination.value.itemCount = 0;
filter: boolean; return;
filterOptions?: { label: string; value: string }[];
filterOptionValues?: string[] | null;
} }
> = rawColumns.value.reduce((acc: Record<string, any>, col) => {
// ✅ 正确访问返回的数据
console.log('✅ 数据请求成功:', response);
// 检查数据结构 - response就是业务数据,不需要再访问.data
if (response?.Results?.length) {
console.log('📊 获取到数据:', response.Results.length, '条');
// 保存当前的filter状态
const currentFilterState: Record<string, any> = rawColumns.value.reduce((acc: Record<string, any>, col) => {
if (col.filter && col.filterOptionValues) { if (col.filter && col.filterOptionValues) {
acc[col.key] = { acc[col.key] = {
filter: col.filter, filter: col.filter,
...@@ -539,18 +556,19 @@ async function fetchData() { ...@@ -539,18 +556,19 @@ async function fetchData() {
return acc; return acc;
}, {}); }, {});
// 先获取字段配置 // ✅ 获取字段配置用于生成基础列
let fieldConfigs = []; let fieldConfigs = [];
try { const { data: entityData, error: entityError } = await window.$api.get(`/Server/Entity/${props.uiConfig.Type}`);
const entityResponse = await axios.get(`/Server/Entity/${props.uiConfig.Type}`);
if (entityResponse.data?.Results) { if (entityError) {
fieldConfigs = entityResponse.data.Results; console.warn('⚠️ 获取字段配置失败:', entityError);
} // 不阻断主流程,使用空配置继续
} catch (error) { } else if (entityData?.Results) {
// console.error('获取字段配置失败:', error); fieldConfigs = entityData.Results;
console.log('✅ 字段配置获取成功:', fieldConfigs.length, '个字段');
} }
// 生成列配置时传入字段配置 // 生成列配置 - 使用response.Results而不是response.data.Results
const generateColumnsWithConfig = (data: any, configs: any[]): TableColumn[] => { const generateColumnsWithConfig = (data: any, configs: any[]): TableColumn[] => {
if (!data) return []; if (!data) return [];
const newColumns: TableColumn[] = []; const newColumns: TableColumn[] = [];
...@@ -568,11 +586,14 @@ async function fetchData() { ...@@ -568,11 +586,14 @@ async function fetchData() {
const processField = (obj: any, prefix = '') => { const processField = (obj: any, prefix = '') => {
Object.entries(obj).forEach(([key, value]) => { 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 fieldConfig = configs.find(field => field.Name === key);
const displayName = fieldConfig?.DisplayName || key; const displayName = fieldConfig?.DisplayName || key;
// 处理数组类型字段
if (Array.isArray(value)) { if (Array.isArray(value)) {
const columnKey = prefix + key; const columnKey = prefix + key;
const existingColumn = rawColumns.value.find(col => col.key === columnKey); const existingColumn = rawColumns.value.find(col => col.key === columnKey);
...@@ -582,9 +603,7 @@ async function fetchData() { ...@@ -582,9 +603,7 @@ async function fetchData() {
key: columnKey, key: columnKey,
width: 200, width: 200,
minWidth: 150, minWidth: 150,
ellipsis: existingColumn?.ellipsis ?? { ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
tooltip: true
},
resizable: true, resizable: true,
sortable: false, sortable: false,
render: (row: any) => { render: (row: any) => {
...@@ -604,10 +623,13 @@ async function fetchData() { ...@@ -604,10 +623,13 @@ async function fetchData() {
return ''; 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, _]) => { Object.entries(value as object).forEach(([nestedKey, _]) => {
// 也过滤嵌套对象中的 Metadata 和 Kvid 字段
if (nestedKey === 'Metadata' || nestedKey.toLowerCase().includes('kvid')) {
return; // 跳过这些字段
}
const columnKey = `${key}.${nestedKey}`; const columnKey = `${key}.${nestedKey}`;
const existingColumn = rawColumns.value.find(col => col.key === columnKey); const existingColumn = rawColumns.value.find(col => col.key === columnKey);
const nestedFieldConfig = configs.find(field => field.Name === nestedKey); const nestedFieldConfig = configs.find(field => field.Name === nestedKey);
...@@ -618,9 +640,7 @@ async function fetchData() { ...@@ -618,9 +640,7 @@ async function fetchData() {
key: columnKey, key: columnKey,
width: key.includes('Time') ? 200 : 150, width: key.includes('Time') ? 200 : 150,
minWidth: 100, minWidth: 100,
ellipsis: existingColumn?.ellipsis ?? { ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
tooltip: true
},
resizable: true, resizable: true,
sortable: existingColumn?.sortable ?? false, sortable: existingColumn?.sortable ?? false,
...(existingColumn?.filter ...(existingColumn?.filter
...@@ -643,9 +663,7 @@ async function fetchData() { ...@@ -643,9 +663,7 @@ async function fetchData() {
} }
}); });
}); });
} } else {
// 处理普通字段
else {
const columnKey = prefix + key; const columnKey = prefix + key;
const existingColumn = rawColumns.value.find(col => col.key === columnKey); const existingColumn = rawColumns.value.find(col => col.key === columnKey);
...@@ -654,9 +672,7 @@ async function fetchData() { ...@@ -654,9 +672,7 @@ async function fetchData() {
key: columnKey, key: columnKey,
width: key.includes('Time') ? 200 : 150, width: key.includes('Time') ? 200 : 150,
minWidth: 100, minWidth: 100,
ellipsis: existingColumn?.ellipsis ?? { ellipsis: existingColumn?.ellipsis ?? { tooltip: true },
tooltip: true
},
resizable: true, resizable: true,
sortable: existingColumn?.sortable ?? false, sortable: existingColumn?.sortable ?? false,
...(existingColumn?.filter ...(existingColumn?.filter
...@@ -683,13 +699,12 @@ async function fetchData() { ...@@ -683,13 +699,12 @@ async function fetchData() {
return newColumns; return newColumns;
}; };
// 使用新的生成函数生成列配置 // ✅ 生成基础列配置(无论是否有保存的配置都需要基础列)
rawColumns.value = generateColumnsWithConfig(response.data.Results[0], fieldConfigs); console.log('📋 生成基础列配置');
rawColumns.value = generateColumnsWithConfig(response.Results[0], fieldConfigs);
// 确保所有列在rawColumns更新后默认可见
visibleColumns.value = rawColumns.value.map(col => col.key); visibleColumns.value = rawColumns.value.map(col => col.key);
// 恢复之前的筛选状态 // 恢复筛选状态
if (Object.keys(currentFilterState).length > 0) { if (Object.keys(currentFilterState).length > 0) {
rawColumns.value.forEach(col => { rawColumns.value.forEach(col => {
const savedState = currentFilterState[col.key]; const savedState = currentFilterState[col.key];
...@@ -701,24 +716,29 @@ async function fetchData() { ...@@ -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) { if (!isConfigLoaded.value) {
// console.log('首次加载配置');
isConfigLoaded.value = true; isConfigLoaded.value = true;
nextTick(() => {
getAllTableSettings();
});
} }
} else { } else {
// 如果没有数据,清空表格数据并设置总数为0 // 没有数据的情况
console.log('📝 没有获取到数据');
tableData.value = []; tableData.value = [];
pagination.value.itemCount = response.data?.Total || 0; pagination.value.itemCount = response?.Total || 0;
} }
} catch (error) { } catch (error) {
console.error('❌ 请求失败:', error); // ✅ 这个catch现在只处理真正的异常情况
console.error('❌ fetchData执行过程中发生异常:', error);
tableData.value = []; tableData.value = [];
pagination.value.itemCount = 0; pagination.value.itemCount = 0;
} finally { } finally {
...@@ -1007,7 +1027,10 @@ const updateFieldVisibility = (field: any, visible: boolean) => { ...@@ -1007,7 +1027,10 @@ const updateFieldVisibility = (field: any, visible: boolean) => {
// 获取可选择的列 // 获取可选择的列
const availableColumns = computed(() => { const availableColumns = computed(() => {
const existingKeys = searchFields.value.map(f => f.key); 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 部分添加以下响应式变量 // 在 script setup 部分添加以下响应式变量
...@@ -1146,7 +1169,12 @@ const generateFilterOptions = (column: TableColumn) => { ...@@ -1146,7 +1169,12 @@ const generateFilterOptions = (column: TableColumn) => {
const selectedField = ref(''); const selectedField = ref('');
const availableFields = computed(() => { const availableFields = computed(() => {
return rawColumns.value.map((column: TableColumn) => ({ return rawColumns.value
.filter(column => {
// 过滤掉 Metadata 字段和包含 Kvid 的字段
return column.key !== 'Metadata' && !column.key.toLowerCase().includes('kvid');
})
.map((column: TableColumn) => ({
label: column.title, label: column.title,
value: column.key value: column.key
})); }));
...@@ -1233,9 +1261,12 @@ const updateTransformedColumnRenders = () => { ...@@ -1233,9 +1261,12 @@ const updateTransformedColumnRenders = () => {
}); });
}; };
const applyTransformations = () => { const applyTransformations = (silent = false) => {
if (!tableData.value || tableData.value.length === 0) { if (!tableData.value || tableData.value.length === 0) {
// 如果是静默模式(配置恢复时),不显示错误信息
if (!silent) {
window.$message?.error('没有可转换的数据'); window.$message?.error('没有可转换的数据');
}
return; return;
} }
...@@ -1250,8 +1281,10 @@ const applyTransformations = () => { ...@@ -1250,8 +1281,10 @@ const applyTransformations = () => {
// window.$message?.success('数据转换成功应用到显示'); // window.$message?.success('数据转换成功应用到显示');
} catch (error: any) { } catch (error: any) {
// console.error('转换过程中出错:', error); // console.error('转换过程中出错:', error);
if (!silent) {
window.$message?.error(`转换过程中出错: ${error.message}`); window.$message?.error(`转换过程中出错: ${error.message}`);
} }
}
}; };
// 数据转换相关的方法 // 数据转换相关的方法
...@@ -1355,16 +1388,8 @@ onMounted(() => { ...@@ -1355,16 +1388,8 @@ onMounted(() => {
WangEditor: Boolean((window as any).$WangEditor) WangEditor: Boolean((window as any).$WangEditor)
}); });
// 在组件挂载时就确保配置加载完成 // 在组件挂载时加载数据(配置获取已在fetchData中处理)
fetchData(); fetchData();
Promise.resolve()
.then(() => fetchData())
.then(() => {
nextTick(() => {
// console.log('组件挂载完成,初始化配置');
// getAllTableSettings();
});
});
}); });
onActivated(() => { 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