功能:按资源配置平台列表搜索
This commit is contained in:
@@ -14,9 +14,9 @@
|
||||
请从详情页中的专用操作推进流程,操作结果会按服务端状态校验并保留审计记录。
|
||||
</a-alert>
|
||||
<div class="list-toolbar">
|
||||
<a-form :model="filters" layout="inline" class="filters" @submit="search">
|
||||
<a-form-item label="关键字">
|
||||
<a-input v-model="filters.keyword" allow-clear placeholder="关键字段模糊搜索" />
|
||||
<a-form v-if="searchEnabled" :model="filters" layout="inline" class="filters" @submit="search">
|
||||
<a-form-item label="模糊搜索">
|
||||
<a-input v-model="filters.keyword" allow-clear :placeholder="searchPlaceholder" />
|
||||
</a-form-item>
|
||||
<a-button type="primary" html-type="submit">查询</a-button>
|
||||
<a-button @click="resetSearch">重置</a-button>
|
||||
@@ -185,6 +185,7 @@ import {
|
||||
protectedListAvatarDisplayName,
|
||||
} from './protected-list-avatar-loader';
|
||||
import { useResourceListExtras } from './use-resource-list-extras';
|
||||
import { useResourceListSearch } from './use-resource-list-search';
|
||||
|
||||
const props = defineProps<{ definition: ResourceUiDefinition }>();
|
||||
const route = useRoute();
|
||||
@@ -196,9 +197,16 @@ const page = ref(Math.max(1, Number(route.query.page) || 1));
|
||||
const pageSize = 50;
|
||||
const total = ref(0);
|
||||
const list = ref<ResourceRow[]>([]);
|
||||
const filters = reactive({
|
||||
keyword: typeof route.query.keyword === 'string' ? route.query.keyword : '',
|
||||
});
|
||||
const {
|
||||
clearUnsupportedKeyword,
|
||||
displayFields,
|
||||
filters,
|
||||
requestKeyword,
|
||||
resetSearchState,
|
||||
searchEnabled,
|
||||
searchPlaceholder,
|
||||
syncQuery,
|
||||
} = useResourceListSearch(definitionRef, page);
|
||||
const relations = useResourceRelations(() => ({
|
||||
staffType: String(route.query.staff_type ?? route.meta.staffType ?? ''),
|
||||
}));
|
||||
@@ -244,20 +252,6 @@ const canArchive = computed(
|
||||
props.definition.canArchive &&
|
||||
(!requiresRoot.value || userStore.role === 'root'),
|
||||
);
|
||||
const displayFields = computed(() =>
|
||||
props.definition.fields
|
||||
.filter((field) => field.key !== 'identity' && field.type !== 'password')
|
||||
.filter(
|
||||
(field) =>
|
||||
!(
|
||||
props.definition.name === 'gas_basic' &&
|
||||
['credit_code', 'address', 'longitude', 'latitude'].includes(
|
||||
field.key,
|
||||
)
|
||||
),
|
||||
)
|
||||
.slice(0, 6),
|
||||
);
|
||||
const listTitle = computed(() => {
|
||||
const owner = String(route.query.owner_name ?? '');
|
||||
return owner
|
||||
@@ -280,7 +274,8 @@ async function load() {
|
||||
if (props.definition.name === 'delivery_account')
|
||||
serverFilters.delivery_basic_identities = managedOwnerIdentity.value;
|
||||
}
|
||||
if (filters.keyword.trim()) serverFilters.keyword = filters.keyword.trim();
|
||||
const keyword = requestKeyword();
|
||||
if (keyword) serverFilters.keyword = keyword;
|
||||
const result = await resourceApi.list<ResourceRow>(
|
||||
props.definition.resource,
|
||||
page.value,
|
||||
@@ -423,23 +418,6 @@ function confirmArchive(row: ResourceRow) {
|
||||
});
|
||||
}
|
||||
|
||||
async function syncQuery() {
|
||||
const query: Record<string, string> = {};
|
||||
for (const key of [
|
||||
'owner_identity',
|
||||
'owner_name',
|
||||
'relation_key',
|
||||
'staff_type',
|
||||
'return_to',
|
||||
]) {
|
||||
const value = route.query[key];
|
||||
if (typeof value === 'string' && value) query[key] = value;
|
||||
}
|
||||
if (page.value > 1) query.page = String(page.value);
|
||||
if (filters.keyword.trim()) query.keyword = filters.keyword.trim();
|
||||
await router.replace({ query });
|
||||
}
|
||||
|
||||
async function search() {
|
||||
page.value = 1;
|
||||
await syncQuery();
|
||||
@@ -483,6 +461,7 @@ async function loadFieldOptions() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await clearUnsupportedKeyword();
|
||||
await Promise.all([
|
||||
relations.preload(displayFields.value),
|
||||
loadFieldOptions(),
|
||||
@@ -493,8 +472,7 @@ onBeforeUnmount(avatarLoader.reset);
|
||||
watch(
|
||||
() => props.definition.resource,
|
||||
async () => {
|
||||
page.value = 1;
|
||||
filters.keyword = '';
|
||||
await resetSearchState();
|
||||
await Promise.all([
|
||||
relations.preload(displayFields.value),
|
||||
loadFieldOptions(),
|
||||
|
||||
@@ -4,9 +4,27 @@
|
||||
*/
|
||||
import { optionLabel } from '@/api/resource-display';
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import type { ResourceField } from '@/api/resources';
|
||||
import type { ResourceField, ResourceUiDefinition } from '@/api/resources';
|
||||
import { isProtectedListAvatarField } from './protected-list-avatar-loader';
|
||||
|
||||
/** 返回标准列表实际渲染的业务字段,搜索提示与表格共同复用该规则。 */
|
||||
export function resourceListDisplayFields(
|
||||
definition: ResourceUiDefinition,
|
||||
): ResourceField[] {
|
||||
return definition.fields
|
||||
.filter((field) => field.key !== 'identity' && field.type !== 'password')
|
||||
.filter(
|
||||
(field) =>
|
||||
!(
|
||||
definition.name === 'gas_basic' &&
|
||||
['credit_code', 'address', 'longitude', 'latitude'].includes(
|
||||
field.key,
|
||||
)
|
||||
),
|
||||
)
|
||||
.slice(0, 6);
|
||||
}
|
||||
|
||||
/** 读取关系列表字段的完整唯一标识。 */
|
||||
export function identityFieldValue(field: ResourceField, row: ResourceRow) {
|
||||
return String(row[field.key] ?? row[`${field.key}_masked`] ?? '');
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 功能描述:管理标准资源列表的可搜索字段、关键字状态和 URL 查询参数。
|
||||
* 版本:v1.0.0
|
||||
*/
|
||||
import { computed, reactive, type Ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import type { ResourceUiDefinition } from '@/api/resources';
|
||||
import { resourceListDisplayFields } from './resource-list-field-display';
|
||||
|
||||
/**
|
||||
* 创建资源列表搜索状态。
|
||||
* 参数:definition 为当前资源定义,page 为当前页码。
|
||||
* 返回值:实际显示字段、搜索提示、请求关键字及 URL 同步方法。
|
||||
*/
|
||||
export function useResourceListSearch(
|
||||
definition: Ref<ResourceUiDefinition>,
|
||||
page: Ref<number>,
|
||||
) {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const filters = reactive({
|
||||
keyword: typeof route.query.keyword === 'string' ? route.query.keyword : '',
|
||||
});
|
||||
const displayFields = computed(() =>
|
||||
resourceListDisplayFields(definition.value),
|
||||
);
|
||||
const searchableFields = computed(() => {
|
||||
const searchableKeys = new Set(
|
||||
definition.value.searchFields.map((field) => field.key),
|
||||
);
|
||||
return displayFields.value.filter((field) => searchableKeys.has(field.key));
|
||||
});
|
||||
const searchEnabled = computed(() => searchableFields.value.length > 0);
|
||||
const searchPlaceholder = computed(
|
||||
() =>
|
||||
`可搜索:${searchableFields.value
|
||||
.map((field) => field.listLabel ?? field.label)
|
||||
.join('、')}`,
|
||||
);
|
||||
|
||||
/** 仅在当前资源支持搜索时返回去除首尾空格的关键字。 */
|
||||
function requestKeyword() {
|
||||
return searchEnabled.value ? filters.keyword.trim() : '';
|
||||
}
|
||||
|
||||
/** 将受支持的搜索状态写入 URL,并移除无效或陈旧的 keyword。 */
|
||||
async function syncQuery() {
|
||||
const query: Record<string, string> = {};
|
||||
for (const key of [
|
||||
'owner_identity',
|
||||
'owner_name',
|
||||
'relation_key',
|
||||
'staff_type',
|
||||
'return_to',
|
||||
]) {
|
||||
const value = route.query[key];
|
||||
if (typeof value === 'string' && value) query[key] = value;
|
||||
}
|
||||
if (page.value > 1) query.page = String(page.value);
|
||||
const keyword = requestKeyword();
|
||||
if (keyword) query.keyword = keyword;
|
||||
await router.replace({ query });
|
||||
}
|
||||
|
||||
/** 首次进入不支持搜索的页面时清除地址栏遗留关键字。 */
|
||||
async function clearUnsupportedKeyword() {
|
||||
if (searchEnabled.value || !route.query.keyword) return;
|
||||
filters.keyword = '';
|
||||
await syncQuery();
|
||||
}
|
||||
|
||||
/** 切换资源时复位搜索状态,并同步移除旧资源关键字。 */
|
||||
async function resetSearchState() {
|
||||
page.value = 1;
|
||||
filters.keyword = '';
|
||||
if (route.query.keyword) await syncQuery();
|
||||
}
|
||||
|
||||
return {
|
||||
clearUnsupportedKeyword,
|
||||
displayFields,
|
||||
filters,
|
||||
requestKeyword,
|
||||
resetSearchState,
|
||||
searchEnabled,
|
||||
searchPlaceholder,
|
||||
syncQuery,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user