笔记:
页面搜索栏封装:
<tableQueryForm
ref="queryFormRef"
:form-select="QUERY_FORM_SELECT"
:model="queryForm"
>
<el-form-item :label="$swpT('page.swModulePlan.isSelf')" prop="orderCode">
<el-select v-model="queryForm.selfInnovateFlag">
<el-option label="是" value="Y">是</el-option>
<el-option label="否" value="N">否</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$swpT('page.swModulePlan.isStandModules')" prop="orderCode">
<el-select v-model="queryForm.standardFlag">
<el-option label="是" value="Y">是</el-option>
<el-option label="否" value="N">否</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-form-item>
<i class="fa-search"/>
<el-button icon="search" type="primary" @click="getTableData"
>{{ $swpT("common.search") }}
</el-button>
<el-button icon="refresh" @click="resetSearch()">{{ $swpT("common.reset") }}</el-button>
</el-form-item>
</el-form-item>
</tableQueryForm>
// 软件模块复用率
import TableQueryForm from "@/components/tableQueryForm.vue";
const queryForm = reactive<any>({});
const queryFormRef = ref<any>();
引用:
// 查询表单
const QUERY_FORM_SELECT = [
{
label: "ECU",
filed: "ecu",
remote: {
url: `${
import.meta.env.VITE_APP_SERVICE_BASE_PATH
}query/condition/ecu/list`,
method: "post"
},
prop: {
filterable: true,
multiple: true,
remote: true,
reserveKeyword: true,
remoteShowSuffix: true,
collapseTags: true
}
},
{
label: $swpT("page.swModulePlan.project"),
filed: "project",
remote: {
url: `${
import.meta.env.VITE_APP_SERVICE_BASE_PATH
}query/condition/softmodule/selection/project`,
method: "get"
},
prop: {
filterable: true,
multiple: true,
clearable: true,
collapseTags: true
}
},
{
label: $swpT("page.swModulePlan.softwareModules"),
filed: "moduleName",
remote: {
url: `${
import.meta.env.VITE_APP_SERVICE_BASE_PATH
}/query/condition/softmodule/reuse/moduleNames`,
method: "get"
},
prop: {
filterable: true,
multiple: true,
clearable: true,
collapseTags: true
}
},
{
label: $swpT("field.center"),
filed: "center",
remote: {
url: `${
import.meta.env.VITE_APP_SERVICE_BASE_PATH
}/query/condition/sys/config/querySpaceCenters`,
method: "get"
},
prop: {
filterable: true,
multiple: true,
clearable: true,
collapseTags: true
}
}
];
选择框封装页面:
<template>
<el-form ref="queryFormRef" class="search-panel" inline>
<el-form-item
v-for="item in props.formSelect"
:key="item.filed"
:label="item.label"
:prop="item.filed"
>
<el-select
v-model="($attrs as any)['model'][item.filed]"
:remote-method="(query:string)=>optionRemoteMethod({query,item})"
v-bind="item.prop"
@click="
item.prop.remote ? optionRemoteMethod({ query: ' ', item }) : ''
"
>
<el-option
v-for="(i, index) in queryOptions[item.filed]"
:key="index"
:label="i[item.optionsValueKey?.label || ''] ?? i"
:value="i[item.optionsValueKey?.value || ''] ?? i"
></el-option>
</el-select>
</el-form-item>
<slot></slot>
</el-form>
</template>
<script lang="ts" setup>
import {Res} from "@/types/global";
RES页面:
// 接口返回值
export type Res<T = any> = {
code: number | string;
msg: string;
biz_content: Nullable<T>;
bizContent: Nullable<T>;
};
import {http} from "@/utils";
import {defineExpose, defineProps, onMounted, reactive, ref, withDefaults} from "vue";
const queryOptions = reactive<any>({});
const props = withDefaults(defineProps<{ formSelect: any }>(), {
formSelect: []
});
const queryFormRef = ref();
onMounted(() => {
getEnum();
});
// 获取枚举筛选项
const getEnum = async() => {
props.formSelect.forEach(async(item: any) => {
const {method, url} = item.remote;
if (!item.prop.remote) {
const {code, biz_content} = await http[method as "get" | "post"]<Res>(
url,
{}
);
if (code == 10000) {
queryOptions[item.filed] = biz_content;
}
}
});
};
// 远程搜索
const optionRemoteMethod = async({query, item}: any) => {
const {remote, filed} = item;
if (query) {
item.prop.loading = true;
const {code, biz_content} = await http[
remote.method as "get" | "post"
]<Res>(remote.url, {
[filed]: query
});
item.prop.loading = false;
if (code == 10000) {
queryOptions[filed] = biz_content;
} else {
queryOptions[filed] = [];
}
}
};
// 重置
const resetFields = () => {
queryFormRef.value.resetFields();
};
defineExpose({resetFields});
</script>
htttp.ts
import {qiankunWindow} from "@/libs/viteQiankun/helper";
import {Lang} from "@/settings/base.ts";
import {useBaseStore} from "@/store";
import type {Axios, AxiosError, AxiosResponse, InternalAxiosRequestConfig} from "axios";
import axios from "axios";
import {ElMessage} from "element-plus";
import qs from "qs";
interface RequestConfig extends InternalAxiosRequestConfig {
silent?: boolean;
}
interface ResponseWrapper<T = object> {
code: number;
msg: string;
sub_msg: string;
success: boolean;
data: T;
value: string;
message: string;
}
interface ResponseError extends AxiosError, Omit<ResponseWrapper, "code"> {
}
const instance: Axios = axios.create({
baseURL: "",
timeout: 600_000,
withCredentials: false,
paramsSerializer: params => qs.stringify(params, {arrayFormat: "repeat"})
});
const requestInterceptor = (config: RequestConfig) => {
// 在组件内调用http方法,pinia已经注册,故可以取到值
// 此处直接解构,因为每次都是取最新的数据,故不需要监听响应式变更
const {token} = useBaseStore();
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
const queryList = window.location.href.split("?");
if (queryList.length > 1) {
queryList[1].split("&").forEach(i => {
if (i.indexOf("space") > -1) {
config.headers["Cbs-Space-Id"] = i.split("=")[1];
}
});
}
} else {
// 本地测试用的空间ID
config.headers["Cbs-Space-Id"] = 1;
}
if (token) {
config.headers.Authorization = token;
config.headers["Cbs-Token"] = token;
config.headers["X-Auth-AppId "] = "1";
}
// 语言
if (Lang === "zh-CN") {
config.headers["Accept-Language"] = "zh-CN";
} else if (Lang === "en") {
config.headers["Accept-Language"] = "en-US";
}
return config;
};
const responseInterceptor = (response: AxiosResponse) => {
const {data, config, status, request} =
response as AxiosResponse<ResponseWrapper>;
if (request.responseType === "blob" && status == 200) {
return data;
} else {
if (data?.code == 10000) {
// 此处不可直接写成data.data,否则拦截器会报ts错误
return response.data;
} else {
// 判断接口401,则跳转到SSO登录页,其他逻辑在组件内处理
// eslint-disable-next-line no-empty
if (data.code === 401) {
} else if (!(config as RequestConfig).silent && data.code !== 302) {
// 判断silent属性,是否message提示http错误
ElMessage({message: data?.sub_msg || data?.msg, type: "error"});
return data || {};
}
return Promise.reject(response);
}
}
};
const responseErrorHandler = (error: ResponseError) => {
const {response}: any = error as ResponseError;
ElMessage({
message: response
? `${response.status}:${
response.data.message || response.data || response.statusText
}${response.data.data || ""}`
: `${error.config?.url}:${error.message}`,
type: "error",
grouping: true
});
return {};
};
instance.interceptors.request.use(requestInterceptor);
instance.interceptors.response.use(responseInterceptor, responseErrorHandler);
// InternalAxiosRequestConfig 的 headers为必填,为避免ts报异常,故使用Partial取部分字段,则可不用必填headers,以下相同
export const get = async <T>(
url: string,
params?: object,
config?: Partial<RequestConfig>
): Promise<T> => {
return await instance.get(url, {
params,
...(config ?? {})
});
};
export const post = async <T>(
url: string,
data?: object,
config?: Partial<RequestConfig>
): Promise<T> => {
return await instance.post(url, data, config);
};
选择器联动封装:
<el-form class="search-panel" inline>
<searchMultiCondition
ref="queryFormRef"
:form-select="QUERY_FORM_SELECT"
:model="queryForm"
:remote-method="remoteMethod"
>
<el-form-item :label="$swpT('page.swModuleDetailPlan.packageName')" prop="codeRepoName">
<el-input
v-model="queryForm.codeRepoName"
:placeholder="$swpT('common.pleaseInput')"
@keyup.enter="getTableData"
></el-input>
</el-form-item>
<el-form-item
><i class="fa-search"/>
<el-button icon="search" type="primary" @click="getTableData">{{ $swpT('common.search') }}</el-button>
<el-button icon="refresh" @click="resetSearch()">{{ $swpT('common.reset') }}</el-button>
</el-form-item>
</searchMultiCondition>
</el-form>
联动封装vue页面:
<template>
<el-form ref="queryFormRef" class="search-panel" inline>
<el-form-item
v-for="item in props.formSelect"
:key="item.filed"
:label="item.label"
:prop="item.filed"
>
<el-select
v-model="attrs['model'][item.filed]"
:filter-method="(value) => filterChangeData(value, item.filed)"
v-bind="item.prop"
@change="getAllSelectOptions"
>
<el-option
v-for="(i, index) in queryOptions[item.filed]"
:key="index"
:label="i[item.optionsValueKey?.label || ''] ?? i"
:value="i[item.optionsValueKey?.value || ''] ?? i"
></el-option>
</el-select>
</el-form-item>
<slot></slot>
</el-form>
</template>
<script lang="ts" setup>
import {Res} from "@/types/global";
import {http} from "@/utils";
import {defineExpose, defineProps, onMounted, ref, useAttrs, withDefaults} from "vue";
const attrs = useAttrs(); // 使用 useAttrs 钩子获取 $attrs
const queryOptions = ref<any>({});
const props = withDefaults(defineProps<{ formSelect: any, remoteMethod: any }>(), {
formSelect: [],
remoteMethod: {}
});
const queryFormRef = ref();
onMounted(() => {
getAllSelectOptions();
});
const getAllSelectOptions = async(value: any, type: any, remote: boolean) => {
const {method, url} = props.remoteMethod;
const params = remote ? {
projectSearch: type === "project" ? value : "",
ecuSearch: type === "ecu" ? value : "",
nodeSearch: type === "node" ? value : "",
baselineSearch: type === "baseline" ? value : ""
} : {
...attrs["model"]
};
const {code, biz_content} = await http[method as "get" | "post"]<Res>(
url,
params
);
if (code == 10000) {
queryOptions.value = biz_content;
}
};
// 筛选filter值
const filterChangeData = async(value: any, type: any) => {
if (value) {
await getAllSelectOptions(value, type, true);
}
};
// 重置
const resetFields = () => {
queryFormRef.value.resetFields();
getAllSelectOptions();
};
defineExpose({
resetFields
});
</script>