<template>
<div class="remote">
<plugin-setting
title="获取远程字段"
class="remote-setting"
:isSecond="true"
@cancel="closePanel"
@save="saveRemote"
>
<template #content>
<div class="create-config">
<div>
<tiny-collapse v-model="state.activeName">
<tiny-collapse-item name="excute">
<template #title>请求信息</template>
<data-source-remote-form
v-model="state.remoteData.options"
@sendRequest="sendRequest"
></data-source-remote-form>
<data-source-remote-autoload v-model="state.remoteData.options.isSync"></data-source-remote-autoload>
<div class="tabBox">
<tiny-tabs v-model="state.activeNameTabs" tab-style="button-card">
<tiny-tab-item class="json-tab" title="请求JSON参数" name="jsonsTab">
<data-source-remote-parameter
v-model="state.remoteData.options.params"
></data-source-remote-parameter>
</tiny-tab-item>
<tiny-tab-item title="请求处理" name="responseTab">
<data-source-remote-adapter
ref="dataSourceRemoteAdapteRef"
v-model="state.responseData"
@sendRequst="sendRequest"
></data-source-remote-adapter>
</tiny-tab-item>
</tiny-tabs>
</div>
<tiny-button type="primary" @click.stop="sendRequest" class="send"> 获取数据</tiny-button>
</tiny-collapse-item>
<tiny-collapse-item name="result">
<template #title>请求结果</template>
<data-srouce-remote-data-result
v-model="state.remoteData.result"
@check="saveRemote"
></data-srouce-remote-data-result>
</tiny-collapse-item>
</tiny-collapse>
</div>
</div>
</template>
</plugin-setting>
</div>
</template>
<script>
import { reactive, watch, ref } from 'vue'
import { Collapse, CollapseItem, Tabs, TabItem, Button } from '@opentiny/vue'
import { PluginSetting } from '@opentiny/tiny-engine-common'
import DataSourceRemoteForm, { getServiceForm } from './DataSourceRemoteForm.vue'
import DataSourceRemoteParameter from './DataSourceRemoteParameter.vue'
import DataSourceRemoteAutoload from './DataSourceRemoteAutoload.vue'
import DataSourceRemoteAdapter from './DataSourceRemoteDataAdapter.vue'
import DataSrouceRemoteDataResult, { getResponseData } from './DataSourceRemoteDataResult.vue'
import { open as openRemoteMapping } from './DataSourceRemoteMapping.vue'
import { useDataSource, useNotify } from '@opentiny/tiny-engine-meta-register'
import { isEmptyObject } from '@opentiny/vue-renderless/common/type'
import { utils } from '@opentiny/tiny-engine-utils'
import { getRequest } from './js/datasource'
const { reactiveObj2String: obj2String, string2Obj } = utils
export const isOpen = ref(false)
export const open = () => {
isOpen.value = true
}
export const close = () => {
isOpen.value = false
}
export default {
components: {
TinyCollapse: Collapse,
TinyButton: Button,
TinyCollapseItem: CollapseItem,
TinyTabs: Tabs,
TinyTabItem: TabItem,
PluginSetting,
DataSourceRemoteForm,
DataSourceRemoteParameter,
DataSourceRemoteAutoload,
DataSourceRemoteAdapter,
DataSrouceRemoteDataResult
},
props: {
editable: {
type: Boolean,
default: true
},
modelValue: {
type: Object,
default: () => ({
options: { name: '', descriptions: '', method: '', uri: '', params: '', isSync: true },
willFetch: {},
dataHandler: {},
result: {},
shouldFetch: {},
errorHandler: {}
})
}
},
emits: ['confirm'],
setup(props, { emit }) {
const dataSourceRemoteAdapteRef = ref(null)
const { dataSourceState } = useDataSource()
const state = reactive({
remoteData: { options: {} },
activeName: ['excute', 'result'],
responseData: {
shouldFetch: null,
dataHandler: null,
errorHandler: null,
willFetch: null
},
activeNameTabs: 'jsonsTab'
})
watch(
() => props.modelValue,
(value) => {
const { willFetch, dataHandler, shouldFetch, errorHandler, columns } = value
state.remoteData.options = { ...value?.options } || {}
state.remoteData.options.params = obj2String(value?.options?.params)
state.responseData.willFetch = willFetch?.value || ''
state.responseData.dataHandler = dataHandler?.value || ''
state.responseData.shouldFetch = shouldFetch?.value || ''
state.responseData.errorHandler = errorHandler?.value || ''
columns?.length === 0 && (state.remoteData.result = {})
},
{ immediate: true }
)
const saveRemote = () => {
getServiceForm().validate((valid) => {
if (valid) {
state.remoteData.result = string2Obj(getResponseData())
const save = () => {
let params = state.remoteData.options?.params
if (params) {
params = string2Obj(params)
}
dataSourceState.remoteConfig = {
options: { ...state.remoteData.options, params },
...dataSourceRemoteAdapteRef.value.getEditorValue()
}
state.remoteData.result = string2Obj(getResponseData())
emit('confirm', state.remoteData.result)
close()
}
save()
if (!isEmptyObject(state.remoteData.result)) {
openRemoteMapping()
}
}
})
}
const sendRequest = async () => {
try {
await getServiceForm().validate()
} catch (error) {
return
}
const options = { ...state.remoteData.options }
if (options.params) {
options.params = string2Obj(options.params)
}
const request = getRequest({ options, ...dataSourceRemoteAdapteRef.value.getEditorValue() })
* 按照数据源请求面板的提示,dataSourceMap函数的相应结果的结构应该会是:
* 对于对象数组:{ code: string, msg: string, data: {items: any[], total: number} }
* 对于树结构:{ code: string, msg: string, data: any }
*/
request
.load()
.then((res) => {
state.remoteData.result = Array.isArray(res?.data?.items) ? res.data.items[0] : res?.data || res
useNotify({
type: 'success',
title: '请求成功',
message: '返回已填充到"请求结果"'
})
const remoteDataEditor = document.querySelector('#remote-data-editor')
remoteDataEditor.scrollIntoView()
})
.catch((error) => {
useNotify({
type: 'error',
title: '请求失败',
message: error.message || '请求失败,请确认请求地址是否正确!'
})
})
}
return {
state,
dataSourceRemoteAdapteRef,
closePanel: close,
saveRemote,
sendRequest,
isOpen
}
}
}
</script>
<style lang="less" scoped>
.remote {
.remote-setting {
:deep(.plugin-save) {
display: none;
}
}
:deep(.plugin-setting-content) {
padding: 0;
}
.create-config {
:deep(.title) {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: var(--ti-lowcode-datasource-toolbar-bg);
border-top: 1px solid var(--ti-lowcode-datasource-tabs-border-color);
color: var(--ti-lowcode-datasource-toolbar-breadcrumb-color);
}
.send {
margin-bottom: 14px;
}
.tip-dot {
padding-left: 4px;
color: var(--ti-lowcode-datasource-description-error-color);
}
.use-service {
color: var(--ti-lowcode-datasource-toolbar-breadcrumb-color);
font-size: 12px;
margin-top: 10px;
span {
color: var(--ti-lowcode-datasource-description-error-color);
}
}
:deep(.send-service) {
text-align: right;
border-top: 1px solid var(--ti-lowcode-datasource-tabs-border-color);
padding: 20px 10px;
margin-bottom: 10px;
.use-service {
text-align: left;
padding-top: 5px;
div {
margin-bottom: 5px;
}
}
.title {
margin-bottom: 10px;
}
}
:deep(.send-request) {
margin: 12px;
}
.monaco-editor {
height: 80px;
margin-top: 8px;
}
.tabBox {
box-sizing: border-box;
overflow-y: scroll;
:deep(.tiny-tabs.tiny-tabs--button-card .tiny-tabs__item) {
border-radius: 4px;
}
:deep(.tiny-tabs__content) {
margin: 12px 0;
}
:deep(.is-active) {
.tiny-tabs__item__title {
color: var(--ti-lowcode-datasource-tabs-bottom-border-color);
}
}
:deep(.tiny-tabs__item__title) {
font-size: 14px;
}
}
:deep(.tiny-tabs__item__title) {
margin-right: 20px;
margin-left: 15px;
color: var(--ti-lowcode-datasource-label-color);
}
}
}
</style>