Commit 1b9070490b0d570c08a2a9c008ba8f2a7afecfcc
Merge branch 'perf/view-interface' into 'main_dev'
perf(src/views): 优化第三方API接口调用 See merge request yunteng/thingskit-view!264
Showing
24 changed files
with
1674 additions
and
140 deletions
... | ... | @@ -143,8 +143,10 @@ export const customRequest = async (request: RequestConfigType) => { |
143 | 143 | requestParamsBodyType, |
144 | 144 | requestOriginUrl, |
145 | 145 | requestVerificationToken, |
146 | - requestTokenKey, | |
147 | - requestTokenSuffix | |
146 | + pondRequestOriginUrl, | |
147 | + pondRequestGlobalTokenKey, | |
148 | + pondRequestGlobalTokenSuffix, | |
149 | + | |
148 | 150 | } = request as ExtraRequestConfigType |
149 | 151 | let { requestUrl } = request as ExtraRequestConfigType |
150 | 152 | const { Header, Body } = requestParams |
... | ... | @@ -169,7 +171,7 @@ export const customRequest = async (request: RequestConfigType) => { |
169 | 171 | { |
170 | 172 | url: |
171 | 173 | requestHttpType === RequestHttpEnum.GET.toUpperCase() ? requestUrl : `${requestUrl}?${objConvertQuery(Params)}`, |
172 | - baseURL: requestVerificationToken ? '' : getOriginUrl(requestOriginUrl!), // 如果是第三方接口,则无需添加baseURL | |
174 | + baseURL: requestVerificationToken ? pondRequestOriginUrl : getOriginUrl(requestOriginUrl!), | |
173 | 175 | method: requestHttpType, |
174 | 176 | params: requestHttpType === RequestHttpEnum.GET.toUpperCase() ? Params : null, |
175 | 177 | data: body, |
... | ... | @@ -180,8 +182,8 @@ export const customRequest = async (request: RequestConfigType) => { |
180 | 182 | apiUrl: '', |
181 | 183 | withShareToken: isShareMode(), |
182 | 184 | withThirdTokenString: requestVerificationToken, |
183 | - withThirdTokenKey: requestTokenKey, | |
184 | - withThirdTokenPrefix: requestTokenSuffix | |
185 | + withThirdTokenKey: pondRequestGlobalTokenKey, | |
186 | + withThirdTokenPrefix: pondRequestGlobalTokenSuffix | |
185 | 187 | } |
186 | 188 | ) |
187 | 189 | //ft | ... | ... |
1 | 1 | import {ref, toRefs, toRaw, watch} from 'vue' |
2 | 2 | import type VChart from 'vue-echarts' |
3 | -import {useChartDataPondFetch} from '@/hooks/' | |
4 | 3 | import {CreateComponentType, ChartFrameEnum, CreateComponentGroupType} from '@/packages/index.d' |
5 | 4 | import {useChartEditStore} from '@/store/modules/chartEditStore/chartEditStore' |
6 | 5 | import {isPreview, intervalUnitHandle} from '@/utils' |
7 | 6 | import {setOption} from '@/packages/public/chart' |
8 | 7 | import {useChartDataSocket} from './useChartDataSocket' |
9 | -import {customRequest} from '@/api/external/customRequest' | |
8 | +import {customRequest, customThirdInterfaceRequest, thirdInterfaceRequest} from '@/api/external/customRequest' | |
10 | 9 | import {useFilterFn} from './useFilterFn' |
11 | 10 | import {RequestContentTypeEnum} from '@/enums/external/httpEnum' |
12 | 11 | import dayjs from 'dayjs' |
12 | +import { RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
13 | +import { convertToCascadingData, findItemByLabel } from '@/utils/external/utils' | |
14 | +import { CascaderOption } from 'naive-ui' | |
13 | 15 | |
14 | 16 | |
15 | 17 | // 获取类型 |
... | ... | @@ -92,6 +94,38 @@ export const useChartDataFetch = ( |
92 | 94 | (toRaw(targetComponent.request).requestParams.Params.startTs as unknown as number) = startTs as unknown as number |
93 | 95 | (toRaw(targetComponent.request).requestParams.Params.endTs as unknown as number) = endTs as unknown as number |
94 | 96 | } |
97 | + //处理过期 | |
98 | + const findCurrentPond = () => { | |
99 | + return chartEditStore.getRequestGlobalConfig.requestDataPond.find((pondItem: RequestDataPondItemType) => | |
100 | + pondItem.dataPondId === targetComponent?.request?.requestDataPondId && targetComponent.request.thirdTokenIsExp) | |
101 | + } | |
102 | + const handleExecuteRequest = async () => { | |
103 | + try { | |
104 | + if(findCurrentPond()?.dataPondRequestConfig?.pondRequestOriginUrl){ | |
105 | + const originUrlString = findCurrentPond()?.dataPondRequestConfig?.pondRequestOriginUrl | |
106 | + const requestUrlString = findCurrentPond()?.dataPondRequestConfig?.pondRequestUrl | |
107 | + const body = findCurrentPond()?.dataPondRequestConfig?.pondRequestParams!['Body']['json'] | |
108 | + const request = { | |
109 | + requestOriginUrl: `${originUrlString}${requestUrlString}`, | |
110 | + body | |
111 | + } | |
112 | + const res = await customThirdInterfaceRequest(request as unknown as thirdInterfaceRequest) | |
113 | + const resOptions = convertToCascadingData(res) as unknown as CascaderOption[] | |
114 | + const findLabel = findItemByLabel(resOptions, targetComponent.request.thirdSelectCascaderLabel as string) | |
115 | + if(findLabel) { | |
116 | + targetComponent.request.requestVerificationToken = findLabel?.value | |
117 | + } | |
118 | + } | |
119 | + } catch (e) { | |
120 | + console.error(e) | |
121 | + } | |
122 | + } | |
123 | + // 定时时间 | |
124 | + const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value | |
125 | + // 单位 | |
126 | + const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value | |
127 | + setInterval(handleExecuteRequest, intervalUnitHandle(time, unit)) | |
128 | + // | |
95 | 129 | const res = await customRequest(toRaw(targetComponent.request)) |
96 | 130 | if (res) { |
97 | 131 | try { |
... | ... | @@ -126,6 +160,8 @@ export const useChartDataFetch = ( |
126 | 160 | const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value |
127 | 161 | // 开启轮询 |
128 | 162 | if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) |
163 | + | |
164 | + | |
129 | 165 | } |
130 | 166 | // eslint-disable-next-line no-empty |
131 | 167 | } catch (error) { | ... | ... |
... | ... | @@ -41,7 +41,29 @@ export const requestConfig: RequestConfigType = { |
41 | 41 | }, |
42 | 42 | Header: {}, |
43 | 43 | Params: {} |
44 | - } | |
44 | + }, | |
45 | + pondRequestOriginUrl: '', | |
46 | + pondRequestInterval: 0, | |
47 | + pondRequestIntervalUnit: '', | |
48 | + pondRequestGlobalTokenSuffix: '', | |
49 | + pondRequestGlobalTokenKey: '', | |
50 | + pondRequestHttpType: RequestHttpEnum.GET, | |
51 | + pondRequestContentType: RequestContentTypeEnum.DEFAULT, | |
52 | + pondRequestUrl:'', | |
53 | + pondRequestSQLContent: { | |
54 | + sql: '' | |
55 | + }, | |
56 | + pondRequestParamsBodyType: RequestBodyEnum.NONE, | |
57 | + pondRequestParams: { | |
58 | + "Body": { | |
59 | + "form-data": {}, | |
60 | + "x-www-form-urlencoded": {}, | |
61 | + "json": "", | |
62 | + "xml": "" | |
63 | + }, | |
64 | + "Header": {}, | |
65 | + "Params": {} | |
66 | + } | |
45 | 67 | } |
46 | 68 | |
47 | 69 | // 单实例类 | ... | ... |
... | ... | @@ -196,6 +196,10 @@ export interface RequestGlobalConfigType extends RequestPublicConfigType { |
196 | 196 | requestTokenKey?: string |
197 | 197 | // 请求头里的Token前缀 |
198 | 198 | requestTokenSuffix?: string, |
199 | + // 全局配置Token前缀 | |
200 | + requestGlobalTokenSuffix?: string, | |
201 | + // 全局配置Token键 | |
202 | + requestGlobalTokenKey?: string, | |
199 | 203 | } |
200 | 204 | |
201 | 205 | // 单个图表请求配置 |
... | ... | @@ -218,12 +222,34 @@ export interface RequestConfigType extends RequestPublicConfigType { |
218 | 222 | requestSQLContent: { |
219 | 223 | sql: string |
220 | 224 | } |
225 | + | |
221 | 226 | // Token |
222 | 227 | requestVerificationToken?: string |
223 | 228 | // 请求头里的Token键 |
224 | 229 | requestTokenKey?: string |
225 | 230 | // 请求头里的Token前缀 |
226 | 231 | requestTokenSuffix?: string |
232 | + pondRequestOriginUrl?: string | |
233 | + pondRequestInterval?: number | |
234 | + pondRequestIntervalUnit?: string | |
235 | + pondRequestGlobalTokenSuffix?: string | |
236 | + pondRequestGlobalTokenKey?: string | |
237 | + pondRequestGlobalCascaderOption?: Recordable | |
238 | + pondRequestGlobalCascaderKey?: string | |
239 | + pondRequestHttpType?: string | |
240 | + pondRequestInterval?: number | |
241 | + pondRequestIntervalUnit?: string | |
242 | + pondRequestContentType?: number | |
243 | + pondRequestHttpType?: string | |
244 | + pondRequestParams?: RequestParams | |
245 | + pondRequestParamsBodyType?: RequestBodyEnum | |
246 | + pondRequestUrl?: string | |
247 | + pondRequestSQLContent?: { | |
248 | + sql: string | |
249 | + } | |
250 | + thirdTokenIsExp?: boolean | |
251 | + thirdSelectCascaderLabel?: string | |
252 | + publicInterfaceSelectId?: string | |
227 | 253 | } |
228 | 254 | |
229 | 255 | // Store 类型 | ... | ... |
... | ... | @@ -11,6 +11,7 @@ import { ContentTypeEnum, RequestEnum } from '@/enums/external/httpEnum'; |
11 | 11 | import omit from 'lodash/omit'; |
12 | 12 | import cloneDeep from 'lodash/cloneDeep'; |
13 | 13 | import { useUserStore } from '@/store/external/modules/user'; |
14 | +import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'; | |
14 | 15 | |
15 | 16 | export * from './axiosTransform'; |
16 | 17 | |
... | ... | @@ -115,6 +116,8 @@ export class VAxios { |
115 | 116 | this.axiosInstance.interceptors.request.use(async (config: AxiosRequestConfig) => { |
116 | 117 | // If cancel repeat request is turned on, then cancel repeat request is prohibited |
117 | 118 | const userStore = useUserStore(); |
119 | + const { targetData } = useTargetData() | |
120 | + | |
118 | 121 | // if (userStore && (userStore.jwtToken || userStore.shareJwtToken)) { |
119 | 122 | if (userStore) { |
120 | 123 | try { |
... | ... | @@ -125,7 +128,7 @@ export class VAxios { |
125 | 128 | const res = jwt_decode(withThirdTokenString) as Recordable; |
126 | 129 | const currentTime = (new Date().getTime() + (config.timeout || 0)) / 1000; |
127 | 130 | if (currentTime >= res?.exp) { |
128 | - window['$message'].warning('第三方平台Token已经失效,请您重新返回设计页面点击执行请求获取最新Token') | |
131 | + targetData.value.request.thirdTokenIsExp = true | |
129 | 132 | } |
130 | 133 | } else { |
131 | 134 | //此平台逻辑 | ... | ... |
... | ... | @@ -65,3 +65,19 @@ export const convertToCascadingData = (obj: Recordable): cascadingData[] => { |
65 | 65 | } |
66 | 66 | return result |
67 | 67 | } |
68 | + | |
69 | +// 递归查找 | |
70 | +export const findItemByLabel = (list:Recordable[], label:string) :Recordable | null => { | |
71 | + let res = list.find((item) => item.label == label) as Recordable | null; | |
72 | + if (res) { | |
73 | + return res; | |
74 | + } else { | |
75 | + for (let i = 0; i < list.length; i++) { | |
76 | + if (list[i].children instanceof Array && list[i].children.length > 0) { | |
77 | + res = findItemByLabel(list[i].children, label); | |
78 | + if (res) return res; | |
79 | + } | |
80 | + } | |
81 | + return null; | |
82 | + } | |
83 | +}; | ... | ... |
... | ... | @@ -41,6 +41,14 @@ |
41 | 41 | 编辑配置 |
42 | 42 | </n-button> |
43 | 43 | </setting-item-box> |
44 | + <setting-item-box name="配置"> | |
45 | + <setting-item name="Token前缀"> | |
46 | + <n-input :disabled="editDisabled" v-model:value.trim="requestGlobalTokenSuffix" type="text" placeholder="例如:Bearer" /> | |
47 | + </setting-item> | |
48 | + <setting-item name="Token键值"> | |
49 | + <n-input :disabled="editDisabled" v-model:value.trim="requestGlobalTokenKey" type="text" placeholder="例如:Token" /> | |
50 | + </setting-item> | |
51 | + </setting-item-box> | |
44 | 52 | <!-- table 内容体 --> |
45 | 53 | <n-collapse-transition :show="showTable"> |
46 | 54 | <request-global-header-table :editDisabled="editDisabled"></request-global-header-table> |
... | ... | @@ -69,13 +77,13 @@ import { ref, toRefs, computed } from 'vue' |
69 | 77 | import { useDesignStore } from '@/store/modules/designStore/designStore' |
70 | 78 | import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' |
71 | 79 | import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook' |
72 | -import { selectTypeOptions, selectTimeOptions } from '@/views/chart/ContentConfigurations/components/ChartData/index.d' | |
80 | +import { selectTimeOptions } from '@/views/chart/ContentConfigurations/components/ChartData/index.d' | |
73 | 81 | import { RequestGlobalHeaderTable } from '../RequestGlobalHeaderTable' |
74 | 82 | import { icon } from '@/plugins' |
75 | 83 | |
76 | 84 | const { PencilIcon, ChevronDownOutlineIcon, ChevronUpOutlineIcon } = icon.ionicons5 |
77 | 85 | const { chartEditStore } = useTargetData() |
78 | -const { requestOriginUrl, requestInterval, requestIntervalUnit } = toRefs(chartEditStore.getRequestGlobalConfig) | |
86 | +const { requestOriginUrl, requestInterval, requestIntervalUnit, requestGlobalTokenSuffix, requestGlobalTokenKey } = toRefs(chartEditStore.getRequestGlobalConfig) | |
79 | 87 | const editDisabled = ref(true) |
80 | 88 | |
81 | 89 | const designStore = useDesignStore() | ... | ... |
1 | +<template> | |
2 | + <n-table | |
3 | + :bordered="false" | |
4 | + :single-line="false" | |
5 | + size="small" | |
6 | + style="border-bottom-right-radius: 7px; border-bottom-left-radius: 7px" | |
7 | + > | |
8 | + <thead> | |
9 | + <tr> | |
10 | + <th>key</th> | |
11 | + <th>value</th> | |
12 | + </tr> | |
13 | + </thead> | |
14 | + <tbody> | |
15 | + <tr v-for="(item, index) in tableArray.content" :key="index"> | |
16 | + <td> | |
17 | + {{ item.key || '暂无'}} | |
18 | + </td> | |
19 | + <td> | |
20 | + {{ item.value || '暂无'}} | |
21 | + </td> | |
22 | + </tr> | |
23 | + </tbody> | |
24 | + </n-table> | |
25 | +</template> | |
26 | + | |
27 | +<script setup lang="ts"> | |
28 | +import { PropType, reactive, ref, toRefs, watch } from 'vue' | |
29 | +import { RequestParamsObjType } from '@/enums/httpEnum' | |
30 | +import noData from '@/assets/images/canvas/noData.png' | |
31 | + | |
32 | +const props = defineProps({ | |
33 | + target: Object as PropType<RequestParamsObjType> | |
34 | +}) | |
35 | + | |
36 | +// 默认表格 | |
37 | +const defaultItem = { | |
38 | + key: '', | |
39 | + value: '' | |
40 | +} | |
41 | +const tableArray = reactive<{ | |
42 | + content: typeof defaultItem[] | |
43 | +}>({ content: [] }) | |
44 | + | |
45 | +// 监听选项 | |
46 | +watch( | |
47 | + () => props.target as RequestParamsObjType, | |
48 | + (target: RequestParamsObjType) => { | |
49 | + tableArray.content = [] | |
50 | + for (const k in target) { | |
51 | + tableArray.content.push({ | |
52 | + key: k, | |
53 | + value: target[k] | |
54 | + }) | |
55 | + } | |
56 | + // 默认值 | |
57 | + if (!tableArray.content.length) tableArray.content = [JSON.parse(JSON.stringify(defaultItem))] | |
58 | + }, | |
59 | + { | |
60 | + immediate: true, | |
61 | + deep: true | |
62 | + } | |
63 | +) | |
64 | +</script> | |
\ No newline at end of file | ... | ... |
1 | +<template> | |
2 | + <div class="go-chart-data-display"> | |
3 | + <n-scrollbar style="max-height: 570px"> | |
4 | + <div class="go-mr-3"> | |
5 | + <div> | |
6 | + <setting-item-box name="主体信息"> | |
7 | + <setting-item name="接口名称"> | |
8 | + <n-input size="small" :placeholder="targetData?.dataPondName || '暂无'" :disabled="true"> </n-input> | |
9 | + </setting-item> | |
10 | + <setting-item name="接口类型"> | |
11 | + <n-input size="small" :placeholder="pondRequestHttpType || '暂无'" :disabled="true"></n-input> | |
12 | + </setting-item> | |
13 | + </setting-item-box> | |
14 | + | |
15 | + <setting-item-box> | |
16 | + <setting-item name="组件间隔"> | |
17 | + <n-input size="small" :placeholder="`${requestInterval || '暂无'}`" :disabled="true"> | |
18 | + <template #suffix> | |
19 | + {{ targetData && SelectHttpTimeNameObj[requestIntervalUnit] }} | |
20 | + </template> | |
21 | + </n-input> | |
22 | + </setting-item> | |
23 | + <setting-item name="全局间隔(默认)"> | |
24 | + <n-input size="small" :placeholder="`${globalData?.requestInterval || '暂无'}`" :disabled="true"> | |
25 | + <template #suffix> {{ globalData && SelectHttpTimeNameObj[globalData.requestIntervalUnit] }} </template> | |
26 | + </n-input> | |
27 | + </setting-item> | |
28 | + </setting-item-box> | |
29 | + | |
30 | + <setting-item-box name="源地址" :alone="true"> | |
31 | + <n-input size="small" :placeholder="pondRequestOriginUrl || '暂无'" :disabled="true"> | |
32 | + <template #prefix> | |
33 | + <n-icon :component="PulseIcon" /> | |
34 | + </template> | |
35 | + </n-input> | |
36 | + </setting-item-box> | |
37 | + | |
38 | + <setting-item-box name="接口地址" :alone="true"> | |
39 | + <n-input | |
40 | + size="small" | |
41 | + :placeholder="pondRequestUrl || '暂无'" | |
42 | + :disabled="true" | |
43 | + > | |
44 | + <template #prefix> | |
45 | + <n-icon :component="FlashIcon" /> | |
46 | + </template> | |
47 | + </n-input> | |
48 | + </setting-item-box> | |
49 | + </div> | |
50 | + <n-divider /> | |
51 | + <setting-item-box name="类型"> | |
52 | + <setting-item name="配置类型"> | |
53 | + <n-input | |
54 | + size="small" | |
55 | + :placeholder="targetData && requestContentTypeObj[requestContentType]" | |
56 | + :disabled="true" | |
57 | + ></n-input> | |
58 | + </setting-item> | |
59 | + <setting-item name="body 类型" v-if="requestContentType === RequestContentTypeEnum.DEFAULT"> | |
60 | + <n-input size="small" :placeholder="targetData && requestParamsBodyType" :disabled="true"></n-input> | |
61 | + </setting-item> | |
62 | + </setting-item-box> | |
63 | + <div v-if="requestContentType === RequestContentTypeEnum.DEFAULT"> | |
64 | + <n-tabs type="line" animated v-model:value="tabValue"> | |
65 | + <n-tab v-for="item in RequestParamsTypeEnum" :key="item" :name="item" :tab="item"> {{ item }} </n-tab> | |
66 | + </n-tabs> | |
67 | + <!-- 各个页面 --> | |
68 | + <div class="go-mt-3"> | |
69 | + <div v-if="tabValue !== RequestParamsTypeEnum.BODY"> | |
70 | + <display-table class="go-my-3" :target="requestParams[tabValue]"> </display-table> | |
71 | + </div> | |
72 | + | |
73 | + <!-- 选择了 body --> | |
74 | + <div v-else> | |
75 | + <!-- 为 none 时 --> | |
76 | + <n-card class="go-mt-3 go-pb-3" v-if="requestParamsBodyType === RequestBodyEnum['NONE']"> | |
77 | + <n-text depth="3">该接口没有 Body 体</n-text> | |
78 | + </n-card> | |
79 | + | |
80 | + <!-- 具有对象属性时 --> | |
81 | + <template | |
82 | + v-else-if=" | |
83 | + requestParamsBodyType === RequestBodyEnum['FORM_DATA'] || | |
84 | + requestParamsBodyType === RequestBodyEnum['X_WWW_FORM_URLENCODED'] | |
85 | + " | |
86 | + > | |
87 | + <display-table | |
88 | + class="go-my-3" | |
89 | + :target="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]" | |
90 | + ></display-table> | |
91 | + </template> | |
92 | + | |
93 | + <!-- json --> | |
94 | + <template v-else-if="requestParamsBodyType === RequestBodyEnum['JSON']"> | |
95 | + <n-card size="small" style="padding-bottom: 7px"> | |
96 | + <n-code | |
97 | + :code="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType] || '暂无内容'" | |
98 | + language="json" | |
99 | + ></n-code> | |
100 | + </n-card> | |
101 | + </template> | |
102 | + | |
103 | + <!-- xml --> | |
104 | + <template v-else-if="requestParamsBodyType === RequestBodyEnum['XML']"> | |
105 | + <n-code | |
106 | + :code="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType] || ''" | |
107 | + language="html" | |
108 | + ></n-code> | |
109 | + </template> | |
110 | + </div> | |
111 | + </div> | |
112 | + </div> | |
113 | + <!-- SQL 请求 --> | |
114 | + <div v-else> | |
115 | + <setting-item-box name="键名"> | |
116 | + <n-text>sql</n-text> | |
117 | + </setting-item-box> | |
118 | + <setting-item-box name="键值"> | |
119 | + <n-code :code="requestSQLContent.sql || ''" language="sql"></n-code> | |
120 | + </setting-item-box> | |
121 | + </div> | |
122 | + </div> | |
123 | + </n-scrollbar> | |
124 | + </div> | |
125 | +</template> | |
126 | + | |
127 | +<script setup lang="ts"> | |
128 | +import { PropType, ref, toRefs } from 'vue' | |
129 | +import { icon } from '@/plugins' | |
130 | +import { MonacoEditor } from '@/components/Pages/MonacoEditor' | |
131 | +import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
132 | +import { RequestDataPondItemType, RequestGlobalConfigType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
133 | +import displayTable from './displayTable.vue' | |
134 | +import { | |
135 | + RequestBodyEnum, | |
136 | + RequestParamsTypeEnum, | |
137 | + SelectHttpTimeNameObj, | |
138 | + RequestContentTypeEnum, | |
139 | + RequestBodyEnumList, | |
140 | + RequestParamsObjType | |
141 | +} from '@/enums/httpEnum' | |
142 | + | |
143 | +const props = defineProps({ | |
144 | + globalData: Object as PropType<RequestGlobalConfigType>, | |
145 | + targetData: Object as PropType<RequestDataPondItemType> | |
146 | +}) | |
147 | + | |
148 | +const { HelpOutlineIcon, FlashIcon, PulseIcon } = icon.ionicons5 | |
149 | +const { | |
150 | + requestUrl, | |
151 | + requestInterval, | |
152 | + requestHttpType, | |
153 | + requestContentType, | |
154 | + requestSQLContent, | |
155 | + requestParams, | |
156 | + requestParamsBodyType, | |
157 | + requestIntervalUnit, | |
158 | + pondRequestOriginUrl, | |
159 | + pondRequestUrl, | |
160 | + pondRequestHttpType | |
161 | +} = toRefs((props.targetData as RequestDataPondItemType).dataPondRequestConfig) | |
162 | +const tabs = [RequestParamsTypeEnum.HEADER] | |
163 | +const requestContentTypeObj = { | |
164 | + [RequestContentTypeEnum.DEFAULT]: '普通请求', | |
165 | + [RequestContentTypeEnum.SQL]: 'SQL 请求' | |
166 | +} | |
167 | +const tabValue = ref<RequestParamsTypeEnum>(RequestParamsTypeEnum.PARAMS) | |
168 | + | |
169 | +// 更新参数表格数据 | |
170 | +const updateRequestParams = (paramsObj: RequestParamsObjType) => { | |
171 | + if (tabValue.value !== RequestParamsTypeEnum.BODY) { | |
172 | + requestParams.value[tabValue.value] = paramsObj | |
173 | + } | |
174 | +} | |
175 | + | |
176 | +// 更新参数表格数据 | |
177 | +const updateRequestBodyTable = (paramsObj: RequestParamsObjType) => { | |
178 | + if ( | |
179 | + tabValue.value === RequestParamsTypeEnum.BODY && | |
180 | + // 仅有两种类型有 body | |
181 | + (requestParamsBodyType.value === RequestBodyEnum.FORM_DATA || | |
182 | + requestParamsBodyType.value === RequestBodyEnum.X_WWW_FORM_URLENCODED) | |
183 | + ) { | |
184 | + requestParams.value[RequestParamsTypeEnum.BODY][requestParamsBodyType.value] = paramsObj | |
185 | + } | |
186 | +} | |
187 | +</script> | |
188 | + | |
189 | +<style lang="scss" scoped> | |
190 | +@include go('chart-data-display') { | |
191 | + flex: 1; | |
192 | +} | |
193 | +</style> | ... | ... |
1 | +<template> | |
2 | + <n-modal class="go-chart-data-pond-control" v-model:show="modelShowRef" :mask-closable="false"> | |
3 | + <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 900px; height: 650px"> | |
4 | + <template #header></template> | |
5 | + <template #header-extra> </template> | |
6 | + <div class="pond-content"> | |
7 | + <!-- 展示区域 --> | |
8 | + <chart-data-display | |
9 | + v-if="pondData && !loading" | |
10 | + :targetData="pondData" | |
11 | + :globalData="chartEditStore.getRequestGlobalConfig" | |
12 | + ></chart-data-display> | |
13 | + <!-- 无数据 --> | |
14 | + <div v-else class="no-data go-flex-center"> | |
15 | + <img :src="noData" alt="暂无数据" /> | |
16 | + <n-text :depth="3">暂未选择公共接口</n-text> | |
17 | + </div> | |
18 | + <!-- 左侧列表 --> | |
19 | + <chart-data-pond-list @createPond="createPond" @deletePond="deletePond"></chart-data-pond-list> | |
20 | + </div> | |
21 | + <!-- 底部 --> | |
22 | + <template #action> | |
23 | + <n-space justify="space-between"> | |
24 | + <n-button type="info" secondary :disabled="!pondData" @click="openPond(true)"> | |
25 | + 编辑内容 | |
26 | + <template #icon> | |
27 | + <n-icon> | |
28 | + <pencil-icon /> | |
29 | + </n-icon> | |
30 | + </template> | |
31 | + </n-button> | |
32 | + <n-button type="primary" @click="closeHandle">保存 & 关闭</n-button> | |
33 | + </n-space> | |
34 | + </template> | |
35 | + </n-card> | |
36 | + </n-modal> | |
37 | + <!-- 请求配置model --> | |
38 | + <pond-data-request | |
39 | + v-model:modelShow="requestShow" | |
40 | + :targetDataRequest="editData" | |
41 | + :isEdit="isEdit" | |
42 | + @editSaveHandle="saveHandle" | |
43 | + ></pond-data-request> | |
44 | +</template> | |
45 | + | |
46 | +<script setup lang="ts"> | |
47 | +import { ref, toRefs, computed, nextTick, watch, toRaw } from 'vue' | |
48 | +import noData from '@/assets/images/canvas/noData.png' | |
49 | +import { ChartDataPondList } from '../ChartDataPondList' | |
50 | +import PondDataRequest from '../../../RequestModal/PondDataRequest.vue' | |
51 | +import { ChartDataDisplay } from '../ChartDataDisplay' | |
52 | +import { requestConfig } from '@/packages/public/publicConfig' | |
53 | +import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook' | |
54 | +import { RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
55 | +import { RequestBodyEnum, RequestContentTypeEnum, RequestDataTypeEnum, RequestHttpEnum } from '@/enums/httpEnum' | |
56 | +import { icon } from '@/plugins' | |
57 | +import { getUUID, goDialog } from '@/utils' | |
58 | +import { cloneDeep } from 'lodash' | |
59 | + | |
60 | +const props = defineProps({ | |
61 | + modelShow: Boolean | |
62 | +}) | |
63 | + | |
64 | +const emit = defineEmits(['update:modelShow', 'sendHandle']) | |
65 | + | |
66 | +const { PencilIcon } = icon.ionicons5 | |
67 | + | |
68 | +const { chartEditStore, targetData } = useTargetData() | |
69 | + | |
70 | +const { requestDataPond } = toRefs(chartEditStore.getRequestGlobalConfig) | |
71 | + | |
72 | +const requestShow = ref(false) | |
73 | + | |
74 | +const modelShowRef = ref(false) | |
75 | + | |
76 | +const loading = ref(false) | |
77 | + | |
78 | +const isEdit = ref(false) | |
79 | + | |
80 | +const editData = ref<RequestDataPondItemType>() | |
81 | + | |
82 | +// 所选数据池 | |
83 | +const pondData = computed(() => { | |
84 | + const selectId = targetData?.value?.request?.requestDataPondId | |
85 | + if (!selectId) return undefined | |
86 | + const data = requestDataPond.value.filter(item => { | |
87 | + return selectId === item.dataPondId | |
88 | + }) | |
89 | + return data[0] | |
90 | +}) | |
91 | + | |
92 | +watch(() => props.modelShow, (newValue) => { | |
93 | + if (newValue) { | |
94 | + modelShowRef.value = true | |
95 | + } else { | |
96 | + modelShowRef.value = false | |
97 | + } | |
98 | +}) | |
99 | + | |
100 | +const cacheEditData = ref<RequestDataPondItemType>() | |
101 | + | |
102 | +watch( | |
103 | + () => pondData.value, | |
104 | + newValue => { | |
105 | + loading.value = true | |
106 | + editData.value = newValue | |
107 | + cacheEditData.value = editData.value | |
108 | + nextTick(() => { | |
109 | + loading.value = false | |
110 | + }) | |
111 | + }, | |
112 | + { | |
113 | + immediate: true | |
114 | + } | |
115 | +) | |
116 | + | |
117 | +// 打开/编辑 | |
118 | +const openPond = (isEditFlag = false) => { | |
119 | + isEdit.value = !!isEditFlag | |
120 | + requestShow.value = true | |
121 | + editData.value = cacheEditData.value || { | |
122 | + dataPondId: getUUID(), | |
123 | + dataPondName: getUUID(), | |
124 | + dataPondRequestConfig: { | |
125 | + requestDataPondId: '', | |
126 | + requestInterval: 0, | |
127 | + requestDataType: RequestDataTypeEnum.AJAX, | |
128 | + requestHttpType: RequestHttpEnum.GET, | |
129 | + requestUrl: '', | |
130 | + requestContentType: RequestContentTypeEnum.DEFAULT, | |
131 | + requestParamsBodyType: RequestBodyEnum.NONE, | |
132 | + requestSQLContent: { | |
133 | + sql: '' | |
134 | + }, | |
135 | + requestVerificationToken: '', | |
136 | + requestTokenKey: '', | |
137 | + requestTokenSuffix: '', | |
138 | + requestParams: { | |
139 | + "Body": { | |
140 | + "form-data": {}, | |
141 | + "x-www-form-urlencoded": {}, | |
142 | + "json": "", | |
143 | + "xml": "" | |
144 | + }, | |
145 | + "Header": {}, | |
146 | + "Params": {} | |
147 | + }, | |
148 | + pondRequestOriginUrl: '', | |
149 | + pondRequestInterval: 0, | |
150 | + pondRequestIntervalUnit: '', | |
151 | + pondRequestGlobalTokenSuffix: '', | |
152 | + pondRequestGlobalTokenKey: '', | |
153 | + pondRequestHttpType:'', | |
154 | + pondRequestUrl:'', | |
155 | + pondRequestContentType:RequestContentTypeEnum.DEFAULT, | |
156 | + pondRequestSQLContent: { | |
157 | + sql: '' | |
158 | + }, | |
159 | + pondRequestParamsBodyType: RequestBodyEnum.NONE, | |
160 | + pondRequestParams: { | |
161 | + "Body": { | |
162 | + "form-data": {}, | |
163 | + "x-www-form-urlencoded": {}, | |
164 | + "json": "", | |
165 | + "xml": "" | |
166 | + }, | |
167 | + "Header": {}, | |
168 | + "Params": {} | |
169 | + } | |
170 | + } | |
171 | + } as unknown as RequestDataPondItemType | |
172 | +} | |
173 | + | |
174 | +// 创建 | |
175 | +const createPond = () => { | |
176 | + const id = getUUID() | |
177 | + editData.value = { | |
178 | + dataPondId: id, | |
179 | + dataPondName: id, | |
180 | + dataPondRequestConfig: cloneDeep({ ...requestConfig, requestDataType: RequestDataTypeEnum.Pond }) | |
181 | + } | |
182 | + cacheEditData.value = editData.value | |
183 | + openPond() | |
184 | +} | |
185 | + | |
186 | +// 完成创建/编辑 | |
187 | +const saveHandle = (newData: RequestDataPondItemType) => { | |
188 | + // 走创建 | |
189 | + if (isEdit.value) { | |
190 | + editSaveHandle(newData) | |
191 | + } else { | |
192 | + createSaveHandle(newData) | |
193 | + } | |
194 | + isEdit.value = false | |
195 | + requestShow.value = false | |
196 | +} | |
197 | + | |
198 | +// 编辑保存之后 | |
199 | +const editSaveHandle = (newData: RequestDataPondItemType) => { | |
200 | + try { | |
201 | + const targetIndex = requestDataPond.value.findIndex(item => item.dataPondId === newData.dataPondId) | |
202 | + if (targetIndex !== -1) { | |
203 | + requestDataPond.value.splice(targetIndex, 1, newData) | |
204 | + // 修改数据池后, 修改所有关联的组件 | |
205 | + chartEditStore.getComponentList.forEach(item => { | |
206 | + if ( | |
207 | + item.request.requestDataType === RequestDataTypeEnum.Pond && | |
208 | + item.request.requestDataPondId === newData.dataPondId | |
209 | + ) { | |
210 | + item.request = { | |
211 | + ...toRaw(newData.dataPondRequestConfig), | |
212 | + requestDataPondId: newData.dataPondId | |
213 | + } | |
214 | + } | |
215 | + }) | |
216 | + window.$message.success('保存成功!') | |
217 | + } else { | |
218 | + window.$message.error('编辑失败,请稍后重试!') | |
219 | + } | |
220 | + } catch (error) { | |
221 | + window.$message.error('编辑失败,请稍后重试!') | |
222 | + } | |
223 | +} | |
224 | + | |
225 | +// 创建保存成功之后 | |
226 | +const createSaveHandle = (newData: RequestDataPondItemType) => { | |
227 | + try { | |
228 | + if (editData.value) { | |
229 | + requestDataPond.value.unshift(newData) | |
230 | + window.$message.success('创建成功!') | |
231 | + } else { | |
232 | + window.$message.error('创建失败,请稍后重试!') | |
233 | + } | |
234 | + } catch (error) { | |
235 | + window.$message.error('创建失败,请稍后重试!') | |
236 | + } | |
237 | +} | |
238 | + | |
239 | +// 删除数据池 | |
240 | +const deletePond = (targetData: RequestDataPondItemType) => { | |
241 | + goDialog({ | |
242 | + message: '删除数据后,需手动处理使用改接口的组件,是否继续?', | |
243 | + isMaskClosable: true, | |
244 | + transformOrigin: 'center', | |
245 | + onPositiveCallback: () => { | |
246 | + const targetIndex = requestDataPond.value.findIndex(item => item.dataPondId === targetData.dataPondId) | |
247 | + if (targetIndex !== -1) { | |
248 | + requestDataPond.value.splice(targetIndex, 1) | |
249 | + window.$message.success('删除成功!') | |
250 | + } else { | |
251 | + window.$message.error('删除失败,请稍后重试!') | |
252 | + } | |
253 | + } | |
254 | + }) | |
255 | +} | |
256 | + | |
257 | +// 关闭 | |
258 | +const closeHandle = () => { | |
259 | + // 将所选内容赋值给对象 | |
260 | + if (pondData.value) { | |
261 | + targetData.value.request = { | |
262 | + ...toRaw(pondData.value.dataPondRequestConfig), | |
263 | + requestDataPondId: pondData.value.dataPondId | |
264 | + } | |
265 | + } | |
266 | + emit('update:modelShow', false) | |
267 | +} | |
268 | +</script> | |
269 | + | |
270 | +<style lang="scss" scoped> | |
271 | +@include go('chart-data-pond-control') { | |
272 | + /* 中间 */ | |
273 | + .pond-content { | |
274 | + display: flex; | |
275 | + } | |
276 | + .no-data { | |
277 | + flex-direction: column; | |
278 | + width: 100%; | |
279 | + img { | |
280 | + width: 200px; | |
281 | + } | |
282 | + } | |
283 | + &.n-card.n-modal, | |
284 | + .n-card { | |
285 | + @extend .go-background-filter; | |
286 | + } | |
287 | + .n-card-shallow { | |
288 | + background-color: rgba(0, 0, 0, 0) !important; | |
289 | + } | |
290 | + @include deep() { | |
291 | + & > .n-card__content { | |
292 | + padding-right: 0; | |
293 | + } | |
294 | + .n-card__content { | |
295 | + padding-bottom: 5px; | |
296 | + } | |
297 | + } | |
298 | +} | |
299 | +</style> | ... | ... |
1 | +<template> | |
2 | + <div class="go-chart-data-pond-list"> | |
3 | + <n-timeline class="pond-item-timeline" style="width: 20px"> | |
4 | + <n-timeline-item type="info"> </n-timeline-item> | |
5 | + <n-timeline-item type="success"></n-timeline-item> | |
6 | + </n-timeline> | |
7 | + <div class="pond-item-box"> | |
8 | + <!-- 新增 --> | |
9 | + <n-button class="create-btn go-py-4" ghost @click="createPond"> | |
10 | + <span> 创建 </span> | |
11 | + <template #icon> | |
12 | + <n-icon> | |
13 | + <DuplicateOutlineIcon></DuplicateOutlineIcon> | |
14 | + </n-icon> | |
15 | + </template> | |
16 | + </n-button> | |
17 | + <n-divider style="margin: 10px 0"></n-divider> | |
18 | + <n-space v-if="!requestDataPond.length" justify="center"> | |
19 | + <n-text class="not-layer-text" :depth="3"> | |
20 | + 暂无数据内容, | |
21 | + <n-a @click="createPond">立即创建</n-a> | |
22 | + </n-text> | |
23 | + </n-space> | |
24 | + <n-scrollbar style="max-height: 490px"> | |
25 | + <div | |
26 | + v-for="item in requestDataPond" | |
27 | + :key="item.dataPondId" | |
28 | + :class="{ select: item.dataPondId === selectPondId }" | |
29 | + class="pond-item" | |
30 | + @click="clickHandle(item)" | |
31 | + > | |
32 | + <div class="item-content"> | |
33 | + <div class="item-content-body"> | |
34 | + <div> | |
35 | + <n-tag class="go-mr-1" :type="item.dataPondId === selectPondId ? 'warning' : ''" :bordered="false"> | |
36 | + 名称 | |
37 | + </n-tag> | |
38 | + <n-ellipsis style="max-width: 180px"> | |
39 | + {{ item.dataPondName || '暂无' }} | |
40 | + </n-ellipsis> | |
41 | + </div> | |
42 | + <div> | |
43 | + <n-tag class="go-mr-1" :type="item.dataPondId === selectPondId ? 'warning' : ''" :bordered="false"> | |
44 | + 地址 | |
45 | + </n-tag> | |
46 | + <n-ellipsis style="max-width: 180px"> | |
47 | + {{ item.dataPondRequestConfig.pondRequestUrl || '暂无' }} | |
48 | + </n-ellipsis> | |
49 | + </div> | |
50 | + </div> | |
51 | + <div class="item-content-icon go-transition-quick" @click="deletePond($event, item)"> | |
52 | + <n-icon> | |
53 | + <trash-icon></trash-icon> | |
54 | + </n-icon> | |
55 | + </div> | |
56 | + </div> | |
57 | + <div :class="{ 'select-modal': item.dataPondId === selectPondId }"></div> | |
58 | + </div> | |
59 | + </n-scrollbar> | |
60 | + </div> | |
61 | + </div> | |
62 | +</template> | |
63 | + | |
64 | +<script setup lang="ts"> | |
65 | +import { toRefs, computed } from 'vue' | |
66 | +import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook' | |
67 | +import { useDesignStore } from '@/store/modules/designStore/designStore' | |
68 | +import { RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
69 | +import { icon } from '@/plugins' | |
70 | + | |
71 | +const emit = defineEmits(['createPond', 'deletePond']) | |
72 | + | |
73 | +const { DuplicateOutlineIcon, TrashIcon } = icon.ionicons5 | |
74 | +const designStore = useDesignStore() | |
75 | +const { chartEditStore, targetData } = useTargetData() | |
76 | +const { requestDataPond } = toRefs(chartEditStore.getRequestGlobalConfig) | |
77 | + | |
78 | +// 选中的全局数据 | |
79 | +const selectPondId = computed(() => { | |
80 | + return targetData.value.request.requestDataPondId | |
81 | +}) | |
82 | + | |
83 | +// 颜色 | |
84 | +const themeColor = computed(() => { | |
85 | + return designStore.getAppTheme | |
86 | +}) | |
87 | + | |
88 | +// 创建数据池 | |
89 | +const createPond = () => { | |
90 | + emit('createPond', true) | |
91 | +} | |
92 | + | |
93 | +// 删除数据池 | |
94 | +const deletePond = (target: Event, targetData: RequestDataPondItemType) => { | |
95 | + target.stopPropagation() | |
96 | + target.preventDefault() | |
97 | + emit('deletePond', targetData) | |
98 | +} | |
99 | + | |
100 | +// 选中 | |
101 | +const clickHandle = (item: RequestDataPondItemType) => { | |
102 | + targetData.value.request.requestDataPondId = item.dataPondId | |
103 | +} | |
104 | +</script> | |
105 | + | |
106 | +<style scoped lang="scss"> | |
107 | +$height: 530px; | |
108 | +$listWidth: 280px; | |
109 | +$centerHeight: 60px; | |
110 | +$centerMiniHeight: 28px; | |
111 | +$textSize: 10px; | |
112 | + | |
113 | +@include go('chart-data-pond-list') { | |
114 | + padding-top: 10px; | |
115 | + padding-bottom: 5px; | |
116 | + margin-right: 5px; | |
117 | + display: flex; | |
118 | + .pond-item-timeline > .n-timeline-item { | |
119 | + &:first-child { | |
120 | + height: $height; | |
121 | + } | |
122 | + } | |
123 | + .pond-item-box { | |
124 | + width: $listWidth; | |
125 | + position: relative; | |
126 | + .create-btn { | |
127 | + width: calc(#{$listWidth - 15px}); | |
128 | + margin-right: 15px; | |
129 | + } | |
130 | + .pond-item { | |
131 | + position: relative; | |
132 | + height: $centerHeight; | |
133 | + padding: 5px; | |
134 | + margin-bottom: 10px; | |
135 | + margin-right: 15px; | |
136 | + border-radius: 5px; | |
137 | + cursor: pointer; | |
138 | + border: 1px solid rgba(0, 0, 0, 0); | |
139 | + @include fetch-bg-color('background-color3'); | |
140 | + @extend .go-transition-quick; | |
141 | + &.hover, | |
142 | + &:hover { | |
143 | + @include fetch-bg-color('background-color4'); | |
144 | + } | |
145 | + &:hover { | |
146 | + @include deep() { | |
147 | + .icon-item { | |
148 | + opacity: 1; | |
149 | + } | |
150 | + } | |
151 | + .item-content-icon { | |
152 | + opacity: 1 !important; | |
153 | + } | |
154 | + } | |
155 | + | |
156 | + &.select { | |
157 | + border: 1px solid v-bind('themeColor'); | |
158 | + background-color: rgba(0, 0, 0, 0); | |
159 | + .item-content-icon { | |
160 | + display: none; | |
161 | + } | |
162 | + } | |
163 | + | |
164 | + .select-modal, | |
165 | + .item-content { | |
166 | + position: absolute; | |
167 | + top: 0; | |
168 | + left: 0; | |
169 | + } | |
170 | + | |
171 | + .item-content { | |
172 | + z-index: 1; | |
173 | + display: flex; | |
174 | + justify-content: space-between; | |
175 | + align-items: center; | |
176 | + padding: 5px; | |
177 | + .item-content-body { | |
178 | + width: 230px; | |
179 | + display: flex; | |
180 | + flex-direction: column; | |
181 | + gap: 5px; | |
182 | + } | |
183 | + .item-content-icon { | |
184 | + opacity: 0; | |
185 | + height: $centerHeight; | |
186 | + line-height: $centerHeight; | |
187 | + padding-left: 5px; | |
188 | + } | |
189 | + } | |
190 | + | |
191 | + .select-modal { | |
192 | + width: 100%; | |
193 | + height: 100%; | |
194 | + opacity: 0.3; | |
195 | + background-color: v-bind('themeColor'); | |
196 | + } | |
197 | + } | |
198 | + } | |
199 | +} | |
200 | +</style> | ... | ... |
1 | +<template> | |
2 | + <!-- 选中内容 --> | |
3 | + <div class="go-chart-data-pond"> | |
4 | + <n-card class="n-card-shallow"> | |
5 | + <setting-item-box name="请求名称" :alone="true"> | |
6 | + <n-input size="small" :placeholder="pondData?.dataPondName || '暂无'" :disabled="true"> | |
7 | + <template #prefix> | |
8 | + <n-icon :component="FishIcon" /> | |
9 | + </template> | |
10 | + </n-input> | |
11 | + </setting-item-box> | |
12 | + | |
13 | + <setting-item-box name="接口地址" :alone="true"> | |
14 | + <n-input size="small" :placeholder="pondData?.dataPondRequestConfig.requestUrl || '暂无'" :disabled="true"> | |
15 | + <template #prefix> | |
16 | + <n-icon :component="FlashIcon" /> | |
17 | + </template> | |
18 | + </n-input> | |
19 | + </setting-item-box> | |
20 | + | |
21 | + <div class="edit-text" @click="controlModelHandle"> | |
22 | + <div class="go-absolute-center"> | |
23 | + <n-button type="primary" secondary>编辑配置</n-button> | |
24 | + </div> | |
25 | + </div> | |
26 | + </n-card> | |
27 | + </div> | |
28 | + | |
29 | + <setting-item-box :alone="true"> | |
30 | + <template #name> | |
31 | + 测试 | |
32 | + <n-tooltip trigger="hover"> | |
33 | + <template #trigger> | |
34 | + <n-icon size="21" :depth="3"> | |
35 | + <help-outline-icon></help-outline-icon> | |
36 | + </n-icon> | |
37 | + </template> | |
38 | + 默认赋值给 dataset 字段 | |
39 | + </n-tooltip> | |
40 | + </template> | |
41 | + <n-button type="primary" ghost @click="sendHandle"> | |
42 | + <template #icon> | |
43 | + <n-icon> | |
44 | + <flash-icon /> | |
45 | + </n-icon> | |
46 | + </template> | |
47 | + 发送请求 | |
48 | + </n-button> | |
49 | + </setting-item-box> | |
50 | + | |
51 | + <!-- 底部数据展示 --> | |
52 | + <chart-data-matching-and-show :show="showMatching && !loading" :ajax="true"></chart-data-matching-and-show> | |
53 | + | |
54 | + <!-- 骨架图 --> | |
55 | + <go-skeleton :load="loading" :repeat="3"></go-skeleton> | |
56 | + | |
57 | + <!-- 编辑 / 新增弹窗 --> | |
58 | + <chart-data-pond-control v-model:modelShow="controlModel" @sendHandle="sendHandle"></chart-data-pond-control> | |
59 | +</template> | |
60 | + | |
61 | +<script setup lang="ts"> | |
62 | +import { ref, toRefs, toRaw, onBeforeUnmount, computed, watchEffect } from 'vue' | |
63 | +import { icon } from '@/plugins' | |
64 | +import { http, customizeHttp } from '@/api/http' | |
65 | +import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
66 | +import { ChartDataPondControl } from './components/ChartDataPondControl' | |
67 | +import { useDesignStore } from '@/store/modules/designStore/designStore' | |
68 | +import { ChartDataMatchingAndShow } from '../ChartDataMatchingAndShow' | |
69 | +import { newFunctionHandle } from '@/utils' | |
70 | +import { useTargetData } from '../../../../hooks/useTargetData.hook' | |
71 | + | |
72 | +const designStore = useDesignStore() | |
73 | +const { HelpOutlineIcon, FlashIcon, PulseIcon, FishIcon } = icon.ionicons5 | |
74 | +const { targetData, chartEditStore } = useTargetData() | |
75 | + | |
76 | +const { | |
77 | + requestDataPond, | |
78 | + requestInterval: GlobalRequestInterval, | |
79 | + requestIntervalUnit: GlobalRequestIntervalUnit | |
80 | +} = toRefs(chartEditStore.getRequestGlobalConfig) | |
81 | + | |
82 | +const loading = ref(false) | |
83 | +const controlModel = ref(false) | |
84 | +const showMatching = ref(false) | |
85 | + | |
86 | +let firstFocus = 0 | |
87 | +let lastFilter: any = undefined | |
88 | + | |
89 | +// 所选数据池 | |
90 | +const pondData = computed(() => { | |
91 | + const selectId = targetData.value.request.requestDataPondId | |
92 | + if (!selectId) return undefined | |
93 | + const data = requestDataPond.value.filter((item: Recordable) => { | |
94 | + return selectId === item.dataPondId | |
95 | + }) | |
96 | + return data[0] | |
97 | +}) | |
98 | + | |
99 | +// 颜色 | |
100 | +const themeColor = computed(() => { | |
101 | + return designStore.getAppTheme | |
102 | +}) | |
103 | + | |
104 | +// 请求配置 model | |
105 | +const controlModelHandle = () => { | |
106 | + controlModel.value = true | |
107 | +} | |
108 | + | |
109 | +// 发送请求 | |
110 | +const sendHandle = async () => { | |
111 | + if (!targetData.value?.request) { | |
112 | + window.$message.warning('请选择一个公共接口!') | |
113 | + return | |
114 | + } | |
115 | + loading.value = true | |
116 | + try { | |
117 | + const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.getRequestGlobalConfig)) | |
118 | + loading.value = false | |
119 | + if (res) { | |
120 | + if (!res?.data && !targetData.value.filter) { | |
121 | + window['$message'].warning('您的数据不符合默认格式,请配置过滤器!') | |
122 | + showMatching.value = true | |
123 | + return | |
124 | + } | |
125 | + targetData.value.option.dataset = newFunctionHandle(res?.data, res, targetData.value.filter) | |
126 | + showMatching.value = true | |
127 | + return | |
128 | + } | |
129 | + window['$message'].warning('没有拿到返回值,请检查接口!') | |
130 | + } catch (error) { | |
131 | + console.error(error); | |
132 | + loading.value = false | |
133 | + window['$message'].warning('数据异常,请检查参数!') | |
134 | + } | |
135 | +} | |
136 | + | |
137 | +watchEffect(() => { | |
138 | + const filter = targetData.value?.filter | |
139 | + if (lastFilter !== filter && firstFocus) { | |
140 | + lastFilter = filter | |
141 | + sendHandle() | |
142 | + } | |
143 | + firstFocus++ | |
144 | +}) | |
145 | + | |
146 | +onBeforeUnmount(() => { | |
147 | + lastFilter = null | |
148 | +}) | |
149 | +</script> | |
150 | + | |
151 | +<style scoped lang="scss"> | |
152 | +@include go('chart-data-pond') { | |
153 | + position: relative; | |
154 | + display: flex; | |
155 | + flex-direction: column; | |
156 | + align-items: center; | |
157 | + .n-card-shallow { | |
158 | + &.n-card { | |
159 | + @extend .go-background-filter; | |
160 | + @include deep() { | |
161 | + .n-card__content { | |
162 | + padding: 10px; | |
163 | + } | |
164 | + } | |
165 | + } | |
166 | + .edit-text { | |
167 | + position: absolute; | |
168 | + top: 0px; | |
169 | + left: 0px; | |
170 | + width: 325px; | |
171 | + height: 136px; | |
172 | + cursor: pointer; | |
173 | + opacity: 0; | |
174 | + transition: all 0.3s; | |
175 | + @extend .go-background-filter; | |
176 | + backdrop-filter: blur(2px) !important; | |
177 | + } | |
178 | + &:hover { | |
179 | + border-color: v-bind('themeColor'); | |
180 | + .edit-text { | |
181 | + opacity: 1; | |
182 | + } | |
183 | + } | |
184 | + } | |
185 | +} | |
186 | +</style> | ... | ... |
... | ... | @@ -4,6 +4,7 @@ import { icon } from '@/plugins' |
4 | 4 | import { useDesignStore } from '@/store/modules/designStore/designStore' |
5 | 5 | import { SettingItemBox } from '@/components/Pages/ChartItemSetting' |
6 | 6 | import { ChartDataMatchingAndShow } from '../../../external/components/ChartDataMatchingAndShow' |
7 | +import { ChartDataPondControl } from '../ChartDataPond/components/ChartDataPondControl' | |
7 | 8 | import { useTargetData } from '../../../../hooks/useTargetData.hook' |
8 | 9 | import { NButton, NSelect, NTooltip, NIcon, SelectOption } from 'naive-ui' |
9 | 10 | import { RequestInfoPanel } from '../RequestInfoPanel' |
... | ... | @@ -93,6 +94,11 @@ watchEffect(() => { |
93 | 94 | onBeforeUnmount(() => { |
94 | 95 | lastFilter = null |
95 | 96 | }) |
97 | + | |
98 | +const controlModel = ref(false) | |
99 | + | |
100 | +const handleOpenChartDataPondModal = () => controlModel.value = true | |
101 | + | |
96 | 102 | </script> |
97 | 103 | |
98 | 104 | <template> |
... | ... | @@ -101,6 +107,10 @@ onBeforeUnmount(() => { |
101 | 107 | <NSelect v-model:value="selectedRequestType" :options="getApiRequestType" /> |
102 | 108 | </SettingItemBox> |
103 | 109 | |
110 | + <SettingItemBox v-if="selectedRequestType === RequestDataTypeEnum.AJAX" name="全局配置" :alone="true"> | |
111 | + <NButton @click="handleOpenChartDataPondModal" type="success">配置</NButton> | |
112 | + </SettingItemBox> | |
113 | + | |
104 | 114 | <RequestInfoPanel @clickPanel="handleClickPanel" /> |
105 | 115 | |
106 | 116 | <SettingItemBox :alone="true"> |
... | ... | @@ -132,6 +142,8 @@ onBeforeUnmount(() => { |
132 | 142 | <go-skeleton :load="loading" :repeat="3"></go-skeleton> |
133 | 143 | |
134 | 144 | <RequestModal ref="requestModal" /> |
145 | + | |
146 | + <chart-data-pond-control v-model:modelShow="controlModel" @sendHandle="sendHandle"></chart-data-pond-control> | |
135 | 147 | </div> |
136 | 148 | </template> |
137 | 149 | ... | ... |
... | ... | @@ -99,6 +99,7 @@ const handleSelectedInterfaceChange = async (_value: string, option: PublicInter |
99 | 99 | const { filter } = option |
100 | 100 | const { targetData } = useTargetData() |
101 | 101 | targetData.value.filter = filter ?? 'return res' |
102 | + targetData.value.request.publicInterfaceSelectId = option.id as string | |
102 | 103 | //ft |
103 | 104 | setDynamicFormValue(option as unknown as ExtraRequestConfigType) |
104 | 105 | } |
... | ... | @@ -154,7 +155,7 @@ const getConfigurationData = () => { |
154 | 155 | |
155 | 156 | const setConfigurationData = async (request: ExtraRequestConfigType) => { |
156 | 157 | await getPublicInterfaceList() |
157 | - const { requestDataPondId, requestParams, requestParamsBodyType, requestContentType, requestHttpType, requestIntervalUnit, requestInterval } = request | |
158 | + const { requestDataPondId, requestParams, requestParamsBodyType, requestContentType, requestHttpType, requestIntervalUnit, requestInterval, publicInterfaceSelectId } = request | |
158 | 159 | const { Header } = requestParams |
159 | 160 | /** |
160 | 161 | * ft 修改 |
... | ... | @@ -162,7 +163,8 @@ const setConfigurationData = async (request: ExtraRequestConfigType) => { |
162 | 163 | * 源代码 selectedPublicInterface.value = requestDataPondId |
163 | 164 | * 修改后代码 selectedPublicInterface.value = publicInterfaceList.value.find(it=>it.id === requestDataPondId)?.id||'' |
164 | 165 | */ |
165 | - selectedPublicInterface.value = publicInterfaceList.value.find(it => it.id === requestDataPondId)?.id || '' | |
166 | + console.log(publicInterfaceList.value) | |
167 | + selectedPublicInterface.value = publicInterfaceList.value.find(it => it.id === publicInterfaceSelectId)?.id || '' | |
166 | 168 | //ft |
167 | 169 | requestContentTypeRef.value = requestContentType |
168 | 170 | requestHttpTypeRef.value = requestHttpType | ... | ... |
... | ... | @@ -14,23 +14,27 @@ import { |
14 | 14 | NTag, |
15 | 15 | NTooltip, |
16 | 16 | SelectOption, |
17 | - NCascader | |
17 | + NDivider, | |
18 | + CascaderOption, | |
19 | + NCascader, | |
18 | 20 | } from 'naive-ui' |
19 | -import { computed, ref, unref, onMounted, reactive } from 'vue' | |
21 | +import { computed, onMounted, reactive, ref, unref } from 'vue' | |
20 | 22 | import GlobalParamsConfiguration from './GlobalParamsConfiguration.vue' |
21 | 23 | import { ChevronDown, ChevronUp } from '@vicons/carbon' |
22 | 24 | import { useDesignStore } from '@/store/modules/designStore/designStore' |
23 | 25 | import { selectTimeOptions } from '../../../index.d' |
24 | 26 | import { useTargetData } from '../../../../hooks/useTargetData.hook' |
25 | 27 | import { RequestDataTypeEnum, RequestHttpIntervalEnum } from '@/enums/httpEnum' |
26 | -import { TokenNameEnum, TokenEnum } from '@/enums/external/tokenEnum' | |
27 | -import { customThirdInterfaceRequest, thirdInterfaceRequest } from '@/api/external/customRequest' | |
28 | -import { CascaderOption } from 'naive-ui' | |
28 | +import { RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
29 | 29 | import { convertToCascadingData } from '@/utils/external/utils' |
30 | +import { customThirdInterfaceRequest, thirdInterfaceRequest } from '@/api/external/customRequest' | |
30 | 31 | |
31 | 32 | const { PencilIcon } = icon.ionicons5 |
33 | + | |
32 | 34 | const designStore = useDesignStore() |
35 | + | |
33 | 36 | const editDisabled = ref(false) |
37 | + | |
34 | 38 | const collapseHeaderTable = ref(false) |
35 | 39 | |
36 | 40 | const { targetData, chartEditStore } = useTargetData() |
... | ... | @@ -39,15 +43,41 @@ const handleCollapse = () => { |
39 | 43 | collapseHeaderTable.value = !unref(collapseHeaderTable) |
40 | 44 | } |
41 | 45 | |
46 | +const requestDataPondLength = computed(()=>chartEditStore.getRequestGlobalConfig.requestDataPond.length) | |
47 | + | |
42 | 48 | // 颜色 |
43 | 49 | const themeColor = computed(() => { |
44 | 50 | return designStore.getAppTheme |
45 | 51 | }) |
46 | 52 | |
47 | -//第三方接口相关代码 | |
48 | -const verifiationValue = ref(TokenEnum.DEFAULT_TOKEN) | |
53 | +const selectGlobalPondValue = ref('') | |
54 | + | |
55 | +const cacheSelectOption = ref<Recordable>({ | |
56 | + dataPondRequestConfig:{ | |
57 | + pondRequestOriginUrl:'', | |
58 | + pondRequestUrl:'', | |
59 | + pondRequestHttpType:'', | |
60 | + pondRequestParams: { | |
61 | + "Body": { | |
62 | + "form-data": {}, | |
63 | + "x-www-form-urlencoded": {}, | |
64 | + "json": "", | |
65 | + "xml": "" | |
66 | + }, | |
67 | + "Header": {}, | |
68 | + "Params": {} | |
69 | + }, | |
70 | + requestVerificationToken:'', | |
71 | + } | |
72 | +}) | |
49 | 73 | |
50 | -const tokenString = ref('') | |
74 | +const selectGlobalPondOptions = computed(() => { | |
75 | + return chartEditStore.getRequestGlobalConfig.requestDataPond.map(dataPond=>({ | |
76 | + value:dataPond.dataPondId, | |
77 | + label:dataPond.dataPondName, | |
78 | + ...dataPond | |
79 | + })) | |
80 | +}) | |
51 | 81 | |
52 | 82 | const getTokenString = ref() |
53 | 83 | |
... | ... | @@ -62,76 +92,53 @@ const cascaderConfig = reactive({ |
62 | 92 | showPath: true |
63 | 93 | }) |
64 | 94 | |
65 | -const requestTokenSuffix = ref('Bearer') | |
66 | - | |
67 | -const requestTokenKey = ref('Token') | |
68 | - | |
69 | -//验证方式 | |
70 | -const verificationMethods = [ | |
71 | - { | |
72 | - label: TokenNameEnum.DEFAULT_TOKEN, | |
73 | - value: TokenEnum.DEFAULT_TOKEN | |
74 | - }, | |
75 | - { | |
76 | - label: TokenNameEnum.TOKEN_STRING, | |
77 | - value: TokenEnum.TOKEN_STRING | |
78 | - }, | |
79 | - { | |
80 | - label: TokenNameEnum.NO_VERIFICATION, | |
81 | - value: TokenEnum.NO_VERIFICATION | |
82 | - } | |
83 | -] | |
84 | - | |
85 | -const hasDefaultToken = (value: string[]) => value.includes(TokenEnum.DEFAULT_TOKEN) | |
86 | - | |
87 | -const hasTokenString = (value: string[]) => value.includes(TokenEnum.TOKEN_STRING) | |
88 | - | |
89 | -const handleVerificationUpdate = (value: string) => { | |
90 | - chartEditStore.getRequestGlobalConfig.requestVerificationMethods = value | |
91 | - hasTokenString([value]) | |
92 | - hasDefaultToken([value]) | |
93 | - resValue.value = null | |
94 | - resOptions.value = [] | |
95 | - getTokenString.value = '' | |
96 | - requestTokenKey.value = '' | |
97 | - requestTokenSuffix.value = '' | |
98 | -} | |
99 | - | |
100 | -const handleResCascader = (value: string | number) => { | |
95 | +const handleResCascader = (value: string | number, options: Recordable) => { | |
96 | + targetData.value.request.thirdSelectCascaderLabel = options.label | |
101 | 97 | getTokenString.value = value as string | number |
102 | - chartEditStore.getRequestGlobalConfig.requestVerificationToken = value as string | |
103 | 98 | targetData.value.request.requestVerificationToken = value as string |
104 | 99 | targetData.value.request.requestDataType = RequestDataTypeEnum.AJAX |
105 | 100 | } |
106 | 101 | |
102 | +const findCurrentPond = () => { | |
103 | + return selectGlobalPondOptions.value.find((pondItem: RequestDataPondItemType & {label: string, value: string}) => pondItem.dataPondId === targetData.value?.request?.requestDataPondId) | |
104 | +} | |
105 | + | |
106 | +const handleSelectGlobalPond = (_:string, options:RequestDataPondItemType & {label: string, value: string}) => { | |
107 | + cacheSelectOption.value = options | |
108 | + targetData.value.request = options.dataPondRequestConfig | |
109 | + targetData.value.request.requestDataPondId = options.dataPondId | |
110 | + getTokenString.value ='' | |
111 | + resOptions.value = [] | |
112 | + resValue.value=null | |
113 | + handleExecuteRequest() | |
114 | +} | |
115 | + | |
107 | 116 | //执行请求获取三方Token |
108 | 117 | const handleExecuteRequest = async () => { |
109 | 118 | try { |
110 | - const request = { | |
111 | - requestOriginUrl: chartEditStore.getRequestGlobalConfig.requestOriginUrl, | |
112 | - body: chartEditStore.getRequestGlobalConfig.requestParams.Header | |
119 | + const originUrlString = cacheSelectOption.value?.dataPondRequestConfig?.pondRequestOriginUrl | |
120 | + const requestUrlString = cacheSelectOption.value?.dataPondRequestConfig?.pondRequestUrl | |
121 | + const body = cacheSelectOption.value?.dataPondRequestConfig?.pondRequestParams['Body']['json'] | |
122 | + if(originUrlString && requestUrlString){ | |
123 | + const request = { | |
124 | + requestOriginUrl: `${originUrlString}${requestUrlString}`, | |
125 | + body | |
126 | + } | |
127 | + const res = await customThirdInterfaceRequest(request as unknown as thirdInterfaceRequest) | |
128 | + resOptions.value = convertToCascadingData(res) as unknown as CascaderOption[] | |
113 | 129 | } |
114 | - const res = await customThirdInterfaceRequest(request as thirdInterfaceRequest) | |
115 | - resOptions.value = convertToCascadingData(res) as unknown as CascaderOption[] | |
116 | - chartEditStore.getRequestGlobalConfig.requestVerificationMethods = verifiationValue.value | |
117 | - chartEditStore.getRequestGlobalConfig.requestVerificationToken = resValue.value as string | |
118 | - chartEditStore.getRequestGlobalConfig.requestTokenKey = requestTokenKey.value | |
119 | - chartEditStore.getRequestGlobalConfig.requestTokenSuffix = requestTokenSuffix.value | |
120 | - //存储到当前组件的请求对象里 | |
121 | - targetData.value.request.requestTokenSuffix = requestTokenSuffix.value | |
122 | - targetData.value.request.requestTokenKey = requestTokenKey.value | |
123 | 130 | } catch (e) { |
124 | 131 | getTokenString.value = JSON.stringify(e) as string |
125 | 132 | } |
126 | 133 | } |
127 | 134 | |
128 | -onMounted(() => { | |
129 | - getTokenString.value = chartEditStore.getRequestGlobalConfig.requestVerificationToken | |
130 | - verifiationValue.value = chartEditStore.getRequestGlobalConfig.requestVerificationMethods || TokenEnum.DEFAULT_TOKEN | |
131 | - requestTokenKey.value = chartEditStore.getRequestGlobalConfig.requestTokenKey! | |
132 | - requestTokenSuffix.value = chartEditStore.getRequestGlobalConfig.requestTokenSuffix! | |
135 | +onMounted(()=>{ | |
136 | + getTokenString.value = targetData.value?.request?.requestVerificationToken | |
137 | + selectGlobalPondValue.value = findCurrentPond()?.dataPondId as string | |
138 | + cacheSelectOption.value = findCurrentPond() as RequestDataPondItemType | |
139 | + resValue.value = targetData.value?.request?.thirdSelectCascaderLabel | |
140 | + handleExecuteRequest() | |
133 | 141 | }) |
134 | -// | |
135 | 142 | </script> |
136 | 143 | |
137 | 144 | <template> |
... | ... | @@ -139,7 +146,7 @@ onMounted(() => { |
139 | 146 | <template #header> |
140 | 147 | <NTag type="info">全局公共配置</NTag> |
141 | 148 | </template> |
142 | - <NSpace vertical> | |
149 | + <NSpace v-if="requestDataPondLength<=0" vertical> | |
143 | 150 | <SettingItemBox |
144 | 151 | name="服务" |
145 | 152 | :itemRightStyle="{ |
... | ... | @@ -188,43 +195,56 @@ onMounted(() => { |
188 | 195 | 编辑配置 |
189 | 196 | </NButton> |
190 | 197 | </SettingItemBox> |
191 | - <!-- 针对第三方接口 --> | |
192 | - <SettingItemBox | |
193 | - name="验证方式" | |
194 | - :itemRightStyle="{ | |
195 | - gridTemplateColumns: '5fr 2fr 1fr' | |
196 | - }" | |
197 | - > | |
198 | - <n-radio-group v-model:value="verifiationValue" name="radiogroup" @update:value="handleVerificationUpdate"> | |
199 | - <n-space> | |
200 | - <n-radio | |
201 | - v-for="verificationItem in verificationMethods" | |
202 | - :key="verificationItem.value" | |
203 | - :value="verificationItem.value" | |
204 | - > | |
205 | - {{ verificationItem.label }} | |
206 | - </n-radio> | |
207 | - </n-space> | |
208 | - </n-radio-group> | |
209 | - </SettingItemBox> | |
210 | - <SettingItemBox v-if="hasDefaultToken([verifiationValue])" name="配置"> | |
211 | - <SettingItem name="Token前缀"> | |
212 | - <n-input v-model:value.trim="requestTokenSuffix" type="text" placeholder="例如:Bearer" /> | |
213 | - </SettingItem> | |
214 | - <SettingItem name="Token键"> | |
215 | - <n-input v-model:value.trim="requestTokenKey" type="text" placeholder="例如:X-Authorization" /> | |
216 | - </SettingItem> | |
217 | - </SettingItemBox> | |
218 | 198 | <NCollapseTransition :show="collapseHeaderTable"> |
219 | - <GlobalParamsConfiguration v-if="hasDefaultToken([verifiationValue])" /> | |
220 | - <n-input | |
221 | - v-else-if="hasTokenString([verifiationValue])" | |
222 | - v-model:value="tokenString" | |
223 | - type="text" | |
224 | - placeholder="请输入令牌字符串" | |
199 | + <GlobalParamsConfiguration /> | |
200 | + </NCollapseTransition> | |
201 | + <section class="arrow"> | |
202 | + <NTooltip> | |
203 | + <template #trigger> | |
204 | + <NIcon @click="handleCollapse" size="30"> | |
205 | + <ChevronDown v-show="!collapseHeaderTable" /> | |
206 | + <ChevronUp v-show="collapseHeaderTable" /> | |
207 | + </NIcon> | |
208 | + </template> | |
209 | + <span>{{ collapseHeaderTable ? '收起' : '展开' }}</span> | |
210 | + </NTooltip> | |
211 | + </section> | |
212 | + </NSpace> | |
213 | + <div v-else> | |
214 | + <NSelect | |
215 | + v-model:value="selectGlobalPondValue" | |
216 | + placeholder="请选择全局公共配置" | |
217 | + class="select-time-options" | |
218 | + size="small" | |
219 | + clearable | |
220 | + :options="selectGlobalPondOptions" | |
221 | + @update:value="handleSelectGlobalPond" | |
225 | 222 | /> |
226 | - <n-empty v-else description="无"> </n-empty> | |
227 | - <div style="margin-top: 10px"> | |
223 | + <NDivider /> | |
224 | + <SettingItemBox name="配置信息" v-if="selectGlobalPondValue" :itemRightStyle="{ | |
225 | + gridTemplateColumns: '5fr 2fr 1fr' | |
226 | + }"> | |
227 | + <SettingItem name="源地址"> | |
228 | + <NInput | |
229 | + v-model:value="cacheSelectOption.dataPondRequestConfig.pondRequestOriginUrl" | |
230 | + disabled | |
231 | + ></NInput> | |
232 | + </SettingItem> | |
233 | + <SettingItem name="url地址"> | |
234 | + <NInput | |
235 | + v-model:value="cacheSelectOption.dataPondRequestConfig.pondRequestUrl" | |
236 | + disabled | |
237 | + ></NInput> | |
238 | + </SettingItem> | |
239 | + <SettingItem name="请求方式"> | |
240 | + <NInput | |
241 | + v-model:value="cacheSelectOption.dataPondRequestConfig.pondRequestHttpType" | |
242 | + disabled | |
243 | + ></NInput> | |
244 | + </SettingItem> | |
245 | + </SettingItemBox> | |
246 | + <NDivider /> | |
247 | + <div style="margin:0 70px" v-if="selectGlobalPondValue"> | |
228 | 248 | <n-space vertical> |
229 | 249 | <n-button type="primary" @click="handleExecuteRequest">执行请求</n-button> |
230 | 250 | <NCascader |
... | ... | @@ -248,20 +268,7 @@ onMounted(() => { |
248 | 268 | /> |
249 | 269 | </n-space> |
250 | 270 | </div> |
251 | - <!-- 针对第三方接口 --> | |
252 | - </NCollapseTransition> | |
253 | - <section class="arrow"> | |
254 | - <NTooltip> | |
255 | - <template #trigger> | |
256 | - <NIcon @click="handleCollapse" size="30"> | |
257 | - <ChevronDown v-show="!collapseHeaderTable" /> | |
258 | - <ChevronUp v-show="collapseHeaderTable" /> | |
259 | - </NIcon> | |
260 | - </template> | |
261 | - <span>{{ collapseHeaderTable ? '收起' : '展开' }}</span> | |
262 | - </NTooltip> | |
263 | - </section> | |
264 | - </NSpace> | |
271 | + </div> | |
265 | 272 | </NCard> |
266 | 273 | </template> |
267 | 274 | ... | ... |
1 | +<template> | |
2 | + <n-modal | |
3 | + class="go-chart-data-request" | |
4 | + v-model:show="modelShowRef" | |
5 | + :mask-closable="false" | |
6 | + :closeOnEsc="true" | |
7 | + :onEsc="onEsc" | |
8 | + > | |
9 | + <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px"> | |
10 | + <template #header></template> | |
11 | + <template #header-extra> </template> | |
12 | + <n-scrollbar style="max-height: 718px"> | |
13 | + <div class="go-pr-3"> | |
14 | + <n-space vertical> | |
15 | + <PondPublicConfiguration ref="pondPublicConfigurationRef" /> | |
16 | + <NDivider /> | |
17 | + <PondRequestConfiguration ref="pondRequestConfigurationRef" /> | |
18 | + </n-space> | |
19 | + </div> | |
20 | + </n-scrollbar> | |
21 | + <!-- 底部 --> | |
22 | + <template #action> | |
23 | + <n-space justify="space-between"> | |
24 | + <n-space v-if="targetDataRequest"> | |
25 | + <n-tag :bordered="false" type="primary">名称:</n-tag> | |
26 | + <n-input | |
27 | + v-model:value="targetDataRequest.dataPondName" | |
28 | + ref="inputInstRef" | |
29 | + type="text" | |
30 | + size="small" | |
31 | + :autofocus="true" | |
32 | + :clearable="true" | |
33 | + :minlength="1" | |
34 | + :maxlength="16" | |
35 | + placeholder="请输入名称" | |
36 | + ></n-input> | |
37 | + </n-space> | |
38 | + <span v-else></span> | |
39 | + <n-space> | |
40 | + <n-button @click="closeHandle">取消</n-button> | |
41 | + <n-button type="primary" @click="closeAndSendHandle">保存</n-button> | |
42 | + </n-space> | |
43 | + </n-space> | |
44 | + </template> | |
45 | + </n-card> | |
46 | + </n-modal> | |
47 | +</template> | |
48 | + | |
49 | +<script script lang="ts" setup> | |
50 | +import { PropType, ref, watch, onMounted, nextTick } from 'vue' | |
51 | +import { RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d' | |
52 | +import { goDialog } from '@/utils' | |
53 | +import PondPublicConfiguration from './PondPublicConfiguration.vue' | |
54 | +import PondRequestConfiguration from './PondRequestConfiguration.vue' | |
55 | +import { merge } from 'lodash' | |
56 | +import { useTargetData } from '../../../../hooks/useTargetData.hook' | |
57 | + | |
58 | +const props = defineProps({ | |
59 | + modelShow: Boolean, | |
60 | + targetDataRequest: Object as PropType<RequestDataPondItemType>, | |
61 | +}) | |
62 | + | |
63 | +const { targetData } = useTargetData() | |
64 | + | |
65 | +const pondPublicConfigurationRef = ref<InstanceType<typeof PondPublicConfiguration>>() | |
66 | + | |
67 | +const pondRequestConfigurationRef = ref<InstanceType<typeof PondRequestConfiguration>>() | |
68 | + | |
69 | +const emit = defineEmits(['update:modelShow', 'editSaveHandle']) | |
70 | + | |
71 | +const inputInstRef = ref() | |
72 | + | |
73 | +const modelShowRef = ref(false) | |
74 | + | |
75 | +watch( | |
76 | + () => props.modelShow, | |
77 | + newValue => { | |
78 | + modelShowRef.value = newValue | |
79 | + if(newValue) { | |
80 | + nextTick(() => { | |
81 | + pondPublicConfigurationRef?.value?.setConfigurationData(props.targetDataRequest?.dataPondRequestConfig as unknown as RequestDataPondItemType) | |
82 | + pondRequestConfigurationRef?.value?.setConfigurationData(props.targetDataRequest?.dataPondRequestConfig as unknown as RequestDataPondItemType) | |
83 | + }) | |
84 | + } | |
85 | + } | |
86 | +) | |
87 | + | |
88 | +const closeHandle = () => { | |
89 | + emit('update:modelShow', false) | |
90 | +} | |
91 | + | |
92 | +const closeAndSendHandle = () => { | |
93 | + if (!props.targetDataRequest?.dataPondName) { | |
94 | + window.$message.warning('请在左下角输入名称!') | |
95 | + inputInstRef.value?.focus() | |
96 | + return | |
97 | + } | |
98 | + const pondPublicData = pondPublicConfigurationRef?.value?.getConfigurationData() | |
99 | + const pondRequestData = pondRequestConfigurationRef?.value?.getConfigurationData() | |
100 | + merge(props.targetDataRequest.dataPondRequestConfig, pondRequestData, pondPublicData) | |
101 | + goDialog({ | |
102 | + message: '保存内容将同步修改所有使用此接口的组件, 是否继续?', | |
103 | + isMaskClosable: true, | |
104 | + transformOrigin: 'center', | |
105 | + onPositiveCallback: () => { | |
106 | + emit('update:modelShow', false) | |
107 | + emit('editSaveHandle', props.targetDataRequest) | |
108 | + } | |
109 | + }) | |
110 | +} | |
111 | + | |
112 | +const onEsc = () => { | |
113 | + closeHandle() | |
114 | +} | |
115 | + | |
116 | +onMounted(()=>{ | |
117 | + pondPublicConfigurationRef?.value?.setConfigurationData(targetData.value.request) | |
118 | +}) | |
119 | +</script> | |
120 | + | |
121 | +<style lang="scss" scoped> | |
122 | +@include go('chart-data-request') { | |
123 | + &.n-card.n-modal, | |
124 | + .n-card { | |
125 | + @extend .go-background-filter; | |
126 | + } | |
127 | + .n-card-shallow { | |
128 | + background-color: rgba(0, 0, 0, 0) !important; | |
129 | + } | |
130 | + @include deep() { | |
131 | + & > .n-card__content { | |
132 | + padding-right: 0; | |
133 | + } | |
134 | + .n-card__content { | |
135 | + padding-bottom: 5px; | |
136 | + } | |
137 | + } | |
138 | +} | |
139 | +</style> | ... | ... |
1 | +<script lang="ts" setup name="PondPublicConfiguration"> | |
2 | +import { SettingItem, SettingItemBox } from '@/components/Pages/ChartItemSetting' | |
3 | +import { icon } from '@/plugins' | |
4 | +import { | |
5 | + NButton, | |
6 | + NCard, | |
7 | + NCollapseTransition, | |
8 | + NIcon, | |
9 | + NInput, | |
10 | + NInputGroup, | |
11 | + NInputNumber, | |
12 | + NSelect, | |
13 | + NSpace, | |
14 | + NTag, | |
15 | + NTooltip, | |
16 | + SelectOption, | |
17 | +} from 'naive-ui' | |
18 | +import { computed, ref, unref } from 'vue' | |
19 | +import GlobalParamsConfiguration from './GlobalParamsConfiguration.vue' | |
20 | +import { ChevronDown, ChevronUp } from '@vicons/carbon' | |
21 | +import { useDesignStore } from '@/store/modules/designStore/designStore' | |
22 | +import { selectTimeOptions } from '../../../index.d' | |
23 | +import { RequestHttpIntervalEnum } from '@/enums/httpEnum' | |
24 | + | |
25 | +const { PencilIcon } = icon.ionicons5 | |
26 | + | |
27 | +const designStore = useDesignStore() | |
28 | + | |
29 | +const editDisabled = ref(false) | |
30 | + | |
31 | +const collapseHeaderTable = ref(false) | |
32 | + | |
33 | +const pondRequestOriginUrlRef= ref('') | |
34 | + | |
35 | +const pondRequestIntervalRef = ref() | |
36 | + | |
37 | +const pondRequestIntervalUnitRef = ref('') | |
38 | + | |
39 | +const pondRequestGlobalTokenSuffixRef = ref('') | |
40 | + | |
41 | +const pondRequestGlobalTokenKeyRef = ref('') | |
42 | + | |
43 | +const handleCollapse = () => { | |
44 | + collapseHeaderTable.value = !unref(collapseHeaderTable) | |
45 | +} | |
46 | + | |
47 | +// 颜色 | |
48 | +const themeColor = computed(() => { | |
49 | + return designStore.getAppTheme | |
50 | +}) | |
51 | + | |
52 | +const getConfigurationData = (): Recordable => { | |
53 | + return { | |
54 | + pondRequestOriginUrl: unref(pondRequestOriginUrlRef), | |
55 | + pondRequestInterval: unref(pondRequestIntervalRef), | |
56 | + pondRequestIntervalUnit: unref(pondRequestIntervalUnitRef), | |
57 | + pondRequestGlobalTokenSuffix: unref(pondRequestGlobalTokenSuffixRef), | |
58 | + pondRequestGlobalTokenKey: unref(pondRequestGlobalTokenKeyRef), | |
59 | + } | |
60 | +} | |
61 | + | |
62 | +const setConfigurationData = (request: Recordable) => { | |
63 | + const { pondRequestOriginUrl, pondRequestInterval, pondRequestIntervalUnit, pondRequestGlobalTokenSuffix, pondRequestGlobalTokenKey } = request | |
64 | + pondRequestOriginUrlRef.value = pondRequestOriginUrl | |
65 | + pondRequestIntervalRef.value = pondRequestInterval | |
66 | + pondRequestIntervalUnitRef.value = pondRequestIntervalUnit | |
67 | + pondRequestGlobalTokenSuffixRef.value = pondRequestGlobalTokenSuffix | |
68 | + pondRequestGlobalTokenKeyRef.value = pondRequestGlobalTokenKey | |
69 | +} | |
70 | + | |
71 | +defineExpose({ | |
72 | + getConfigurationData, | |
73 | + setConfigurationData | |
74 | +}) | |
75 | +</script> | |
76 | + | |
77 | +<template> | |
78 | + <NCard> | |
79 | + <template #header> | |
80 | + <NTag type="info">全局公共配置</NTag> | |
81 | + </template> | |
82 | + <NSpace vertical> | |
83 | + <SettingItemBox | |
84 | + name="服务" | |
85 | + :itemRightStyle="{ | |
86 | + gridTemplateColumns: '5fr 2fr 1fr' | |
87 | + }" | |
88 | + > | |
89 | + <!-- 源地址 --> | |
90 | + <SettingItem name="前置 URL"> | |
91 | + <NInput | |
92 | + v-model:value="pondRequestOriginUrlRef" | |
93 | + default-value="http://127.0.0.1" | |
94 | + :disabled="editDisabled" | |
95 | + size="small" | |
96 | + placeholder="例:http://127.0.0.1" | |
97 | + ></NInput> | |
98 | + </SettingItem> | |
99 | + <SettingItem name="更新间隔,为 0 只会初始化"> | |
100 | + <NInputGroup> | |
101 | + <NInputNumber | |
102 | + v-model:value="pondRequestIntervalRef" | |
103 | + class="select-time-number" | |
104 | + size="small" | |
105 | + min="0" | |
106 | + :show-button="false" | |
107 | + :disabled="editDisabled" | |
108 | + placeholder="请输入数字" | |
109 | + > | |
110 | + </NInputNumber> | |
111 | + <!-- 单位 --> | |
112 | + <NSelect | |
113 | + v-model:value="pondRequestIntervalUnitRef" | |
114 | + class="select-time-options" | |
115 | + size="small" | |
116 | + :default-value="RequestHttpIntervalEnum.SECOND" | |
117 | + :options="selectTimeOptions as SelectOption[]" | |
118 | + :disabled="editDisabled" | |
119 | + /> | |
120 | + </NInputGroup> | |
121 | + </SettingItem> | |
122 | + <NButton v-show="editDisabled" type="primary" ghost @click="editDisabled = false"> | |
123 | + <template #icon> | |
124 | + <NIcon> | |
125 | + <PencilIcon /> | |
126 | + </NIcon> | |
127 | + </template> | |
128 | + 编辑配置 | |
129 | + </NButton> | |
130 | + </SettingItemBox> | |
131 | + <!-- 针对第三方接口 --> | |
132 | + <SettingItemBox name="配置"> | |
133 | + <SettingItem name="Token前缀"> | |
134 | + <n-input v-model:value.trim="pondRequestGlobalTokenSuffixRef" type="text" placeholder="例如:Bearer" /> | |
135 | + </SettingItem> | |
136 | + <SettingItem name="Token键值"> | |
137 | + <n-input v-model:value.trim="pondRequestGlobalTokenKeyRef" type="text" placeholder="例如:Token" /> | |
138 | + </SettingItem> | |
139 | + </SettingItemBox> | |
140 | + <NCollapseTransition :show="collapseHeaderTable"> | |
141 | + <GlobalParamsConfiguration /> | |
142 | + <!-- 针对第三方接口 --> | |
143 | + </NCollapseTransition> | |
144 | + <section class="arrow"> | |
145 | + <NTooltip> | |
146 | + <template #trigger> | |
147 | + <NIcon @click="handleCollapse" size="30"> | |
148 | + <ChevronDown v-show="!collapseHeaderTable" /> | |
149 | + <ChevronUp v-show="collapseHeaderTable" /> | |
150 | + </NIcon> | |
151 | + </template> | |
152 | + <span>{{ collapseHeaderTable ? '收起' : '展开' }}</span> | |
153 | + </NTooltip> | |
154 | + </section> | |
155 | + </NSpace> | |
156 | + </NCard> | |
157 | +</template> | |
158 | + | |
159 | +<style lang="scss" scoped> | |
160 | +.arrow { | |
161 | + display: flex; | |
162 | + justify-content: center; | |
163 | + align-items: center; | |
164 | + cursor: pointer; | |
165 | + | |
166 | + &:hover { | |
167 | + color: v-bind('themeColor'); | |
168 | + } | |
169 | +} | |
170 | +</style> | ... | ... |
1 | +<script setup lang="ts"> | |
2 | +import { NInput, NInputNumber, NInputGroup, NSelect, SelectOption, NTabs, NTabPane } from 'naive-ui' | |
3 | +import { SettingItem, SettingItemBox } from '@/components/Pages/ChartItemSetting' | |
4 | +import { selectTimeOptions, selectTypeOptions } from '../../../index.d' | |
5 | +import { | |
6 | + RequestBodyEnum, | |
7 | + RequestContentTypeEnum, | |
8 | + RequestHttpEnum, | |
9 | + RequestHttpIntervalEnum, | |
10 | + RequestParams | |
11 | +} from '@/enums/httpEnum' | |
12 | +import { ref, unref } from 'vue' | |
13 | +import DefaultRequestContent from './DefaultRequestContent.vue' | |
14 | +import SqlConfiguration from './SqlConfiguration.vue' | |
15 | + | |
16 | +const pondRequestContentTypeRef = ref(RequestContentTypeEnum.DEFAULT) | |
17 | + | |
18 | +const pondRequestHttpTypeRef = ref(RequestHttpEnum.GET) | |
19 | + | |
20 | +const pondRequestIntervalRef = ref<number | undefined>(20) | |
21 | + | |
22 | +const pondRequestIntervalUnitRef = ref(RequestHttpIntervalEnum.SECOND) | |
23 | + | |
24 | +const pondRequestSQLContentRef = ref('select * from where') | |
25 | + | |
26 | +const pondRequestParamsRef = ref({ | |
27 | + Header: {}, | |
28 | + Params: {}, | |
29 | + Body: { 'form-data': {}, json: '', 'x-www-form-urlencoded': {}, xml: '' } | |
30 | +} as RequestParams) | |
31 | + | |
32 | +const pondRequestParamsBodyTypeRef = ref(RequestBodyEnum.NONE) | |
33 | + | |
34 | +const pondRequestUrlRef = ref<string>() | |
35 | + | |
36 | +const getConfigurationData = (): Recordable => { | |
37 | + return { | |
38 | + pondRequestInterval: unref(pondRequestIntervalRef), | |
39 | + pondRequestIntervalUnit: unref(pondRequestIntervalUnitRef), | |
40 | + pondRequestHttpType: unref(pondRequestHttpTypeRef), | |
41 | + pondRequestUrl: unref(pondRequestUrlRef), | |
42 | + pondRequestContentType: unref(pondRequestContentTypeRef), | |
43 | + pondRequestSQLContent: unref(pondRequestSQLContentRef), | |
44 | + pondRequestParamsBodyType: unref(pondRequestParamsBodyTypeRef), | |
45 | + pondRequestParams: unref(pondRequestParamsRef) | |
46 | + } | |
47 | +} | |
48 | + | |
49 | +const setConfigurationData = (request: Recordable) => { | |
50 | + const { | |
51 | + pondRequestInterval, | |
52 | + pondRequestIntervalUnit, | |
53 | + pondRequestHttpType, | |
54 | + pondRequestUrl, | |
55 | + pondRequestContentType, | |
56 | + pondRequestSQLContent, | |
57 | + pondRequestParamsBodyType, | |
58 | + pondRequestParams | |
59 | + } = request | |
60 | + pondRequestIntervalRef.value = pondRequestInterval | |
61 | + pondRequestIntervalUnitRef.value = pondRequestIntervalUnit | |
62 | + pondRequestHttpTypeRef.value = pondRequestHttpType | |
63 | + pondRequestUrlRef.value = pondRequestUrl | |
64 | + pondRequestContentTypeRef.value = pondRequestContentType | |
65 | + pondRequestSQLContentRef.value = pondRequestSQLContent | |
66 | + pondRequestParamsBodyTypeRef.value = pondRequestParamsBodyType | |
67 | + pondRequestParamsRef.value = pondRequestParams | |
68 | +} | |
69 | + | |
70 | +defineExpose({ | |
71 | + getConfigurationData, | |
72 | + setConfigurationData | |
73 | +}) | |
74 | +</script> | |
75 | + | |
76 | +<template> | |
77 | + <SettingItemBox | |
78 | + name="地址" | |
79 | + :itemRightStyle="{ | |
80 | + gridTemplateColumns: '5fr 2fr 1fr' | |
81 | + }" | |
82 | + > | |
83 | + <SettingItem name="请求方式 & URL地址"> | |
84 | + <NInputGroup> | |
85 | + <NSelect | |
86 | + v-model:value="pondRequestHttpTypeRef" | |
87 | + style="width: 150px" | |
88 | + size="small" | |
89 | + :options="(selectTypeOptions as SelectOption[])" | |
90 | + /> | |
91 | + <NInput | |
92 | + v-model:value="pondRequestUrlRef" | |
93 | + class="select-time-number" | |
94 | + size="small" | |
95 | + min="0" | |
96 | + :show-button="false" | |
97 | + placeholder="请输入URL地址" | |
98 | + /> | |
99 | + </NInputGroup> | |
100 | + </SettingItem> | |
101 | + <SettingItem name="更新间隔,为 0 只会初始化"> | |
102 | + <NInputGroup> | |
103 | + <NInputNumber | |
104 | + v-model:value="pondRequestIntervalRef" | |
105 | + class="select-time-number" | |
106 | + size="small" | |
107 | + min="0" | |
108 | + :show-button="false" | |
109 | + placeholder="请输入数字" | |
110 | + /> | |
111 | + <!-- 单位 --> | |
112 | + <NSelect | |
113 | + v-model:value="pondRequestIntervalUnitRef" | |
114 | + class="select-time-options" | |
115 | + size="small" | |
116 | + :options="(selectTimeOptions as SelectOption[])" | |
117 | + /> | |
118 | + </NInputGroup> | |
119 | + </SettingItem> | |
120 | + </SettingItemBox> | |
121 | + <SettingItemBox name="选择方式"> | |
122 | + <NTabs v-model:value="pondRequestContentTypeRef" type="segment" size="small"> | |
123 | + <NTabPane :name="RequestContentTypeEnum.DEFAULT" tab="普通请求" /> | |
124 | + <NTabPane :name="RequestContentTypeEnum.SQL" tab="SQL请求" /> | |
125 | + </NTabs> | |
126 | + </SettingItemBox> | |
127 | + <SettingItemBox> | |
128 | + <DefaultRequestContent | |
129 | + v-if="pondRequestContentTypeRef === RequestContentTypeEnum.DEFAULT" | |
130 | + v-model:value="pondRequestParamsRef" | |
131 | + v-model:request-params-body-type="pondRequestParamsBodyTypeRef" | |
132 | + /> | |
133 | + <SqlConfiguration | |
134 | + v-model:value="pondRequestSQLContentRef" | |
135 | + v-if="pondRequestContentTypeRef === RequestContentTypeEnum.SQL" | |
136 | + /> | |
137 | + </SettingItemBox> | |
138 | +</template> | ... | ... |
... | ... | @@ -35,7 +35,7 @@ const openModal = async (flag = true, type: RequestDataTypeEnum) => { |
35 | 35 | requestDataType.value = type |
36 | 36 | showModal.value = flag |
37 | 37 | await nextTick() |
38 | - | |
38 | + console.log(unref(selectTarget)!.request) | |
39 | 39 | unref(componentConfigurationEl as any)?.setConfigurationData(unref(selectTarget)!.request || {}) |
40 | 40 | unref(publicInterfaceFormEl as any)?.setConfigurationData(unref(selectTarget)!.request || {}) |
41 | 41 | } |
... | ... | @@ -85,20 +85,19 @@ const sendHandle = async () => { |
85 | 85 | } |
86 | 86 | } |
87 | 87 | |
88 | -const setRequestKey = (value: Recordable, key: string) => { | |
89 | - value[key] = | |
90 | - targetData.value.request.requestDataType === RequestDataTypeEnum.AJAX | |
91 | - ? (targetData.value.request as unknown as Recordable)[key] || | |
92 | - (chartEditStore.getRequestGlobalConfig as unknown as Recordable)[key] | |
93 | - : null | |
94 | -} | |
95 | - | |
96 | 88 | const handleSaveAction = async () => { |
97 | 89 | if (!(await validate())) return |
98 | - const value = getResult() | |
99 | - setRequestKey(value, 'requestTokenSuffix') | |
100 | - setRequestKey(value, 'requestTokenKey') | |
101 | - setRequestKey(value, 'requestVerificationToken') | |
90 | + let value = getResult() | |
91 | + value.pondRequestGlobalTokenKey = targetData.value.request.pondRequestGlobalTokenKey | |
92 | + value.pondRequestGlobalTokenSuffix = targetData.value.request.pondRequestGlobalTokenSuffix | |
93 | + value.pondRequestInterval = targetData.value.request.pondRequestInterval | |
94 | + value.pondRequestIntervalUnit= targetData.value.request.pondRequestIntervalUnit | |
95 | + value.pondRequestOriginUrl= targetData.value.request.pondRequestOriginUrl | |
96 | + value.requestVerificationToken = targetData.value.request.requestVerificationToken | |
97 | + value.requestDataPondId = targetData.value.request.requestDataPondId | |
98 | + value.thirdSelectCascaderLabel = targetData.value.request.thirdSelectCascaderLabel | |
99 | + value.requestDataType = targetData.value.request.requestDataType | |
100 | + value.publicInterfaceSelectId = targetData.value.request.publicInterfaceSelectId | |
102 | 101 | if (unref(selectTarget)) { |
103 | 102 | chartEditStore.updateComponentList(chartEditStore.fetchTargetIndex(), { |
104 | 103 | ...unref(selectTarget)!, | ... | ... |