Commit 93d60ba9856e0eeefebe296091422a9140b20054

Authored by xp.Huang
2 parents 07cfb735 9e4ba1a1

Merge branch 'local_dev_ft' into 'main_dev'

feat:新增公共接口管理(先测试)

See merge request yunteng/thingskit-front!513
@@ -3,17 +3,20 @@ import type { @@ -3,17 +3,20 @@ import type {
3 queryPageParams, 3 queryPageParams,
4 BigScreenCenterParams, 4 BigScreenCenterParams,
5 ConfigurationCenterInfo, 5 ConfigurationCenterInfo,
6 - BigScreenCenterItemsModal, 6 + BigScreenCenterItemsModel,
  7 + BigScreenInterfaceParams,
7 } from './model/bigscreenCenterModel'; 8 } from './model/bigscreenCenterModel';
8 import { getPageData } from '../../base'; 9 import { getPageData } from '../../base';
9 import { FileUploadResponse } from '../../oem/model'; 10 import { FileUploadResponse } from '../../oem/model';
10 enum API { 11 enum API {
11 basicUrl = '/data_view', 12 basicUrl = '/data_view',
12 UPLOAD = '/oss/upload', 13 UPLOAD = '/oss/upload',
  14 + //大屏公共接口
  15 + DATA_VIEW_INTERFACE = '/data_view_interface',
13 } 16 }
14 17
15 export const getPage = (params: queryPageParams) => { 18 export const getPage = (params: queryPageParams) => {
16 - return getPageData<BigScreenCenterItemsModal>(params, API.basicUrl); 19 + return getPageData<BigScreenCenterItemsModel>(params, API.basicUrl);
17 }; 20 };
18 21
19 export const saveConfigurationCenter = (params: BigScreenCenterParams) => { 22 export const saveConfigurationCenter = (params: BigScreenCenterParams) => {
@@ -46,3 +49,58 @@ export const saveOrUpdateBigScreenCenter = (params: ConfigurationCenterInfo, isU @@ -46,3 +49,58 @@ export const saveOrUpdateBigScreenCenter = (params: ConfigurationCenterInfo, isU
46 export const uploadThumbnail = (file: FormData) => { 49 export const uploadThumbnail = (file: FormData) => {
47 return defHttp.post<FileUploadResponse>({ url: API.UPLOAD, params: file }); 50 return defHttp.post<FileUploadResponse>({ url: API.UPLOAD, params: file });
48 }; 51 };
  52 +
  53 +export const bigScreenPublish = (id) => {
  54 + return defHttp.get({
  55 + url: API.basicUrl + '/publish/' + id,
  56 + });
  57 +};
  58 +
  59 +export const bigScreenCancelPublish = (id) => {
  60 + return defHttp.get({
  61 + url: API.basicUrl + '/cancel_publish/' + id,
  62 + });
  63 +};
  64 +
  65 +/**
  66 + * 大屏公共接口
  67 + */
  68 +
  69 +export const getDataViewInterfacePage = (params: queryPageParams) => {
  70 + return getPageData<BigScreenCenterItemsModel>(params, API.DATA_VIEW_INTERFACE);
  71 +};
  72 +
  73 +export const saveDataViewInterface = (params: BigScreenInterfaceParams) => {
  74 + return defHttp.post({
  75 + url: API.DATA_VIEW_INTERFACE,
  76 + data: params,
  77 + });
  78 +};
  79 +
  80 +export const updateDataViewInterface = (params: BigScreenInterfaceParams) => {
  81 + return defHttp.put({
  82 + url: API.DATA_VIEW_INTERFACE,
  83 + data: params,
  84 + });
  85 +};
  86 +
  87 +export const deleteBigViewInterface = (ids: string[]) => {
  88 + return defHttp.delete({
  89 + url: API.DATA_VIEW_INTERFACE,
  90 + data: {
  91 + ids: ids,
  92 + },
  93 + });
  94 +};
  95 +
  96 +export const getPublish = (id) => {
  97 + return defHttp.get({
  98 + url: API.DATA_VIEW_INTERFACE + '/publish/' + id,
  99 + });
  100 +};
  101 +
  102 +export const getCancelPublish = (id) => {
  103 + return defHttp.get({
  104 + url: API.DATA_VIEW_INTERFACE + '/cancel_publish/' + id,
  105 + });
  106 +};
@@ -25,3 +25,32 @@ export interface BigScreenCenterParams { @@ -25,3 +25,32 @@ export interface BigScreenCenterParams {
25 defaultContent?: string; 25 defaultContent?: string;
26 } 26 }
27 export type ConfigurationCenterInfo = BigScreenCenterParams; 27 export type ConfigurationCenterInfo = BigScreenCenterParams;
  28 +
  29 +/**
  30 + * 大屏公共接口
  31 + */
  32 +export interface BigScreenInterfaceParams {
  33 + createTime: string;
  34 + creator: string;
  35 + defaultConfig: string;
  36 + description: string;
  37 + enabled: boolean;
  38 + icon: string;
  39 + id: string;
  40 + interfaceName: string;
  41 + name: string;
  42 + remark: string;
  43 + requestContentType: 0;
  44 + requestHttpType: string;
  45 + requestOriginUrl: string;
  46 + requestParams: string;
  47 + requestUrl: string;
  48 + roleIds: [string];
  49 + state: number;
  50 + tenantExpireTime: string;
  51 + tenantId: string;
  52 + tenantProfileId: string;
  53 + tenantStatus: string;
  54 + updateTime: string;
  55 + updater: string;
  56 +}
1 -<script lang="ts" setup>  
2 - import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';  
3 - import { computed, useSlots } from 'vue';  
4 - import Icon from '../Icon';  
5 - import { usePermission } from '/@/hooks/web/usePermission';  
6 -  
7 - export interface AuthDropDownProps {  
8 - dropMenuList: AuthDropMenuList[];  
9 - trigger?: ('contextmenu' | 'click' | 'hover')[];  
10 - }  
11 -  
12 - export interface AuthDropMenuList {  
13 - icon?: string;  
14 - event: string | number;  
15 - text: string;  
16 - disabled?: boolean;  
17 - divider?: boolean;  
18 - auth?: string;  
19 - onClick?: Fn;  
20 - popconfirm?: {  
21 - cancelText?: string;  
22 - okText?: string;  
23 - okType?: string;  
24 - title?: string;  
25 - icon?: string;  
26 - disabled?: boolean;  
27 - onCancel?: Fn;  
28 - onConfirm?: Fn;  
29 - onVisibleChange?: Fn;  
30 - };  
31 - }  
32 -  
33 - const props = defineProps<AuthDropDownProps>();  
34 -  
35 - const slot = useSlots();  
36 -  
37 - const { hasPermission } = usePermission();  
38 -  
39 - const getMenuList = computed(() => {  
40 - const { dropMenuList } = props;  
41 - return dropMenuList.filter((menu) => (menu.auth ? hasPermission(menu.auth) : true));  
42 - });  
43 -  
44 - const hasDefaultSlot = computed(() => {  
45 - return !!slot.default;  
46 - });  
47 -</script>  
48 -  
49 -<template>  
50 - <Dropdown :trigger="$props.trigger">  
51 - <template #overlay>  
52 - <Menu v-if="getMenuList.length">  
53 - <template v-for="item in getMenuList" :key="item.event">  
54 - <Menu.Divider v-if="item.divider" />  
55 - <Menu.Item v-if="!item.popconfirm" @click="item.onClick">  
56 - <span class="flex justify-center items-center">  
57 - <Icon :icon="item.icon" />  
58 - <span class="ml-2">{{ item.text }}</span>  
59 - </span>  
60 - </Menu.Item>  
61 - <Menu.Item v-if="item.popconfirm">  
62 - <Popconfirm v-bind="item.popconfirm">  
63 - <template v-if="item.popconfirm.icon" #icon>  
64 - <Icon :icon="item.popconfirm.icon" />  
65 - </template>  
66 - <span class="flex justify-center items-center">  
67 - <Icon :icon="item.icon" />  
68 - <span class="ml-2">{{ item.text }}</span>  
69 - </span>  
70 - </Popconfirm>  
71 - </Menu.Item>  
72 - </template>  
73 - </Menu>  
74 - </template>  
75 - <Icon  
76 - v-if="!hasDefaultSlot"  
77 - class="items-center justify-center"  
78 - icon="ant-design:ellipsis-outlined"  
79 - :class="!getMenuList.length ? '!text-gray-200 !cursor-not-allowed' : ''"  
80 - />  
81 - <slot name="default"></slot>  
82 - </Dropdown>  
83 -</template> 1 +<script lang="ts" setup>
  2 + import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
  3 + import { computed, useSlots } from 'vue';
  4 + import Icon from '../Icon';
  5 + import { usePermission } from '/@/hooks/web/usePermission';
  6 +
  7 + export interface AuthDropDownProps {
  8 + dropMenuList: AuthDropMenuList[];
  9 + trigger?: ('contextmenu' | 'click' | 'hover')[];
  10 + }
  11 +
  12 + export interface AuthDropMenuList {
  13 + icon?: string;
  14 + event: string | number;
  15 + text: string;
  16 + disabled?: boolean;
  17 + divider?: boolean;
  18 + auth?: string;
  19 + onClick?: Fn;
  20 + popconfirm?: {
  21 + cancelText?: string;
  22 + okText?: string;
  23 + okType?: string;
  24 + title?: string;
  25 + icon?: string;
  26 + disabled?: boolean;
  27 + onCancel?: Fn;
  28 + onConfirm?: Fn;
  29 + onVisibleChange?: Fn;
  30 + };
  31 + }
  32 +
  33 + const props = defineProps<AuthDropDownProps>();
  34 +
  35 + const slot = useSlots();
  36 +
  37 + const { hasPermission } = usePermission();
  38 +
  39 + const getMenuList = computed(() => {
  40 + const { dropMenuList } = props;
  41 + return dropMenuList.filter((menu) => (menu.auth ? hasPermission(menu.auth) : true));
  42 + });
  43 +
  44 + const hasDefaultSlot = computed(() => {
  45 + return !!slot.default;
  46 + });
  47 +</script>
  48 +
  49 +<template>
  50 + <Dropdown :trigger="$props.trigger">
  51 + <template #overlay>
  52 + <Menu v-if="getMenuList.length">
  53 + <template v-for="item in getMenuList" :key="item.event">
  54 + <Menu.Divider v-if="item.divider" />
  55 + <Menu.Item :disabled="item.disabled" v-if="!item.popconfirm" @click="item.onClick">
  56 + <span class="flex justify-center items-center">
  57 + <Icon :icon="item.icon" />
  58 + <span class="ml-2">{{ item.text }}</span>
  59 + </span>
  60 + </Menu.Item>
  61 + <Menu.Item :disabled="item.disabled" v-if="item.popconfirm">
  62 + <Popconfirm v-bind="item.popconfirm">
  63 + <template v-if="item.popconfirm.icon" #icon>
  64 + <Icon :icon="item.popconfirm.icon" />
  65 + </template>
  66 + <span class="flex justify-center items-center">
  67 + <Icon :icon="item.icon" />
  68 + <span class="ml-2">{{ item.text }}</span>
  69 + </span>
  70 + </Popconfirm>
  71 + </Menu.Item>
  72 + </template>
  73 + </Menu>
  74 + </template>
  75 + <Icon
  76 + v-if="!hasDefaultSlot"
  77 + class="items-center justify-center"
  78 + icon="ant-design:ellipsis-outlined"
  79 + :class="!getMenuList.length ? '!text-gray-200 !cursor-not-allowed' : ''"
  80 + />
  81 + <slot name="default"></slot>
  82 + </Dropdown>
  83 +</template>
@@ -3,7 +3,12 @@ @@ -3,7 +3,12 @@
3 import { ReloadOutlined } from '@ant-design/icons-vue'; 3 import { ReloadOutlined } from '@ant-design/icons-vue';
4 import { onMounted, reactive, ref, unref } from 'vue'; 4 import { onMounted, reactive, ref, unref } from 'vue';
5 import { OrganizationIdTree, useResetOrganizationTree } from '../common/organizationIdTree'; 5 import { OrganizationIdTree, useResetOrganizationTree } from '../common/organizationIdTree';
6 - import { deleteBigScreenenter, getPage } from '/@/api/bigscreen/center/bigscreenCenter'; 6 + import {
  7 + bigScreenCancelPublish,
  8 + bigScreenPublish,
  9 + deleteBigScreenenter,
  10 + getPage,
  11 + } from '/@/api/bigscreen/center/bigscreenCenter';
7 import { BigScreenCenterItemsModel } from '/@/api/bigscreen/center/model/bigscreenCenterModel'; 12 import { BigScreenCenterItemsModel } from '/@/api/bigscreen/center/model/bigscreenCenterModel';
8 import { PageWrapper } from '/@/components/Page'; 13 import { PageWrapper } from '/@/components/Page';
9 import { BasicForm, useForm } from '/@/components/Form'; 14 import { BasicForm, useForm } from '/@/components/Form';
@@ -18,6 +23,7 @@ @@ -18,6 +23,7 @@
18 import { useGlobSetting } from '/@/hooks/setting'; 23 import { useGlobSetting } from '/@/hooks/setting';
19 import { AuthIcon, CardLayoutButton } from '/@/components/Widget'; 24 import { AuthIcon, CardLayoutButton } from '/@/components/Widget';
20 import AuthDropDown from '/@/components/Widget/AuthDropDown.vue'; 25 import AuthDropDown from '/@/components/Widget/AuthDropDown.vue';
  26 + import { PublicApiDrawer } from './publicApi/index';
21 27
22 const listColumn = ref(5); 28 const listColumn = ref(5);
23 29
@@ -88,6 +94,8 @@ @@ -88,6 +94,8 @@
88 94
89 const [registerDrawer, { openDrawer }] = useDrawer(); 95 const [registerDrawer, { openDrawer }] = useDrawer();
90 96
  97 + const [registerPublicDrawer, { openDrawer: openPublicApiDrawer }] = useDrawer();
  98 +
91 const handleCreateOrUpdate = (record?: BigScreenCenterItemsModel) => { 99 const handleCreateOrUpdate = (record?: BigScreenCenterItemsModel) => {
92 if (record) { 100 if (record) {
93 openDrawer(true, { 101 openDrawer(true, {
@@ -101,6 +109,8 @@ @@ -101,6 +109,8 @@
101 } 109 }
102 }; 110 };
103 111
  112 + const handleCreateOrUpdatePublicApi = () => openPublicApiDrawer(true);
  113 +
104 const { largeDesignerPrefix } = useGlobSetting(); 114 const { largeDesignerPrefix } = useGlobSetting();
105 115
106 const handlePreview = (record: BigScreenCenterItemsModel) => { 116 const handlePreview = (record: BigScreenCenterItemsModel) => {
@@ -143,6 +153,19 @@ @@ -143,6 +153,19 @@
143 (listContainerEl.style.overflowY = 'auto') && 153 (listContainerEl.style.overflowY = 'auto') &&
144 (listContainerEl.style.overflowX = 'hidden'); 154 (listContainerEl.style.overflowX = 'hidden');
145 }); 155 });
  156 +
  157 + const getPublicApiListData = () => {};
  158 +
  159 + const handlePublish = async ({ id }) => {
  160 + await bigScreenPublish(id);
  161 + createMessage.success('发布成功');
  162 + getListData();
  163 + };
  164 + const handleCancelPublish = async ({ id }) => {
  165 + await bigScreenCancelPublish(id);
  166 + createMessage.success('取消发布成功');
  167 + getListData();
  168 + };
146 </script> 169 </script>
147 170
148 <template> 171 <template>
@@ -166,6 +189,9 @@ @@ -166,6 +189,9 @@
166 <Authority :value="ConfigurationPermission.CREATE"> 189 <Authority :value="ConfigurationPermission.CREATE">
167 <Button type="primary" @click="handleCreateOrUpdate()">新增大屏</Button> 190 <Button type="primary" @click="handleCreateOrUpdate()">新增大屏</Button>
168 </Authority> 191 </Authority>
  192 + <Authority :value="ConfigurationPermission.CREATE">
  193 + <Button type="primary" @click="handleCreateOrUpdatePublicApi()">公共接口管理</Button>
  194 + </Authority>
169 <CardLayoutButton v-model:value="listColumn" @change="handleCardLayoutChange" /> 195 <CardLayoutButton v-model:value="listColumn" @change="handleCardLayoutChange" />
170 <Tooltip title="刷新"> 196 <Tooltip title="刷新">
171 <Button type="primary" @click="getListData"> 197 <Button type="primary" @click="getListData">
@@ -196,6 +222,11 @@ @@ -196,6 +222,11 @@
196 >所属组织:{{ item.organizationDTO.name }}</span 222 >所属组织:{{ item.organizationDTO.name }}</span
197 > 223 >
198 </div> 224 </div>
  225 + <div>
  226 + <span class="masker-text-state"
  227 + >发布状态:{{ item.state === 1 ? '已发布' : '未发布' }}</span
  228 + >
  229 + </div>
199 </div> 230 </div>
200 </div> 231 </div>
201 </div> 232 </div>
@@ -218,16 +249,33 @@ @@ -218,16 +249,33 @@
218 <AuthDropDown 249 <AuthDropDown
219 :dropMenuList="[ 250 :dropMenuList="[
220 { 251 {
  252 + text: '发布',
  253 + auth: ConfigurationPermission.UPDATE,
  254 + icon: 'ant-design:node-expand-outlined',
  255 + event: '',
  256 + onClick: handlePublish.bind(null, item),
  257 + disabled: item.state === 0 ? false : true,
  258 + },
  259 + {
  260 + text: '取消发布',
  261 + icon: 'ant-design:node-collapse-outlined',
  262 + event: '',
  263 + onClick: handleCancelPublish.bind(null, item),
  264 + disabled: item.state === 1 ? false : true,
  265 + },
  266 + {
221 text: '编辑', 267 text: '编辑',
222 auth: ConfigurationPermission.UPDATE, 268 auth: ConfigurationPermission.UPDATE,
223 icon: 'clarity:note-edit-line', 269 icon: 'clarity:note-edit-line',
224 event: '', 270 event: '',
225 onClick: handleCreateOrUpdate.bind(null, item), 271 onClick: handleCreateOrUpdate.bind(null, item),
  272 + disabled: item.state === 0 ? false : true,
226 }, 273 },
227 { 274 {
228 text: '删除', 275 text: '删除',
229 auth: ConfigurationPermission.DELETE, 276 auth: ConfigurationPermission.DELETE,
230 icon: 'ant-design:delete-outlined', 277 icon: 'ant-design:delete-outlined',
  278 + disabled: item.state === 0 ? false : true,
231 event: '', 279 event: '',
232 popconfirm: { 280 popconfirm: {
233 title: '是否确认删除操作?', 281 title: '是否确认删除操作?',
@@ -244,6 +292,7 @@ @@ -244,6 +292,7 @@
244 </List> 292 </List>
245 </section> 293 </section>
246 <ConfigurationCenterDrawer @register="registerDrawer" @success="getListData" /> 294 <ConfigurationCenterDrawer @register="registerDrawer" @success="getListData" />
  295 + <PublicApiDrawer @register="registerPublicDrawer" @success="getPublicApiListData" />
247 </PageWrapper> 296 </PageWrapper>
248 </template> 297 </template>
249 298
@@ -298,6 +347,13 @@ @@ -298,6 +347,13 @@
298 bottom: 0; 347 bottom: 0;
299 left: 0.8rem; 348 left: 0.8rem;
300 } 349 }
  350 +
  351 + .masker-text-state {
  352 + font-size: 12px;
  353 + position: absolute;
  354 + bottom: 0;
  355 + right: 0.8rem;
  356 + }
301 } 357 }
302 } 358 }
303 359
  1 +import EditCellTable from './index.vue';
  2 +
  3 +export { EditCellTable };
  1 +<template>
  2 + <div class="table-content">
  3 + <!-- 采用的原生表格 -->
  4 + <table align="center">
  5 + <thead>
  6 + <tr>
  7 + <th></th>
  8 + <th>内置参数</th>
  9 + <th>参数名</th>
  10 + <th>是否必须</th>
  11 + <th>操作</th>
  12 + </tr>
  13 + </thead>
  14 + <tbody>
  15 + <tr v-for="(item, index) in tableArray.content" :key="index">
  16 + <td>
  17 + {{ index + 1 }}
  18 + </td>
  19 + <td>
  20 + <Select
  21 + v-model:value="item.key"
  22 + placeholder="请选择"
  23 + :options="selectOptions"
  24 + @change="handleChange"
  25 + allowClear
  26 + />
  27 + </td>
  28 + <td>
  29 + <a-input
  30 + :disabled="item.editDisabled"
  31 + placeholder="请输入"
  32 + v-model:value="item.value"
  33 + />
  34 + </td>
  35 + <td>
  36 + <a-switch v-model:checked="item.required" />
  37 + </td>
  38 + <td>
  39 + <div>
  40 + <Button type="dashed" @click="add(item, index)">
  41 + <template #icon><PlusOutlined /></template
  42 + ></Button>
  43 + <Button type="dashed" style="margin-left: 5px" @click="remove(item, index)">
  44 + <template #icon> <MinusOutlined /></template>
  45 + </Button>
  46 + </div>
  47 + </td>
  48 + </tr>
  49 + </tbody>
  50 + </table>
  51 + </div>
  52 +</template>
  53 +<script lang="ts" setup name="editCellTable">
  54 + import { reactive, ref, onMounted, nextTick } from 'vue';
  55 + import { Select, Button } from 'ant-design-vue';
  56 + import { findDictItemByCode } from '/@/api/system/dict';
  57 + import { PlusOutlined, MinusOutlined } from '@ant-design/icons-vue';
  58 +
  59 + defineProps({
  60 + method: {
  61 + type: String,
  62 + },
  63 + });
  64 +
  65 + onMounted(() => {
  66 + getSelectOptions();
  67 + });
  68 +
  69 + const getSelectOptions = async () => {
  70 + const res = await findDictItemByCode({ dictCode: 'dataview_builtin_params' });
  71 + selectOptions.value = res.map((m) => ({ label: m.itemText, value: m.itemValue }));
  72 + selectOptions.value.push({
  73 + label: '自定义',
  74 + value: 'scope',
  75 + });
  76 + };
  77 +
  78 + const selectOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  79 +
  80 + type defaultItem = {
  81 + key: null | string;
  82 + value: string;
  83 + editDisabled: boolean;
  84 + required: boolean;
  85 + };
  86 +
  87 + const tableArray = reactive<{
  88 + content: defaultItem[];
  89 + }>({
  90 + content: [
  91 + {
  92 + key: null,
  93 + value: '',
  94 + editDisabled: false,
  95 + required: false,
  96 + },
  97 + ],
  98 + });
  99 +
  100 + // 新增
  101 + const add = (_, index: number) => {
  102 + tableArray.content.splice(index + 1, 0, {
  103 + key: null,
  104 + value: '',
  105 + editDisabled: false,
  106 + required: false,
  107 + });
  108 + };
  109 +
  110 + // 减少
  111 + const remove = (item, index: number) => {
  112 + if (tableArray.content.length !== 1) {
  113 + selectOptions.value.forEach((ele) => {
  114 + if (ele.value == item.key) {
  115 + ele.disabled = false;
  116 + }
  117 + });
  118 + tableArray.content.splice(index, 1);
  119 + }
  120 + };
  121 +
  122 + //Select互斥
  123 + const handleChange = () => {
  124 + selectOptions.value.forEach((ele) => {
  125 + ele.disabled = false;
  126 + tableArray.content.forEach((element) => {
  127 + if (element.key === 'scope') {
  128 + element.editDisabled = false;
  129 + } else {
  130 + element.value = '';
  131 + element.editDisabled = true;
  132 + }
  133 + if (element.key === ele.value && element.key !== 'scope') {
  134 + ele.disabled = true;
  135 + }
  136 + });
  137 + });
  138 + };
  139 +
  140 + //获取数据
  141 + const getValue = () => {
  142 + return tableArray.content;
  143 + };
  144 +
  145 + //设置数据
  146 + const setValue = (data) => {
  147 + nextTick(() => (tableArray.content = data));
  148 + nextTick(() =>
  149 + setTimeout(() => {
  150 + tableArray.content.forEach(() => {
  151 + handleChange();
  152 + });
  153 + }, 20)
  154 + );
  155 + };
  156 +
  157 + //重置数据
  158 + const resetValue = () => {
  159 + tableArray.content = [];
  160 + tableArray.content.push({
  161 + key: null,
  162 + value: '',
  163 + editDisabled: false,
  164 + required: false,
  165 + });
  166 + nextTick(() => {
  167 + tableArray.content.forEach(() => {
  168 + handleChange();
  169 + });
  170 + });
  171 + };
  172 + defineExpose({
  173 + getValue,
  174 + setValue,
  175 + resetValue,
  176 + });
  177 +</script>
  178 +
  179 +<style scoped lang="less">
  180 + @table-color: #e5e7eb;
  181 +
  182 + .table-border-color {
  183 + border: 1px solid #e5e7eb;
  184 + text-align: center;
  185 + }
  186 +
  187 + .table-content {
  188 + overflow-x: auto;
  189 +
  190 + table {
  191 + border-collapse: collapse;
  192 + width: 100%;
  193 + &:extend(.table-border-color);
  194 + }
  195 +
  196 + table td {
  197 + width: 7vw;
  198 + padding: 5px;
  199 + white-space: nowrap;
  200 + &:extend(.table-border-color);
  201 + }
  202 +
  203 + table th {
  204 + padding: 5px;
  205 + &:extend(.table-border-color);
  206 + }
  207 + }
  208 +</style>
  1 +<template>
  2 + <div>
  3 + <a-radio-group
  4 + v-model:value="getRequestBody.content.requestParamsBodyType"
  5 + @change="handleChange"
  6 + >
  7 + <a-radio v-for="item in RequestBodyTypeEnum" :key="item" :value="item">{{ item }}</a-radio>
  8 + </a-radio-group>
  9 + <div class="mt-3">
  10 + <a-textarea
  11 + v-show="getRequestBody.content.requestParamsBodyType === 'none'"
  12 + disabled
  13 + placeholder="该接口没有 Body 体"
  14 + :rows="2"
  15 + />
  16 + <EditCellTable
  17 + ref="editCellTableRef"
  18 + v-show="
  19 + getRequestBody.content.requestParamsBodyType === 'form-data' ||
  20 + getRequestBody.content.requestParamsBodyType === 'x-www-form-urlencoded'
  21 + "
  22 + />
  23 + <a-textarea
  24 + v-model:value="getRequestBody.content.json"
  25 + v-show="getRequestBody.content.requestParamsBodyType === 'json'"
  26 + placeholder="请输入json"
  27 + :rows="6"
  28 + />
  29 + <a-textarea
  30 + v-model:value="getRequestBody.content.xml"
  31 + v-show="getRequestBody.content.requestParamsBodyType === 'xml'"
  32 + placeholder="请输入xml"
  33 + :rows="6"
  34 + />
  35 + </div>
  36 + </div>
  37 +</template>
  38 +<script lang="ts" setup name="body">
  39 + import { reactive, ref, nextTick } from 'vue';
  40 + import { RequestBodyTypeEnum } from '../../enum/index';
  41 + import { EditCellTable } from '../EditCellTable/index';
  42 +
  43 + defineProps({
  44 + method: {
  45 + type: String,
  46 + },
  47 + paramsType: {
  48 + type: String,
  49 + },
  50 + data: {
  51 + type: Array,
  52 + },
  53 + });
  54 +
  55 + const getRequestBody = reactive({
  56 + content: {
  57 + requestParamsBodyType: 'none',
  58 + json: '',
  59 + xml: '',
  60 + 'form-data': {},
  61 + 'x-www-form-urlencoded': {},
  62 + },
  63 + });
  64 +
  65 + const editCellTableRef = ref<InstanceType<typeof EditCellTable>>();
  66 +
  67 + const handleChange = ({ target }) => {
  68 + const { value } = target;
  69 + getRequestBody.content.requestParamsBodyType = value;
  70 + };
  71 +
  72 + //获取数据
  73 + const getValue = () => {
  74 + const type = Reflect.get(getRequestBody.content, 'requestParamsBodyType');
  75 + if (type === 'none') getRequestBody.content = {} as any;
  76 + const values = editCellTableRef.value?.getValue();
  77 + if (type === 'form-data') getRequestBody.content['form-data'] = values as any;
  78 + if (type === 'x-www-form-urlencoded')
  79 + getRequestBody.content['x-www-form-urlencoded'] = values as any;
  80 + return getRequestBody.content;
  81 + };
  82 +
  83 + //设置数据
  84 + const setValue = (data) => {
  85 + nextTick(() => {
  86 + try {
  87 + if (!data) return;
  88 + const type = data?.requestParamsBodyType;
  89 + getRequestBody.content.requestParamsBodyType = type;
  90 + type === 'none'
  91 + ? null
  92 + : type === 'form-data'
  93 + ? editCellTableRef.value?.setValue(data['form-data'])
  94 + : type === 'x-www-form-urlencoded'
  95 + ? editCellTableRef.value?.setValue(data['x-www-form-urlencoded'])
  96 + : type === 'json'
  97 + ? (getRequestBody.content.json = data['json'])
  98 + : type === 'xml'
  99 + ? (getRequestBody.content.xml = data['xml'])
  100 + : '';
  101 + } finally {
  102 + }
  103 + });
  104 + };
  105 +
  106 + //重置数据
  107 + const resetValue = () => {
  108 + for (let i in getRequestBody.content) Reflect.set(getRequestBody.content, i, '');
  109 + getRequestBody.content.requestParamsBodyType = 'none';
  110 + nextTick(() => editCellTableRef?.value?.resetValue());
  111 + };
  112 + defineExpose({
  113 + getValue,
  114 + setValue,
  115 + resetValue,
  116 + });
  117 +</script>
  1 +<template>
  2 + <div>
  3 + <Tabs @change="handleChange" v-model:activeKey="activeKey">
  4 + <TabPane class="tab-pane" forceRender key="Params" tab="Params">
  5 + <EditCellTable ref="editCellTableRef" :method="method" />
  6 + <TestRequest
  7 + @testInterface="handleTestInterface"
  8 + ref="testParRequestRef"
  9 + :method="method"
  10 + :data="dataList"
  11 + />
  12 + </TabPane>
  13 + <TabPane
  14 + v-if="method !== '2' && paramsType !== 'GET'"
  15 + class="tab-pane"
  16 + forceRender
  17 + key="Body"
  18 + tab="Body"
  19 + >
  20 + <Body ref="bodyRef" :method="method" :paramsType="paramsType" :data="dataList" />
  21 + <TestRequest
  22 + @testInterface="handleTestInterface"
  23 + ref="testBodyRequestRef"
  24 + :method="method"
  25 + :data="dataList"
  26 + />
  27 + </TabPane>
  28 + <TabPane v-if="method !== '2'" class="tab-pane" forceRender key="Header" tab="Header">
  29 + <EditCellTable ref="editHeaderCellTableRef" :method="method" />
  30 + <TestRequest
  31 + @testInterface="handleTestInterface"
  32 + ref="testHeaderRequestRef"
  33 + :method="method"
  34 + :data="dataList"
  35 + />
  36 + </TabPane>
  37 + </Tabs>
  38 + </div>
  39 +</template>
  40 +<script lang="ts" setup name="simpleRequest">
  41 + import { ref, nextTick } from 'vue';
  42 + import { Tabs, TabPane } from 'ant-design-vue';
  43 + import { EditCellTable } from '../EditCellTable';
  44 + import Body from './body.vue';
  45 + import { TestRequest } from '../TestRequest/index';
  46 +
  47 + defineProps({
  48 + method: {
  49 + type: String,
  50 + },
  51 + paramsType: {
  52 + type: String,
  53 + },
  54 + });
  55 +
  56 + const emits = defineEmits(['activeKey']);
  57 +
  58 + const activeKey = ref('Params');
  59 +
  60 + const editCellTableRef = ref<InstanceType<typeof EditCellTable>>();
  61 +
  62 + const editHeaderCellTableRef = ref<InstanceType<typeof EditCellTable>>();
  63 +
  64 + const testParRequestRef = ref<InstanceType<typeof TestRequest>>();
  65 +
  66 + const testBodyRequestRef = ref<InstanceType<typeof TestRequest>>();
  67 +
  68 + const testHeaderRequestRef = ref<InstanceType<typeof TestRequest>>();
  69 +
  70 + const dataList: any = ref(null);
  71 +
  72 + const bodyRef = ref<InstanceType<typeof Body>>();
  73 +
  74 + const handleChange = () => {
  75 + testParRequestRef.value?.setValue();
  76 + testHeaderRequestRef.value?.setValue();
  77 + testBodyRequestRef.value?.setValue();
  78 + };
  79 +
  80 + const handleTestInterface = () => {
  81 + const value = getValue(false);
  82 + nextTick(() => (dataList.value = value));
  83 + };
  84 +
  85 + //获取数据
  86 + const getValue = (status) => {
  87 + const type = activeKey.value;
  88 + const Body = bodyRef.value?.getValue();
  89 + const Params = editCellTableRef.value?.getValue();
  90 + const Header = editHeaderCellTableRef.value?.getValue();
  91 + status ? emits('activeKey', type) : null;
  92 + return type === 'Params' ? Params : type === 'Body' ? Body : Header;
  93 + };
  94 +
  95 + //设置数据
  96 + const setValue = (data) => {
  97 + const Objects = JSON.parse(data?.requestParams);
  98 + nextTick(() => {
  99 + if (!Objects) return;
  100 + activeKey.value = Objects?.type;
  101 + if (activeKey.value === 'Params') {
  102 + editCellTableRef.value?.setValue(Objects?.Params);
  103 + testParRequestRef.value?.setValue();
  104 + } else if (activeKey.value === 'Body') {
  105 + bodyRef.value?.setValue(Objects?.Body);
  106 + testBodyRequestRef.value?.setValue();
  107 + } else if (activeKey.value === 'Header') {
  108 + editHeaderCellTableRef.value?.setValue(Objects?.Header);
  109 + testHeaderRequestRef.value?.setValue();
  110 + } else {
  111 + }
  112 + });
  113 + };
  114 +
  115 + //重置数据
  116 + const resetValue = () => {
  117 + activeKey.value = 'Params';
  118 + nextTick(() => {
  119 + editCellTableRef.value?.resetValue();
  120 + editHeaderCellTableRef.value?.resetValue();
  121 + bodyRef.value?.resetValue();
  122 + handleChange();
  123 + });
  124 + };
  125 +
  126 + defineExpose({
  127 + getValue,
  128 + setValue,
  129 + resetValue,
  130 + });
  131 +</script>
  132 +
  133 +<style lang="less" scoped>
  134 + .tab-pane {
  135 + display: flex;
  136 + justify-content: flex-start;
  137 + flex-direction: column;
  138 + align-items: flex-start;
  139 + }
  140 +</style>
  1 +import TestEditCellTable from './index.vue';
  2 +
  3 +export { TestEditCellTable };
  1 +<template>
  2 + <div class="table-content">
  3 + <table align="center" style="width: 100%" cellspacing="0">
  4 + <thead>
  5 + <tr>
  6 + <th></th>
  7 + <th>内置参数</th>
  8 + <th>参数名</th>
  9 + </tr>
  10 + </thead>
  11 + <tbody>
  12 + <tr v-for="(item, index) in tableTestArray.content" :key="index">
  13 + <td>
  14 + {{ index + 1 }}
  15 + </td>
  16 + <td>
  17 + <Select
  18 + :disabled="true"
  19 + v-model:value="item.key"
  20 + placeholder="请选择"
  21 + notFoundContent="请选择"
  22 + style="width: 14rem"
  23 + :options="selectOptions"
  24 + @change="handleChange(item)"
  25 + allowClear
  26 + />
  27 + </td>
  28 + <td>
  29 + <a-input
  30 + v-if="item.key === 'scope'"
  31 + placeholder="请输入"
  32 + v-model:value="item.value"
  33 + style="width: 14rem"
  34 + />
  35 + <a-tree-select
  36 + v-else-if="item.key === 'organizationId'"
  37 + v-model:value="item.value"
  38 + show-search
  39 + style="width: 14rem"
  40 + :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
  41 + placeholder="请选择组织"
  42 + allow-clear
  43 + tree-default-expand-all
  44 + :tree-data="treeData"
  45 + @change="handleOrgnationChange(item)"
  46 + />
  47 + <Select
  48 + v-else-if="item.key === 'entityId'"
  49 + v-model:value="item.value"
  50 + placeholder="请选择"
  51 + notFoundContent="请选择"
  52 + style="width: 14rem"
  53 + :options="entityOptions"
  54 + allowClear
  55 + />
  56 + <Select
  57 + v-else-if="item.key === 'keys'"
  58 + v-model:value="item.value"
  59 + placeholder="请选择"
  60 + notFoundContent="请选择"
  61 + style="width: 14rem"
  62 + :options="attributeOptions"
  63 + allowClear
  64 + />
  65 + <Select
  66 + v-else
  67 + v-model:value="item.value"
  68 + placeholder="请选择"
  69 + notFoundContent="请选择"
  70 + style="width: 14rem"
  71 + :options="valueOptions"
  72 + allowClear
  73 + @change="handleValueChange(item)"
  74 + />
  75 + </td>
  76 + </tr>
  77 + </tbody>
  78 + </table>
  79 + </div>
  80 +</template>
  81 +<script lang="ts" setup name="editCellTable">
  82 + import { reactive, ref, onMounted } from 'vue';
  83 + import { Select } from 'ant-design-vue';
  84 + import { findDictItemByCode } from '/@/api/system/dict';
  85 + import { getAllDeviceByOrg } from '/@/api/dataBoard';
  86 + import { getDeviceAttributes } from '/@/api/dataBoard';
  87 + import { useApi } from '../../hooks/useApi';
  88 + import { cloneDeep } from 'lodash-es';
  89 +
  90 + const props = defineProps({
  91 + method: {
  92 + type: String,
  93 + },
  94 + });
  95 +
  96 + onMounted(async () => {
  97 + const res = await findDictItemByCode({ dictCode: 'dataview_builtin_params' });
  98 + selectOptions.value = res.map((m) => ({ label: m.itemText, value: m.itemValue }));
  99 + selectOptions.value.push({
  100 + label: '自定义',
  101 + value: 'scope',
  102 + });
  103 + if (props.method === '2')
  104 + selectOptions.value = selectOptions.value.filter((f) => f.value !== 'scope');
  105 + });
  106 +
  107 + const selectOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  108 +
  109 + const valueOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  110 +
  111 + const entityOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  112 +
  113 + const attributeOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  114 +
  115 + const treeData = ref([]);
  116 +
  117 + type defaultItem = {
  118 + key: null | string;
  119 + value: null | string;
  120 + editDisabled: boolean;
  121 + };
  122 +
  123 + const tableTestArray = reactive<{
  124 + content: defaultItem[];
  125 + }>({
  126 + content: [
  127 + {
  128 + key: null,
  129 + value: null,
  130 + editDisabled: false,
  131 + },
  132 + ],
  133 + });
  134 +
  135 + //设置数据
  136 + const setTableArray = (data) => {
  137 + const list = cloneDeep(data);
  138 + if (Array.isArray(list)) (tableTestArray.content = list) && getApi(list);
  139 + if (list.hasOwnProperty('form-data') && Array.isArray(list['form-data']))
  140 + (tableTestArray.content = list['form-data']) && getApi(list['form-data']);
  141 + if (
  142 + list.hasOwnProperty('x-www-form-urlencoded') &&
  143 + Array.isArray(list['x-www-form-urlencoded'])
  144 + )
  145 + (tableTestArray.content = list['x-www-form-urlencoded']) &&
  146 + getApi(list['x-www-form-urlencoded']);
  147 + };
  148 +
  149 + const getApi = (list) => {
  150 + list?.forEach(async (it) => {
  151 + if (it.key === 'deviceProfileId') {
  152 + const { options } = await useApi(it.key);
  153 + valueOptions.value = options;
  154 + } else if (it.key === 'organizationId') {
  155 + const { options } = await useApi(it.key);
  156 + treeData.value = options as any;
  157 + }
  158 + });
  159 + };
  160 +
  161 + //Select互斥
  162 + const handleChange = async (e) => {
  163 + selectOptions.value.forEach((ele) => {
  164 + ele.disabled = false;
  165 + tableTestArray.content.forEach((element) => {
  166 + if (element.key === e.key && element.key !== 'scope') {
  167 + element.value = null;
  168 + }
  169 + if (element.key === ele.value && element.key !== 'scope') {
  170 + ele.disabled = true;
  171 + }
  172 + });
  173 + });
  174 + //获取对应项
  175 + if (e.key === 'deviceProfileId') {
  176 + const { options } = await useApi(e.key);
  177 + valueOptions.value = options;
  178 + } else if (e.key === 'organizationId') {
  179 + const { options } = await useApi(e.key);
  180 + treeData.value = options as any;
  181 + } else if (e.key === 'entityId') {
  182 + const getOrganizationIds = tableTestArray.content
  183 + .map((f) => {
  184 + if (f.key === 'organizationId') {
  185 + return f.value;
  186 + }
  187 + })
  188 + .filter(Boolean);
  189 + if (getOrganizationIds.length === 0) return;
  190 + getEntityOptions(getOrganizationIds?.at(-1));
  191 + } else if (e.key === 'keys') {
  192 + const getIds = tableTestArray.content
  193 + .map((f) => {
  194 + if (f.key === 'deviceProfileId') {
  195 + return f.value;
  196 + }
  197 + })
  198 + .filter(Boolean);
  199 + if (getIds.length === 0) return;
  200 + getAttributeOptions({ deviceProfileId: getIds?.at(-1) });
  201 + } else if (e.key === 'date') {
  202 + valueOptions.value = [];
  203 + }
  204 + };
  205 +
  206 + const handleOrgnationChange = async (e) => {
  207 + tableTestArray.content.forEach((f) => {
  208 + if (f.key === 'entityId') {
  209 + f.value = null;
  210 + }
  211 + });
  212 + getEntityOptions(e.value);
  213 + };
  214 +
  215 + const getEntityOptions = async (organizationId: string, deviceProfileId?: string) => {
  216 + const res = await getAllDeviceByOrg(organizationId, deviceProfileId);
  217 + entityOptions.value = res.map((item) => ({
  218 + label: item.name,
  219 + value: item.tbDeviceId,
  220 + }));
  221 + };
  222 +
  223 + const getAttributeOptions = async (params) => {
  224 + const res = await getDeviceAttributes(params);
  225 + if (Object.keys(res).length === 0) return (attributeOptions.value.length = 0);
  226 + attributeOptions.value = res?.map((item) => ({ label: item.name, value: item.identifier }));
  227 + };
  228 +
  229 + const handleValueChange = (e) => {
  230 + if (e.key === 'deviceProfileId') {
  231 + tableTestArray.content.forEach((f) => {
  232 + if (f.key === 'keys') {
  233 + f.value = null;
  234 + }
  235 + });
  236 + getAttributeOptions({ deviceProfileId: e.value });
  237 + }
  238 + };
  239 +
  240 + //获取数据
  241 + const getValue = () => {
  242 + return tableTestArray.content;
  243 + };
  244 + defineExpose({
  245 + getValue,
  246 + setTableArray,
  247 + });
  248 +</script>
  249 +
  250 +<style scoped lang="less">
  251 + @table-color: #e5e7eb;
  252 +
  253 + .table-border-color {
  254 + border: 1px solid #e5e7eb;
  255 + text-align: center;
  256 + }
  257 +
  258 + .table-content {
  259 + table {
  260 + &:extend(.table-border-color);
  261 + }
  262 +
  263 + table td {
  264 + width: 7vw;
  265 + padding: 5px;
  266 + white-space: nowrap;
  267 + &:extend(.table-border-color);
  268 + }
  269 +
  270 + table th {
  271 + padding: 5px;
  272 + &:extend(.table-border-color);
  273 + }
  274 + }
  275 +</style>
  1 +import TestRequest from './index.vue';
  2 +
  3 +export { TestRequest };
  1 +<template>
  2 + <div>
  3 + <div class="mt-8">
  4 + <div>
  5 + <Button @click="handleTest" type="primary"> 测试接口 </Button>
  6 + </div>
  7 + <div v-if="showTestEditCell" class="mt-8">
  8 + <a-row type="flex" justify="center">
  9 + <a-col :span="3"> 参数设置 </a-col>
  10 + <a-col :span="21">
  11 + <TestEditCellTable ref="testEditCellTableRef" :method="method" />
  12 + </a-col>
  13 + </a-row>
  14 + </div>
  15 + </div>
  16 + <div v-if="showTestEditCell" class="mt-8">
  17 + <div>
  18 + <Button @click="handleExcute" type="primary"> 执行 </Button>
  19 + </div>
  20 + <div class="mt-8">
  21 + <a-row type="flex" justify="center">
  22 + <a-col :span="3"> 测试结果 </a-col>
  23 + <a-col :span="21">
  24 + <a-textarea
  25 + allow-clear
  26 + show-count
  27 + v-model:value="testResult"
  28 + placeholder="测试结果为:"
  29 + :rows="8"
  30 + />
  31 + </a-col>
  32 + </a-row>
  33 + </div>
  34 + </div>
  35 + </div>
  36 +</template>
  37 +<script lang="ts" setup name="testRequest">
  38 + import { ref, nextTick } from 'vue';
  39 + import { Button } from 'ant-design-vue';
  40 + import { TestEditCellTable } from '../TestEditCellTable/index';
  41 + import { getAllDeviceByOrg } from '/@/api/dataBoard';
  42 + import { getDeviceAttributes } from '/@/api/dataBoard';
  43 +
  44 + const emits = defineEmits(['testInterface']);
  45 +
  46 + const props = defineProps({
  47 + method: {
  48 + type: String,
  49 + },
  50 + data: {
  51 + type: Array,
  52 + },
  53 + });
  54 +
  55 + const showTestEditCell = ref(false);
  56 +
  57 + const testEditCellTableRef = ref<InstanceType<typeof TestEditCellTable>>();
  58 +
  59 + const testResult = ref('');
  60 +
  61 + // const testNeedKeys = ['organizationId', 'deviceProfileId', 'entityId', 'keys', 'date'];
  62 +
  63 + const handleTest = () => {
  64 + showTestEditCell.value = true;
  65 + emits('testInterface');
  66 + getValue();
  67 + };
  68 +
  69 + const getValue = async () => {
  70 + await nextTick();
  71 + await nextTick(() => {
  72 + testEditCellTableRef.value?.setTableArray(props.data);
  73 + });
  74 + };
  75 +
  76 + const handleExcute = async () => {
  77 + const params = testEditCellTableRef.value?.getValue();
  78 + const keys = params?.map((m) => ({ key: m.key, value: m.value }));
  79 + keys?.forEach(async (it: any) => {
  80 + if (it.key === 'organizationId') {
  81 + //获取设备
  82 + const data = await getAllDeviceByOrg(it.value!);
  83 + testResult.value = JSON.stringify(data);
  84 + }
  85 + if (it.key === 'deviceProfileId') {
  86 + //获取属性
  87 + const data = await getDeviceAttributes({ deviceProfileId: it.value! });
  88 + testResult.value = JSON.stringify(data);
  89 + }
  90 + });
  91 + };
  92 +
  93 + //设置数据
  94 + const setValue = () => {
  95 + showTestEditCell.value = false;
  96 + testResult.value = '';
  97 + };
  98 + defineExpose({
  99 + setValue,
  100 + handleTest,
  101 + });
  102 +</script>
  103 +
  104 +<style scoped lang="less"></style>
  1 +import TestSql from './index.vue';
  2 +
  3 +export { TestSql };
  1 +<template>
  2 + <div class="mt-8">
  3 + <div>
  4 + <Button @click="handleExcute" type="primary"> 测试SQL </Button>
  5 + </div>
  6 + </div>
  7 + <div v-if="testResultStatus" class="mt-8">
  8 + <div class="mt-8">
  9 + <a-row type="flex" justify="center">
  10 + <a-col :span="3"> 测试结果 </a-col>
  11 + <a-col :span="21">
  12 + <a-textarea
  13 + allow-clear
  14 + show-count
  15 + v-model:value="testResult"
  16 + placeholder="测试结果为:"
  17 + :rows="8"
  18 + />
  19 + </a-col>
  20 + </a-row>
  21 + </div>
  22 + </div>
  23 +</template>
  24 +<script lang="ts" setup name="testRequest">
  25 + import { ref } from 'vue';
  26 + import { Button } from 'ant-design-vue';
  27 +
  28 + defineProps({
  29 + method: {
  30 + type: String,
  31 + },
  32 + });
  33 +
  34 + const testResult = ref('');
  35 +
  36 + const testResultStatus = ref(false);
  37 +
  38 + const handleExcute = async () => {
  39 + testResultStatus.value = true;
  40 + testResult.value = '测试结果为:1234';
  41 + };
  42 +
  43 + const resetValue = () => (testResult.value = '');
  44 +
  45 + defineExpose({
  46 + resetValue,
  47 + });
  48 +</script>
  49 +
  50 +<style scoped lang="less"></style>
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import { h } from 'vue';
  3 +import { Tag } from 'ant-design-vue';
  4 +import { findDictItemByCode } from '/@/api/system/dict';
  5 +
  6 +// 表格配置
  7 +export const columns: BasicColumn[] = [
  8 + {
  9 + title: '接口名称',
  10 + dataIndex: 'interfaceName',
  11 + width: 150,
  12 + },
  13 + {
  14 + title: '请求方式',
  15 + dataIndex: 'requestContentType',
  16 + width: 90,
  17 + format(text) {
  18 + return Number(text) === 0 ? '普通请求' : Number(text) === 1 ? 'SQL请求' : 'websocket请求';
  19 + },
  20 + },
  21 + {
  22 + title: '接口内容',
  23 + dataIndex: 'content',
  24 + width: 80,
  25 + slots: { customRender: 'content' },
  26 + },
  27 + {
  28 + title: '状态',
  29 + dataIndex: 'state',
  30 + width: 80,
  31 + customRender: ({ record }) => {
  32 + const status = record.state;
  33 + const color = status == 1 ? 'green' : 'red';
  34 + const text = status == 1 ? '发布' : '未发布';
  35 + return h(Tag, { color: color }, () => text);
  36 + },
  37 + },
  38 +];
  39 +
  40 +// 查询配置
  41 +export const searchFormSchema: FormSchema[] = [
  42 + {
  43 + field: 'name',
  44 + label: '接口名称',
  45 + component: 'Input',
  46 + colProps: { span: 8 },
  47 + componentProps: {
  48 + maxLength: 36,
  49 + placeholder: '请输入接口名称',
  50 + },
  51 + },
  52 + {
  53 + field: 'state',
  54 + label: '发布状态',
  55 + component: 'Select',
  56 + colProps: { span: 8 },
  57 + componentProps: {
  58 + options: [
  59 + {
  60 + label: '发布',
  61 + value: 1,
  62 + },
  63 + {
  64 + label: '未发布',
  65 + value: 0,
  66 + },
  67 + ],
  68 + placeholder: '请选择发布状态',
  69 + },
  70 + },
  71 +];
  72 +
  73 +//表单配置
  74 +export const schemas: FormSchema[] = [
  75 + {
  76 + field: 'interfaceName',
  77 + label: '接口名称',
  78 + colProps: { span: 24 },
  79 + required: true,
  80 + component: 'Input',
  81 + componentProps: {
  82 + maxLength: 64,
  83 + placeholder: '请输入接口名称',
  84 + },
  85 + },
  86 + {
  87 + field: 'requestContentType',
  88 + label: '请求方式',
  89 + component: 'ApiSelect',
  90 + required: true,
  91 + colProps: { span: 24 },
  92 + defaultValue: '0',
  93 + componentProps: ({ formActionType }) => {
  94 + const { setFieldsValue, updateSchema } = formActionType;
  95 + return {
  96 + api: findDictItemByCode,
  97 + params: {
  98 + dictCode: 'dataview_select_methods',
  99 + },
  100 + placeholder: '请选择请求方式',
  101 + labelField: 'itemText',
  102 + valueField: 'itemValue',
  103 + getPopupContainer: () => document.body,
  104 + async onChange(e) {
  105 + setFieldsValue({ requestHttpType: e === '1' ? 'POST' : 'GET' });
  106 + const res = await findDictItemByCode({
  107 + dictCode: e === '1' ? 'dataview_select_sql_request' : 'dataview_select_request',
  108 + });
  109 + const options = res.map((m) => ({ label: m.itemText, value: m.itemValue }));
  110 + updateSchema({
  111 + field: 'requestHttpType',
  112 + componentProps: {
  113 + options,
  114 + },
  115 + });
  116 + },
  117 + };
  118 + },
  119 + },
  120 + {
  121 + field: 'requestOriginUrl',
  122 + label: '源地址',
  123 + colProps: { span: 24 },
  124 + required: true,
  125 + component: 'Input',
  126 + componentProps: {
  127 + maxLength: 64,
  128 + placeholder: '请输入源地址',
  129 + },
  130 + },
  131 + {
  132 + field: 'requestHttpType',
  133 + label: '请求类型',
  134 + component: 'ApiSelect',
  135 + required: true,
  136 + colProps: { span: 6 },
  137 + defaultValue: 'GET',
  138 + componentProps: {
  139 + placeholder: '请选择请求类型',
  140 + api: findDictItemByCode,
  141 + params: {
  142 + dictCode: 'dataview_select_request',
  143 + },
  144 + labelField: 'itemText',
  145 + valueField: 'itemValue',
  146 + },
  147 + ifShow: ({ values }) =>
  148 + values['requestContentType'] === '0' || values['requestContentType'] === '1',
  149 + },
  150 + {
  151 + field: 'requestUrl',
  152 + label: '',
  153 + component: 'Input',
  154 + required: true,
  155 + colProps: { span: 18 },
  156 + componentProps: {
  157 + maxLength: 64,
  158 + placeholder: '请输入接口地址',
  159 + },
  160 + ifShow: ({ values }) =>
  161 + values['requestContentType'] === '0' || values['requestContentType'] === '1',
  162 + },
  163 + {
  164 + field: 'requestSQLKey',
  165 + label: '键名',
  166 + colProps: { span: 12 },
  167 + component: 'Input',
  168 + defaultValue: 'sql',
  169 + componentProps: {
  170 + disabled: true,
  171 + },
  172 + ifShow: ({ values }) => values['requestContentType'] === '1',
  173 + },
  174 + {
  175 + field: 'requestSQLContent',
  176 + label: '键值',
  177 + colProps: { span: 24 },
  178 + component: 'InputTextArea',
  179 + defaultValue: 'select * from where',
  180 + componentProps: {
  181 + maxLength: 255,
  182 + placeholder: '请输入键值',
  183 + rows: 6,
  184 + },
  185 + ifShow: ({ values }) => values['requestContentType'] === '1',
  186 + },
  187 + {
  188 + field: 'slot',
  189 + label: '参数设置',
  190 + component: 'Input',
  191 + slot: 'selectMethods',
  192 + colProps: { span: 24 },
  193 + ifShow: ({ values }) => values['requestContentType'] !== '1',
  194 + },
  195 + {
  196 + field: 'testSlot',
  197 + label: '',
  198 + component: 'Input',
  199 + slot: 'testSql',
  200 + colProps: { span: 24 },
  201 + ifShow: ({ values }) => values['requestContentType'] === '1',
  202 + },
  203 +];
  1 +/**
  2 + * @description: 请求参数类型
  3 + */
  4 +export enum RequestParamsTypeEnum {
  5 + PARAMS = 'Params',
  6 + BODY = 'Body',
  7 + HEADER = 'Header',
  8 +}
  9 +
  10 +/**
  11 + * @description: 请求体类型
  12 + */
  13 +export enum RequestBodyTypeEnum {
  14 + NONE = 'none',
  15 + FORMDATA = 'form-data',
  16 + XWWW = 'x-www-form-urlencoded',
  17 + JSON = 'json',
  18 + XML = 'xml',
  19 +}
  1 +<template>
  2 + <div>
  3 + <BasicDrawer
  4 + showFooter
  5 + v-bind="$attrs"
  6 + @register="registerDrawer"
  7 + width="45%"
  8 + @ok="handleOnSubmit"
  9 + >
  10 + <BasicForm @register="registerForm">
  11 + <template #selectMethods="{ model }">
  12 + <SimpleRequest
  13 + ref="simpleRequestRef"
  14 + v-if="model['requestContentType'] === '0'"
  15 + @activeKey="handleActiveKey"
  16 + :paramsType="model['requestHttpType']"
  17 + :method="model['requestContentType']"
  18 + />
  19 + <SimpleRequest
  20 + ref="simpleRequestRef"
  21 + @activeKey="handleActiveKey"
  22 + v-else-if="model['requestContentType'] === '2'"
  23 + :paramsType="model['requestHttpType']"
  24 + :method="model['requestContentType']"
  25 + />
  26 + </template>
  27 + <template #testSql="{ model }">
  28 + <div style="margin: auto 7.5rem">
  29 + <TestSql
  30 + ref="testSqlRef"
  31 + v-if="model['requestContentType'] === '1'"
  32 + :method="model['requestContentType']"
  33 + />
  34 + </div>
  35 + </template>
  36 + </BasicForm>
  37 + </BasicDrawer>
  38 + </div>
  39 +</template>
  40 +<script lang="ts" setup name="publicApi">
  41 + import { ref, nextTick } from 'vue';
  42 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  43 + import { BasicForm, useForm } from '/@/components/Form';
  44 + import { schemas } from './config';
  45 + import SimpleRequest from './components/SimpleRequest/index.vue';
  46 + import { TestSql } from './components/TestSql/index';
  47 + import {
  48 + saveDataViewInterface,
  49 + updateDataViewInterface,
  50 + } from '/@/api/bigscreen/center/bigscreenCenter';
  51 + import { useMessage } from '/@/hooks/web/useMessage';
  52 + import { findDictItemByCode } from '/@/api/system/dict';
  53 +
  54 + const emits = defineEmits(['success', 'register']);
  55 +
  56 + const { createMessage } = useMessage();
  57 +
  58 + const isUpdate = ref(false);
  59 +
  60 + const putId = ref('');
  61 +
  62 + const activeKey = ref('');
  63 +
  64 + const simpleRequestRef = ref<InstanceType<typeof SimpleRequest>>();
  65 +
  66 + const testSqlRef = ref<InstanceType<typeof TestSql>>();
  67 +
  68 + const [registerForm, { resetFields, validate, setFieldsValue, updateSchema }] = useForm({
  69 + labelWidth: 120,
  70 + schemas,
  71 + showActionButtonGroup: false,
  72 + });
  73 +
  74 + const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
  75 + setDrawerProps({ loading: true });
  76 + await nextTick();
  77 + await resetFields();
  78 + await nextTick(() => simpleRequestRef.value?.resetValue());
  79 + await nextTick(() => testSqlRef.value?.resetValue());
  80 + const title = `${!data.isUpdate ? '新增' : '修改'}公共接口`;
  81 + setDrawerProps({ title });
  82 + isUpdate.value = data.isUpdate;
  83 + !isUpdate.value ? (putId.value = '') : (putId.value = data.record.id);
  84 + if (isUpdate.value) {
  85 + await setFieldsValue({
  86 + ...data.record,
  87 + requestContentType: String(data.record?.requestContentType),
  88 + requestSQLContent: JSON.parse(data.record?.requestParams)?.requestSQLContent?.sql,
  89 + });
  90 + await nextTick(() =>
  91 + setTimeout(() => {
  92 + simpleRequestRef.value?.setValue(data.record);
  93 + }, 200)
  94 + );
  95 + const res = await findDictItemByCode({
  96 + dictCode:
  97 + data.record?.requestContentType === 1
  98 + ? 'dataview_select_sql_request'
  99 + : 'dataview_select_request',
  100 + });
  101 + const options = res.map((m) => ({ label: m.itemText, value: m.itemValue }));
  102 + updateSchema({
  103 + field: 'requestHttpType',
  104 + componentProps: {
  105 + options,
  106 + },
  107 + });
  108 + }
  109 + setDrawerProps({ loading: false });
  110 + });
  111 +
  112 + const handleActiveKey = (v) => (activeKey.value = v);
  113 +
  114 + const handleOnSubmit = async () => {
  115 + setDrawerProps({ loading: true });
  116 + try {
  117 + const values = await validate();
  118 + if (!values) return;
  119 + const Objects = simpleRequestRef.value?.getValue(true);
  120 + const data = {
  121 + ...values,
  122 + id: !putId.value ? null : putId.value,
  123 + requestParams: JSON.stringify({
  124 + requestSQLContent: {
  125 + sql: values?.requestSQLContent,
  126 + },
  127 + type: activeKey.value,
  128 + Params: activeKey.value === 'Params' ? Objects : {},
  129 + Body: activeKey.value === 'Body' ? Objects : {},
  130 + Header: activeKey.value === 'Header' ? Objects : {},
  131 + }),
  132 + };
  133 + !putId.value ? await saveDataViewInterface(data) : await updateDataViewInterface(data);
  134 + emits('success');
  135 + closeDrawer();
  136 + createMessage.success(`${!isUpdate.value ? '新增' : '修改'}成功`);
  137 + } finally {
  138 + setDrawerProps({ loading: false });
  139 + }
  140 + };
  141 +</script>
  1 +import { ref } from 'vue';
  2 +import { deviceProfile } from '/@/api/device/deviceManager';
  3 +import { getOrganizationList } from '/@/api/system/system';
  4 +// import { copyTransFun } from '/@/utils/fnUtils';
  5 +// import { getAllDeviceByOrg } from '/@/api/dataBoard';
  6 +// import { getDeviceAttributes } from '/@/api/dataBoard';
  7 +import { copyTransFun } from '/@/utils/fnUtils';
  8 +
  9 +export const useApi = async (key) => {
  10 + const options = ref<{ label: string; value: string; disabled?: boolean }[]>([]);
  11 + switch (key) {
  12 + case 'deviceProfileId':
  13 + const res = await deviceProfile();
  14 + options.value = res?.map((m) => ({ label: m.name, value: m.id }));
  15 + break;
  16 + case 'organizationId':
  17 + const data = await getOrganizationList();
  18 + copyTransFun(data as never as any);
  19 + options.value = data as any;
  20 + }
  21 +
  22 + return { options: options.value };
  23 +};
  1 +import PublicApiDrawer from './index.vue';
  2 +import PublicApiList from './list.vue';
  3 +import PublicApiForm from './form.vue';
  4 +
  5 +export { PublicApiDrawer, PublicApiList, PublicApiForm };
  1 +<template>
  2 + <div>
  3 + <BasicDrawer v-bind="$attrs" @register="registerDrawer" title="公共接口管理页" width="50%">
  4 + <PublicApiList />
  5 + </BasicDrawer>
  6 + </div>
  7 +</template>
  8 +<script lang="ts" setup name="publicApi">
  9 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  10 + import { PublicApiList } from './index';
  11 +
  12 + const [registerDrawer] = useDrawerInner();
  13 +</script>
  1 +<template>
  2 + <div>
  3 + <BasicTable @register="registerTable">
  4 + <template #content="{ record }">
  5 + <a-button type="link" class="ml-2" @click="handleRecordContent(record)"> 查看 </a-button>
  6 + </template>
  7 + <template #toolbar>
  8 + <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增公共接口 </a-button>
  9 + <Popconfirm
  10 + title="您确定要批量删除数据"
  11 + ok-text="确定"
  12 + cancel-text="取消"
  13 + @confirm="handleDeleteOrBatchDelete(null)"
  14 + >
  15 + <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
  16 + </Popconfirm>
  17 + </template>
  18 + <template #action="{ record }">
  19 + <TableAction
  20 + :actions="[
  21 + {
  22 + label: '发布',
  23 + icon: 'ant-design:node-expand-outlined',
  24 + onClick: handlePublish.bind(null, record),
  25 + ifShow: () => {
  26 + return record.state === 0;
  27 + },
  28 + },
  29 + {
  30 + label: '取消发布',
  31 + icon: 'ant-design:node-collapse-outlined',
  32 + onClick: handleCancelPublish.bind(null, record),
  33 + ifShow: () => {
  34 + return record.state === 1;
  35 + },
  36 + },
  37 + {
  38 + label: '修改',
  39 + icon: 'clarity:note-edit-line',
  40 + onClick: handleCreateOrEdit.bind(null, record),
  41 + ifShow: () => {
  42 + return record.state === 0;
  43 + },
  44 + },
  45 + {
  46 + label: '删除',
  47 + icon: 'ant-design:delete-outlined',
  48 + color: 'error',
  49 + ifShow: () => {
  50 + return record.state === 0;
  51 + },
  52 + popConfirm: {
  53 + title: '是否确认删除',
  54 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  55 + },
  56 + },
  57 + ]"
  58 + />
  59 + </template>
  60 + </BasicTable>
  61 + </div>
  62 + <PublicApiForm @register="registerDrawer" @success="handleSuccess" />
  63 +</template>
  64 +<script lang="ts" setup name="list">
  65 + import { nextTick, h } from 'vue';
  66 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  67 + import { useDrawer } from '/@/components/Drawer';
  68 + import { columns, searchFormSchema } from './config';
  69 + import { PublicApiForm } from './index';
  70 + import {
  71 + getDataViewInterfacePage,
  72 + deleteBigViewInterface,
  73 + getPublish,
  74 + getCancelPublish,
  75 + } from '/@/api/bigscreen/center/bigscreenCenter';
  76 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  77 + import { Popconfirm, Modal } from 'ant-design-vue';
  78 + import { JsonPreview } from '/@/components/CodeEditor';
  79 + import { useMessage } from '/@/hooks/web/useMessage';
  80 +
  81 + const [registerTable, { reload, setProps }] = useTable({
  82 + api: getDataViewInterfacePage,
  83 + columns,
  84 + showIndexColumn: false,
  85 + clickToRowSelect: false,
  86 + showTableSetting: true,
  87 + bordered: true,
  88 + formConfig: {
  89 + labelWidth: 120,
  90 + schemas: searchFormSchema,
  91 + },
  92 + useSearchForm: true,
  93 + actionColumn: {
  94 + width: 150,
  95 + title: '操作',
  96 + dataIndex: 'action',
  97 + slots: { customRender: 'action' },
  98 + fixed: 'right',
  99 + },
  100 + });
  101 +
  102 + const [registerDrawer, { openDrawer }] = useDrawer();
  103 +
  104 + const { createMessage } = useMessage();
  105 +
  106 + const handleSuccess = () => reload();
  107 +
  108 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
  109 + deleteBigViewInterface,
  110 + handleSuccess,
  111 + setProps
  112 + );
  113 +
  114 + nextTick(() => {
  115 + setProps(selectionOptions);
  116 + });
  117 +
  118 + const handleCreateOrEdit = (record) => {
  119 + const isUpdate = record === null ? false : true;
  120 + const recordContent = record === null ? null : record;
  121 + openDrawer(true, {
  122 + isUpdate,
  123 + record: recordContent,
  124 + });
  125 + };
  126 +
  127 + const handleRecordContent = (record) => {
  128 + Modal.info({
  129 + title: '接口内容',
  130 + width: 600,
  131 + content: h(JsonPreview, { data: JSON.parse(record.requestParams) }),
  132 + });
  133 + };
  134 +
  135 + const handlePublish = async (record) => {
  136 + await getPublish(record.id);
  137 + createMessage.success(`发布成功`);
  138 + handleSuccess();
  139 + };
  140 +
  141 + const handleCancelPublish = async (record) => {
  142 + await getCancelPublish(record.id);
  143 + createMessage.success(`取消发布成功`);
  144 + handleSuccess();
  145 + };
  146 +</script>