Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
0890350cf2
|
@ -7,24 +7,29 @@ export {}
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AAlert: typeof import('ant-design-vue/es')['Alert']
|
||||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||||
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
|
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
|
||||||
ACol: typeof import('ant-design-vue/es')['Col']
|
ACol: typeof import('ant-design-vue/es')['Col']
|
||||||
|
ACollapse: typeof import('ant-design-vue/es')['Collapse']
|
||||||
|
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
|
||||||
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||||
AEmpty: typeof import('ant-design-vue/es')['Empty']
|
AEmpty: typeof import('ant-design-vue/es')['Empty']
|
||||||
AForm: typeof import('ant-design-vue/es')['Form']
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
AInput: typeof import('ant-design-vue/es')['Input']
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
|
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
ARow: typeof import('ant-design-vue/es')['Row']
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||||
ATable: typeof import('ant-design-vue/es')['Table']
|
ATable: typeof import('ant-design-vue/es')['Table']
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,6 @@
|
||||||
import { get } from '@/utils/request'
|
import { get, post } from '@/utils/request'
|
||||||
|
|
||||||
// 三方应用账户信息
|
// 三方应用账户信息
|
||||||
export const applicationInfo = (code: string) => get(`/application/sso/bind-code/${code}`)
|
export const applicationInfo = (code: string): any => get(`/application/sso/bind-code/${code}`)
|
||||||
|
// 立即绑定
|
||||||
|
export const bindAccount = (code: string): any => post(`/application/sso/me/bind/${code}`)
|
|
@ -0,0 +1,25 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
// 更新全部菜单
|
||||||
|
export const updateMenus = (data: any) => server
|
||||||
|
// 添加角色
|
||||||
|
export const addRole = (data: any) => server.post(`/role`)
|
||||||
|
|
||||||
|
//更新权限菜单
|
||||||
|
export const getRoleMenu = (id: string) => server.get(`/menu/role/${id}/_grant/tree`)
|
||||||
|
|
||||||
|
//更新权限菜单
|
||||||
|
export const updateRoleMenu = (id: string, data: any) => server.put(`/menu/role/${id}/_grant`)
|
||||||
|
|
||||||
|
// 记录初始化
|
||||||
|
export const saveInit = () => server.post(`/user/settings/init`,{ init: true },)
|
||||||
|
|
||||||
|
//获取初始化
|
||||||
|
export const getInit = () => server.get(`/user/settings/init`)
|
||||||
|
|
||||||
|
// 获取当前系统权限信息
|
||||||
|
|
||||||
|
export const getSystemPermission = () =>server.get(`/system/resources/permission`)
|
||||||
|
|
||||||
|
// 保存基础信息
|
||||||
|
export const save = (data?: any) => server.post('/system/config/scope/_save')
|
|
@ -0,0 +1,33 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
export const getProviders = () => server.get(`/gateway/device/providers`);
|
||||||
|
|
||||||
|
export const detail = (id) => server.get(`/gateway/device/${id}`);
|
||||||
|
|
||||||
|
export const getNetworkList = (networkType, data, params) =>
|
||||||
|
server.get(
|
||||||
|
`/network/config/${networkType}/_alive?include=${params.include}`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getProtocolList = (transport, params) =>
|
||||||
|
server.get(`/protocol/supports/${transport ? transport : ''}`, params);
|
||||||
|
|
||||||
|
export const getConfigView = (id, transport) =>
|
||||||
|
server.get(`/protocol/${id}/transport/${transport}`);
|
||||||
|
|
||||||
|
export const getChildConfigView = (id) =>
|
||||||
|
server.get(`/protocol/${id}/transports`);
|
||||||
|
|
||||||
|
export const save = (data) => server.post(`/gateway/device`, data);
|
||||||
|
|
||||||
|
export const update = (data) => server.patch(`/gateway/device`, data);
|
||||||
|
|
||||||
|
export const list = (data) =>
|
||||||
|
server.post(`/gateway/device/detail/_query`, data);
|
||||||
|
|
||||||
|
export const undeploy = (id) => server.post(`/gateway/device/${id}/_shutdown`);
|
||||||
|
|
||||||
|
export const deploy = (id) => server.post(`/gateway/device/${id}/_startup`);
|
||||||
|
|
||||||
|
export const del = (id) => server.remove(`/gateway/device/${id}`);
|
|
@ -2,17 +2,10 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div
|
<div
|
||||||
class="card-warp"
|
class="card-warp"
|
||||||
:class="{
|
:class="{ active: active ? 'active' : ''}"
|
||||||
hover: maskShow ? 'hover' : '',
|
|
||||||
active: actived ? 'active' : '',
|
|
||||||
}"
|
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<div
|
<div class="card-content">
|
||||||
class="card-content"
|
|
||||||
@mouseenter="setMaskShow(true)"
|
|
||||||
@mouseleave="setMaskShow(false)"
|
|
||||||
>
|
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="6">
|
<a-col :span="6">
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
|
@ -27,7 +20,7 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- 勾选 -->
|
<!-- 勾选 -->
|
||||||
<div v-if="actived" class="checked-icon">
|
<div v-if="active" class="checked-icon">
|
||||||
<div>
|
<div>
|
||||||
<CheckOutlined />
|
<CheckOutlined />
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,21 +40,12 @@
|
||||||
></BadgeStatus>
|
></BadgeStatus>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 遮罩层 -->
|
|
||||||
<div
|
|
||||||
v-if="showMask"
|
|
||||||
class="card-mask"
|
|
||||||
:class="maskShow ? 'show' : ''"
|
|
||||||
>
|
|
||||||
<slot name="mask"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 按钮 -->
|
<!-- 按钮 -->
|
||||||
<slot name="bottom-tool">
|
<slot name="bottom-tool">
|
||||||
<div v-if="showTool" class="card-tools">
|
<div v-if="showTool && actions && actions.length" class="card-tools">
|
||||||
<div
|
<div
|
||||||
v-for="item in actions"
|
v-for="item in actions"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
|
@ -70,18 +54,32 @@
|
||||||
delete: item.key === 'delete',
|
delete: item.key === 'delete',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-tooltip v-if="item.disabled === true">
|
<a-popconfirm v-if="item.popConfirm" v-bind="item.popConfirm">
|
||||||
<template #title>{{ item.message }}</template>
|
<template v-if="item.key === 'delete'">
|
||||||
<a-button :disabled="item.disabled">
|
<a-button :disabled="item.disabled">
|
||||||
<template #icon><SearchOutlined /></template>
|
<DeleteOutlined />
|
||||||
<span>{{ item.label }}</span>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</template>
|
||||||
|
<template v-else>
|
||||||
<a-button v-else :disabled="item.disabled">
|
<a-button :disabled="item.disabled">
|
||||||
<template #icon><SearchOutlined /></template>
|
<AIcon :type="item.icon" />
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-popconfirm>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="item.key === 'delete'">
|
||||||
|
<a-button :disabled="item.disabled">
|
||||||
|
<DeleteOutlined />
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-button :disabled="item.disabled">
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item.text }}</span>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
|
@ -89,18 +87,26 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SearchOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
import { SearchOutlined, CheckOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||||
import { StatusColorEnum } from '@/utils/consts.ts';
|
import { StatusColorEnum } from '@/utils/consts.ts';
|
||||||
|
import type { ActionsType } from '@/components/Table/index.vue'
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
type EmitProps = {
|
type EmitProps = {
|
||||||
(e: 'update:modelvalue', data: string | number): void;
|
// (e: 'update:modelValue', data: Record<string, any>): void;
|
||||||
(e: 'actived', data: boolean): void;
|
(e: 'click', data: Record<string, any>): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TableActionsType = Partial<ActionsType>
|
||||||
|
|
||||||
const emit = defineEmits<EmitProps>();
|
const emit = defineEmits<EmitProps>();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<Record<string, any>>,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
showStatus: {
|
showStatus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -109,10 +115,7 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
showMask: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
statusText: {
|
statusText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '正常',
|
default: '正常',
|
||||||
|
@ -125,21 +128,17 @@ const props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
type: Array as any,
|
type: Array as PropType<TableActionsType[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const maskShow = ref(false);
|
|
||||||
const actived = ref(false);
|
|
||||||
|
|
||||||
const setMaskShow = (val: boolean) => {
|
|
||||||
maskShow.value = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
actived.value = !actived.value;
|
emit('click', props.value);
|
||||||
emit('actived', actived.value);
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="jtable-content">
|
<div class="jtable-content">
|
||||||
<!-- <div class="jtable-alert">
|
<div class="jtable-alert" v-if="rowSelection.selectedRowKeys && rowSelection.selectedRowKeys.length">
|
||||||
<a-alert message="Info Text" type="info" />
|
<a-alert :message="'已选择' + rowSelection.selectedRowKeys.length + '项'" type="info" :afterClose="handleAlertClose">
|
||||||
</div> -->
|
<template #closeText>
|
||||||
|
<a>取消选择</a>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</div>
|
||||||
<div v-if="_model === ModelEnum.CARD" class="jtable-card">
|
<div v-if="_model === ModelEnum.CARD" class="jtable-card">
|
||||||
<div
|
<div
|
||||||
v-if="_dataSource.length"
|
v-if="_dataSource.length"
|
||||||
|
@ -29,16 +33,7 @@
|
||||||
v-for="(item, index) in _dataSource"
|
v-for="(item, index) in _dataSource"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<CardBox :actions="actions" v-bind="cardProps">
|
<slot name="card" v-bind="item" :index="index"></slot>
|
||||||
<template #img>
|
|
||||||
<slot name="img">
|
|
||||||
<img :src="getImage('/device-product.png')" />
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<slot name="cardContent" :item="item" :index="index"></slot>
|
|
||||||
</template>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -46,23 +41,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<a-table :rowSelection="rowSelection" :columns="[..._columns]" :dataSource="_dataSource" :pagination="false" :scroll="{ x: 1366 }">
|
<a-table rowKey="id" :rowSelection="rowSelection" :columns="[..._columns]" :dataSource="_dataSource" :pagination="false" :scroll="{ x: 1366 }">
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'action'">
|
<!-- <template v-if="column.key === 'action'">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-tooltip v-for="i in actions" :key="i.key" v-bind="i.tooltip">
|
<a-tooltip v-for="i in actions" :key="i.key" v-bind="i.tooltip">
|
||||||
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm">
|
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm">
|
||||||
<a>
|
<a><AIcon :type="i.icon" /></a>
|
||||||
{{i.text}}
|
|
||||||
</a>
|
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<a v-else @click="i.onClick && i.onClick(record)">
|
<a v-else @click="i.onClick && i.onClick(record)">
|
||||||
{{i.text}}
|
<AIcon :type="i.icon" />
|
||||||
</a>
|
</a>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template> -->
|
||||||
<template v-else-if="column.scopedSlots">
|
<template v-if="column.scopedSlots">
|
||||||
<slot :name="column.key" :row="record"></slot>
|
<slot :name="column.key" :row="record"></slot>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@ -88,12 +81,11 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
||||||
import type { TableProps } from 'ant-design-vue/es/table'
|
import type { TableProps, ColumnsType } from 'ant-design-vue/es/table'
|
||||||
import type { TooltipProps } from 'ant-design-vue/es/tooltip'
|
import type { TooltipProps } from 'ant-design-vue/es/tooltip'
|
||||||
import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm'
|
import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm'
|
||||||
import { Empty } from 'ant-design-vue'
|
import { Empty } from 'ant-design-vue'
|
||||||
import { CSSProperties } from 'vue';
|
import { CSSProperties } from 'vue';
|
||||||
import { getImage } from '@/utils/comm';
|
|
||||||
|
|
||||||
enum ModelEnum {
|
enum ModelEnum {
|
||||||
TABLE = 'TABLE',
|
TABLE = 'TABLE',
|
||||||
|
@ -123,13 +115,17 @@ export interface ActionsType {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JTableProps extends TableProps{
|
export interface JColumnsProps extends ColumnsType{
|
||||||
|
scopedSlots?: boolean; // 是否为插槽 true: 是 false: 否
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JTableProps extends TableProps{
|
||||||
request?: (params: Record<string, any> & {
|
request?: (params: Record<string, any> & {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
}) => Promise<Partial<RequestData>>;
|
}) => Promise<Partial<RequestData>>;
|
||||||
cardBodyClass?: string;
|
cardBodyClass?: string;
|
||||||
columns: Record<string, any>[];
|
columns: JColumnsProps;
|
||||||
params?: Record<string, any> & {
|
params?: Record<string, any> & {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
|
@ -147,6 +143,11 @@ const props = withDefaults(defineProps<JTableProps>(), {
|
||||||
request: undefined,
|
request: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// emit
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'cancelSelect'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
|
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
|
||||||
|
|
||||||
const _model = ref<keyof typeof ModelEnum>(props.model ? props.model : ModelEnum.CARD); // 模式切换
|
const _model = ref<keyof typeof ModelEnum>(props.model ? props.model : ModelEnum.CARD); // 模式切换
|
||||||
|
@ -155,16 +156,17 @@ const _dataSource = ref<Record<string, any>[]>([])
|
||||||
const pageIndex = ref<number>(0)
|
const pageIndex = ref<number>(0)
|
||||||
const pageSize = ref<number>(6)
|
const pageSize = ref<number>(6)
|
||||||
const total = ref<number>(0)
|
const total = ref<number>(0)
|
||||||
const _columns = ref<Record<string, any>[]>([])
|
const _columns = ref<Record<string, any>[]>([...props.columns])
|
||||||
const loading = ref<boolean>(true)
|
const loading = ref<boolean>(true)
|
||||||
//
|
|
||||||
// const slotColumns = computed(() => props.columns.filter((item) => item.scopedSlots))
|
|
||||||
// 方法
|
// 方法
|
||||||
// 切换卡片和表格
|
// 切换卡片和表格
|
||||||
const modelChange = (type: keyof typeof ModelEnum) => {
|
const modelChange = (type: keyof typeof ModelEnum) => {
|
||||||
_model.value = type
|
_model.value = type
|
||||||
}
|
}
|
||||||
// 请求数据
|
/**
|
||||||
|
* 请求数据
|
||||||
|
*/
|
||||||
const handleSearch = async (_params?: Record<string, any>) => {
|
const handleSearch = async (_params?: Record<string, any>) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
if(props.request) {
|
if(props.request) {
|
||||||
|
@ -194,7 +196,9 @@ const handleSearch = async (_params?: Record<string, any>) => {
|
||||||
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 页码变化
|
||||||
|
*/
|
||||||
const pageChange = (page: number, size: number) => {
|
const pageChange = (page: number, size: number) => {
|
||||||
handleSearch({
|
handleSearch({
|
||||||
...props.params,
|
...props.params,
|
||||||
|
@ -203,22 +207,30 @@ const pageChange = (page: number, size: number) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alert关闭,取消选择
|
||||||
|
const handleAlertClose = () => {
|
||||||
|
emit('cancelSelect')
|
||||||
|
}
|
||||||
|
|
||||||
|
// watchEffect(() => {
|
||||||
|
// if(Array.isArray(props.actions) && props.actions.length) {
|
||||||
|
// _columns.value = [...props.columns,
|
||||||
|
// {
|
||||||
|
// title: '操作',
|
||||||
|
// key: 'action',
|
||||||
|
// fixed: 'right',
|
||||||
|
// width: 250
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// } else {
|
||||||
|
// _columns.value = [...props.columns]
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if(Array.isArray(props.actions) && props.actions.length) {
|
|
||||||
_columns.value = [...props.columns,
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'action',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 250
|
|
||||||
}
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
_columns.value = [...props.columns]
|
|
||||||
}
|
|
||||||
handleSearch(props.params)
|
handleSearch(props.params)
|
||||||
})
|
})
|
||||||
|
// TODO 选择的双向绑定和图标的渲染
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -250,11 +262,13 @@ watchEffect(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.jtable-content {
|
.jtable-content {
|
||||||
|
.jtable-alert {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
.jtable-card {
|
.jtable-card {
|
||||||
.jtable-card-items {
|
.jtable-card-items {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 26px;
|
grid-gap: 26px;
|
||||||
// grid-template-columns: repeat(4, 1fr);
|
|
||||||
.jtable-card-item {
|
.jtable-card-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
.jtable-body {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 24px 24px;
|
|
||||||
background-color: white;
|
|
||||||
.jtable-body-header {
|
|
||||||
padding: 16px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
.jtable-body-header-right {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
.jtable-setting-item {
|
|
||||||
color: rgba(0, 0, 0, 0.75);
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: @primary-color-hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: @primary-color-active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jtable-content {
|
|
||||||
.jtable-card {
|
|
||||||
.jtable-card-items {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 26px;
|
|
||||||
// grid-template-columns: repeat(4, 1fr);
|
|
||||||
.jtable-card-item {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jtable-pagination {
|
|
||||||
position: absolute;
|
|
||||||
right: 24px;
|
|
||||||
bottom: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
|
|
||||||
import styles from './index.module.less'
|
|
||||||
import { Pagination, Table, Empty } from 'ant-design-vue'
|
|
||||||
import type { TableProps } from 'ant-design-vue/es/table'
|
|
||||||
|
|
||||||
enum ModelEnum {
|
|
||||||
TABLE = 'TABLE',
|
|
||||||
CARD = 'CARD',
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare type RequestData = {
|
|
||||||
code: string;
|
|
||||||
result: {
|
|
||||||
data: any[] | undefined;
|
|
||||||
pageIndex: number;
|
|
||||||
pageSize: number;
|
|
||||||
total: number;
|
|
||||||
};
|
|
||||||
status: number;
|
|
||||||
} & Record<string, any>;
|
|
||||||
|
|
||||||
interface JTableProps extends TableProps{
|
|
||||||
request: (params: Record<string, any> & {
|
|
||||||
pageSize: number;
|
|
||||||
pageIndex: number;
|
|
||||||
}) => Promise<Partial<RequestData>>;
|
|
||||||
cardBodyClass: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const JTable = defineComponent<JTableProps>({
|
|
||||||
name: 'JTable',
|
|
||||||
slots: [
|
|
||||||
'headerTitle', // 顶部左边插槽
|
|
||||||
'cardRender', // 卡片内容
|
|
||||||
],
|
|
||||||
emits: [
|
|
||||||
'modelChange', // 切换卡片和表格
|
|
||||||
],
|
|
||||||
props: {
|
|
||||||
cardBodyClass: '',
|
|
||||||
request: undefined,
|
|
||||||
columns: []
|
|
||||||
} as any,
|
|
||||||
setup(props ,{ slots, emit }){
|
|
||||||
const model = ref<keyof typeof ModelEnum>(ModelEnum.CARD); // 模式切换
|
|
||||||
const column = ref<number>(3);
|
|
||||||
console.log(props.columns, props.request)
|
|
||||||
const dataSource = ref<any[]>([
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: '胡彦斌',
|
|
||||||
age: 32,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: '胡彦祖1',
|
|
||||||
age: 42,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
name: '胡彦斌',
|
|
||||||
age: 32,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '4',
|
|
||||||
name: '胡彦祖1',
|
|
||||||
age: 42,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '5',
|
|
||||||
name: '胡彦斌',
|
|
||||||
age: 32,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '6',
|
|
||||||
name: '胡彦祖1',
|
|
||||||
age: 42,
|
|
||||||
address: '西湖区湖底公园1号',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 请求数据
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => <div class={styles["jtable-body"]}>
|
|
||||||
<div class={styles["jtable-body-header"]}>
|
|
||||||
<div class={styles["jtable-body-header-left"]}>
|
|
||||||
{/* 顶部左边插槽 */}
|
|
||||||
{slots.headerTitle && slots.headerTitle()}
|
|
||||||
</div>
|
|
||||||
<div class={styles["jtable-body-header-right"]}>
|
|
||||||
{/* <Space> */}
|
|
||||||
<div class={[styles["jtable-setting-item"], ModelEnum.CARD === model.value ? styles['active'] : '']} onClick={() => {
|
|
||||||
model.value = ModelEnum.CARD
|
|
||||||
}}>
|
|
||||||
<AppstoreOutlined />
|
|
||||||
</div>
|
|
||||||
<div class={[styles["jtable-setting-item"], ModelEnum.TABLE === model.value ? styles['active'] : '']} onClick={() => {
|
|
||||||
model.value = ModelEnum.TABLE
|
|
||||||
}}>
|
|
||||||
<UnorderedListOutlined />
|
|
||||||
</div>
|
|
||||||
{/* </Space> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* content */}
|
|
||||||
<div class={styles['jtable-content']}>
|
|
||||||
{
|
|
||||||
model.value === ModelEnum.CARD ?
|
|
||||||
<div class={styles['jtable-card']}>
|
|
||||||
{
|
|
||||||
dataSource.value.length ?
|
|
||||||
<div
|
|
||||||
class={styles['jtable-card-items']}
|
|
||||||
style={{gridTemplateColumns: `repeat(${column.value}, 1fr)`}}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
dataSource.value.map(item => slots.cardRender ?
|
|
||||||
<div class={[styles['jtable-card-item'], props.cardBodyClass]}>{slots.cardRender(item)}</div>
|
|
||||||
: null)
|
|
||||||
}
|
|
||||||
</div> :
|
|
||||||
<div><Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /></div>
|
|
||||||
}
|
|
||||||
</div> :
|
|
||||||
<div>
|
|
||||||
<Table
|
|
||||||
dataSource={dataSource.value}
|
|
||||||
columns={props.columns}
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{/* 分页 */}
|
|
||||||
{
|
|
||||||
dataSource.value.length &&
|
|
||||||
<div class={styles['jtable-pagination']}>
|
|
||||||
<Pagination
|
|
||||||
size="small"
|
|
||||||
total={50}
|
|
||||||
showTotal={(total) => {
|
|
||||||
const min = 1
|
|
||||||
const max = 1
|
|
||||||
return `第 ${min} - ${max} 条/总共 ${total} 条`
|
|
||||||
}}
|
|
||||||
onChange={() => {
|
|
||||||
|
|
||||||
}}
|
|
||||||
onShowSizeChange={() => {
|
|
||||||
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default JTable
|
|
|
@ -63,8 +63,17 @@ export default [
|
||||||
path: '/link/certificate/detail/add',
|
path: '/link/certificate/detail/add',
|
||||||
component: () => import('@/views/link/Certificate/Detail/index.vue')
|
component: () => import('@/views/link/Certificate/Detail/index.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/link/accessConfig',
|
||||||
|
component: () => import('@/views/link/AccessConfig/index.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/link/accessConfig/detail/add',
|
path: '/link/accessConfig/detail/add',
|
||||||
component: () => import('@/views/link/AccessConfig/Detail/index.vue')
|
component: () => import('@/views/link/AccessConfig/Detail/index.vue')
|
||||||
},
|
},
|
||||||
|
// 初始化
|
||||||
|
{
|
||||||
|
path: '/init-home',
|
||||||
|
component: () => import('@/views/init-home/index.vue')
|
||||||
|
},
|
||||||
]
|
]
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="title">第三方账户绑定</div>
|
<div class="title">第三方账户绑定</div>
|
||||||
<!-- 已登录-绑定三方账号 -->
|
<!-- 已登录-绑定三方账号 -->
|
||||||
<template v-if="false">
|
<template v-if="!token">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<a-card style="width: 280px">
|
<a-card style="width: 280px">
|
||||||
<template #title>
|
<template #title>
|
||||||
|
@ -28,14 +28,21 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="info-body">
|
<div class="info-body">
|
||||||
<img :src="getImage('/bind/wechat-webapp.png')" />
|
<img
|
||||||
|
:src="
|
||||||
|
accountInfo?.avatar ||
|
||||||
|
getImage('/bind/wechat-webapp.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
<p>用户名:-</p>
|
<p>用户名:-</p>
|
||||||
<p>名称:微信昵称</p>
|
<p>名称:{{ accountInfo?.name || '-' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn">
|
<div class="btn">
|
||||||
<a-button type="primary">立即绑定</a-button>
|
<a-button type="primary" @click="handleBind"
|
||||||
|
>立即绑定</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- 未登录-绑定三方账号 -->
|
<!-- 未登录-绑定三方账号 -->
|
||||||
|
@ -74,23 +81,25 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
label="验证码"
|
label="验证码"
|
||||||
v-bind="validateInfos.captcha"
|
v-bind="validateInfos.verifyCode"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="formData.captcha"
|
v-model:value="formData.verifyCode"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
>
|
>
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<span style="cursor: pointer">
|
<img
|
||||||
图形验证码
|
:src="captcha.base64"
|
||||||
</span>
|
@click="getCode"
|
||||||
|
style="cursor: pointer"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleSubmit"
|
@click="handleLoginBind"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
登录并绑定账户
|
登录并绑定账户
|
||||||
|
@ -105,32 +114,58 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage, LocalStore } from '@/utils/comm';
|
||||||
|
import { TOKEN_KEY } from '@/utils/variable';
|
||||||
import { Form } from 'ant-design-vue';
|
import { Form } from 'ant-design-vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { applicationInfo } from '@/api/bind';
|
import { applicationInfo, bindAccount } from '@/api/bind';
|
||||||
|
import { code, authLogin } from '@/api/login';
|
||||||
|
|
||||||
const useForm = Form.useForm;
|
const useForm = Form.useForm;
|
||||||
|
|
||||||
interface formData {
|
interface formData {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
captcha: string;
|
verifyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const token = computed(() => LocalStore.get(TOKEN_KEY));
|
||||||
|
|
||||||
|
// 已登录直接绑定
|
||||||
|
const getUrlCode = () => {
|
||||||
|
const url = new URLSearchParams(window.location.href);
|
||||||
|
return url.get('code') as string;
|
||||||
|
};
|
||||||
// 三方应用信息
|
// 三方应用信息
|
||||||
|
const accountInfo = ref({
|
||||||
|
avatar: '',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
const getAppInfo = async () => {
|
const getAppInfo = async () => {
|
||||||
const code: string = '73ab60c88979a1475963a5dde31e374b';
|
const code = getUrlCode();
|
||||||
const res = await applicationInfo(code);
|
const res = await applicationInfo(code);
|
||||||
console.log('getAppInfo: ', res);
|
accountInfo.value = res?.result?.result;
|
||||||
};
|
};
|
||||||
getAppInfo();
|
getAppInfo();
|
||||||
|
|
||||||
// 登录表单
|
/**
|
||||||
|
* 立即绑定
|
||||||
|
*/
|
||||||
|
const handleBind = async () => {
|
||||||
|
const code = getUrlCode();
|
||||||
|
const res = await bindAccount(code);
|
||||||
|
console.log('bindAccount: ', res);
|
||||||
|
message.success('绑定成功');
|
||||||
|
goRedirect();
|
||||||
|
setTimeout(() => window.close(), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 未登录-先登录再绑定
|
||||||
const formData = ref<formData>({
|
const formData = ref<formData>({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
captcha: '',
|
verifyCode: '',
|
||||||
});
|
});
|
||||||
const formRules = ref({
|
const formRules = ref({
|
||||||
username: [
|
username: [
|
||||||
|
@ -145,7 +180,7 @@ const formRules = ref({
|
||||||
message: '请输入密码',
|
message: '请输入密码',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
captcha: [
|
verifyCode: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入验证码',
|
message: '请输入验证码',
|
||||||
|
@ -158,17 +193,40 @@ const { resetFields, validate, validateInfos } = useForm(
|
||||||
formRules.value,
|
formRules.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图形验证码
|
||||||
|
*/
|
||||||
|
const captcha = ref({
|
||||||
|
base64: '',
|
||||||
|
key: '',
|
||||||
|
});
|
||||||
|
const getCode = async () => {
|
||||||
|
const res: any = await code();
|
||||||
|
captcha.value = res.result;
|
||||||
|
};
|
||||||
|
getCode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录并绑定账户
|
* 登录并绑定账户
|
||||||
*/
|
*/
|
||||||
const handleSubmit = () => {
|
const handleLoginBind = () => {
|
||||||
validate()
|
validate()
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
console.log('toRaw:', toRaw(formData.value));
|
const code = getUrlCode();
|
||||||
console.log('formData.value:', formData.value);
|
const params = {
|
||||||
|
...formData.value,
|
||||||
|
verifyKey: captcha.value.key,
|
||||||
|
bindCode: code,
|
||||||
|
expires: 3600000,
|
||||||
|
};
|
||||||
|
const res = await authLogin(params);
|
||||||
|
console.log('res: ', res);
|
||||||
|
message.success('登录成功');
|
||||||
|
goRedirect();
|
||||||
|
setTimeout(() => window.close(), 1000);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log('error', err);
|
getCode();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
status="disable"
|
status="disable"
|
||||||
:statusNames="{ disable: StatusColorEnum.error }"
|
:statusNames="{ disable: StatusColorEnum.error }"
|
||||||
statusText="正常"
|
statusText="正常"
|
||||||
:showMask="false"
|
|
||||||
:actions="actions"
|
:actions="actions"
|
||||||
|
v-model="data"
|
||||||
>
|
>
|
||||||
<template #img>
|
<template #img>
|
||||||
<img :src="getImage('/device-product.png')" />
|
<img :src="getImage('/device-product.png')" />
|
||||||
|
@ -63,6 +63,10 @@ const actions = ref([
|
||||||
label: '删除',
|
label: '删除',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const data = ref({
|
||||||
|
id: 123
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -18,16 +18,34 @@
|
||||||
dataIndex: 'classifiedName',
|
dataIndex: 'classifiedName',
|
||||||
key: 'classifiedName',
|
key: 'classifiedName',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 250,
|
||||||
|
scopedSlots: true
|
||||||
|
}
|
||||||
]"
|
]"
|
||||||
:actions="actions"
|
:actions="actions"
|
||||||
:request="request"
|
:request="request"
|
||||||
:rowSelection="rowSelection"
|
:rowSelection="{
|
||||||
|
selectedRowKeys: _selectedRowKeys,
|
||||||
|
onChange: onSelectChange
|
||||||
|
}"
|
||||||
|
@cancelSelect="cancelSelect"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button type="primary">新增</a-button>
|
<a-button type="primary">新增</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #cardContent="slotProps">
|
<template #card="slotProps">
|
||||||
<h3>{{slotProps.item.name}}</h3>
|
<CardBox :value="slotProps" @click="handleClick" :actions="actions" v-bind="slotProps" :active="_selectedRowKeys.includes(slotProps.id)">
|
||||||
|
<template #img>
|
||||||
|
<slot name="img">
|
||||||
|
<img :src="getImage('/device-product.png')" />
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<h3>{{slotProps.name}}</h3>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<div class="card-item-content-text">
|
<div class="card-item-content-text">
|
||||||
|
@ -43,9 +61,23 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
<template #id="slotProps">
|
<template #id="slotProps">
|
||||||
<a>{{slotProps.row.id}}</a>
|
<a>{{slotProps.row.id}}</a>
|
||||||
</template>
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<a-tooltip v-for="i in actions" :key="i.key" v-bind="i.tooltip">
|
||||||
|
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm">
|
||||||
|
<a-button style="padding: 0" type="link"><AIcon :type="i.icon" /></a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
<a-button style="padding: 0" type="link" v-else @click="i.onClick && i.onClick(slotProps)">
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -54,7 +86,6 @@
|
||||||
import server from "@/utils/request";
|
import server from "@/utils/request";
|
||||||
import type { ActionsType } from '@/components/Table/index.vue'
|
import type { ActionsType } from '@/components/Table/index.vue'
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import type { TableProps, TableColumnType } from 'ant-design-vue';
|
|
||||||
|
|
||||||
const request = (data: any) => server.post(`/device-product/_query`, data)
|
const request = (data: any) => server.post(`/device-product/_query`, data)
|
||||||
const actions: ActionsType[] = [
|
const actions: ActionsType[] = [
|
||||||
|
@ -65,30 +96,50 @@ const actions: ActionsType[] = [
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: '编辑'
|
title: '编辑'
|
||||||
},
|
},
|
||||||
// component: <UnorderedListOutlined />
|
icon: 'icon-rizhifuwu'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'import',
|
||||||
|
// disabled: true,
|
||||||
|
text: "导入",
|
||||||
|
tooltip: {
|
||||||
|
title: '导入'
|
||||||
|
},
|
||||||
|
icon: 'icon-xiazai'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
disabled: true,
|
// disabled: true,
|
||||||
text: "删除",
|
text: "删除",
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: '删除'
|
title: '删除'
|
||||||
},
|
},
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: '确认删除?'
|
title: '确认删除?'
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const rowSelection: TableProps['rowSelection'] = {
|
const _selectedRowKeys = ref<string[]>([])
|
||||||
onChange: (selectedRowKeys: string[], selectedRows: any[]) => {
|
|
||||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
const onSelectChange = (keys: string[]) => {
|
||||||
},
|
_selectedRowKeys.value = [...keys]
|
||||||
getCheckboxProps: (record: any) => ({
|
}
|
||||||
disabled: record.name === 'Disabled User',
|
|
||||||
name: record.name,
|
const cancelSelect = () => {
|
||||||
}),
|
_selectedRowKeys.value = []
|
||||||
};
|
}
|
||||||
|
|
||||||
|
const handleClick = (dt: any) => {
|
||||||
|
// _selectedRowKeys.value = [dt.id] // 单选
|
||||||
|
// _selectedRowKeys.value = [..._selectedRowKeys.value, dt.id] // 多选
|
||||||
|
if(_selectedRowKeys.value.includes(dt.id)) {
|
||||||
|
const _index = _selectedRowKeys.value.findIndex(i => i === dt.id)
|
||||||
|
_selectedRowKeys.value.splice(_index, 1)
|
||||||
|
} else {
|
||||||
|
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,9 @@
|
||||||
重置
|
重置
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<JTable :columns="columns">
|
||||||
:columns="columns"
|
|
||||||
:data-source="tableData"
|
|
||||||
:row-selection="{
|
|
||||||
onChange: (selectedRowKeys, selectedRows) =>
|
|
||||||
(selectItem = selectedRows),
|
|
||||||
type: 'radio',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</a-table>
|
|
||||||
|
|
||||||
|
</JTable>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-button key="back" @click="visible = false">取消</a-button>
|
<a-button key="back" @click="visible = false">取消</a-button>
|
||||||
|
@ -90,9 +82,7 @@ const productList = ref<[productItem] | []>([]);
|
||||||
const getOptions = () => {
|
const getOptions = () => {
|
||||||
productList.value = [];
|
productList.value = [];
|
||||||
};
|
};
|
||||||
const clickSearch = ()=>{
|
const clickSearch = () => {};
|
||||||
|
|
||||||
}
|
|
||||||
const clickReset = () => {
|
const clickReset = () => {
|
||||||
Object.entries(form.value).forEach(([prop]) => {
|
Object.entries(form.value).forEach(([prop]) => {
|
||||||
form.value[prop] = '';
|
form.value[prop] = '';
|
||||||
|
@ -102,27 +92,27 @@ const clickReset = () => {
|
||||||
// 表格部分
|
// 表格部分
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'deviceId',
|
title: '设备Id',
|
||||||
dataIndex: 'deviceId',
|
dataIndex: 'deviceId',
|
||||||
key: 'deviceId',
|
key: 'deviceId',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'deviceName',
|
title: '设备名称',
|
||||||
dataIndex: 'deviceName',
|
dataIndex: 'deviceName',
|
||||||
key: 'deviceName',
|
key: 'deviceName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'productName',
|
title: '产品名称',
|
||||||
dataIndex: 'productName',
|
dataIndex: 'productName',
|
||||||
key: 'productName',
|
key: 'productName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'createTime',
|
title: '注册时间',
|
||||||
dataIndex: 'createTime',
|
dataIndex: 'createTime',
|
||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'status',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,5 @@
|
||||||
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||||
/** 初始化数据提交表单 */
|
/** 初始化数据提交表单 */
|
||||||
export interface modalState {
|
export interface modalState {
|
||||||
host: string; // 本地地址
|
host: string; // 本地地址
|
||||||
|
@ -19,7 +21,7 @@ export interface formState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* logo上传表当
|
* logo上传表单
|
||||||
*/
|
*/
|
||||||
export interface logoState {
|
export interface logoState {
|
||||||
logoValue: string;
|
logoValue: string;
|
||||||
|
@ -29,5 +31,7 @@ export interface logoState {
|
||||||
inBackground: boolean;
|
inBackground: boolean;
|
||||||
iconValue: string;
|
iconValue: string;
|
||||||
backValue: string;
|
backValue: string;
|
||||||
handleChangeLogo:(url: string) => void
|
handleChangeLogo:(info: UploadChangeParam ) => void
|
||||||
|
beforeBackUpload:(file: UploadProps['beforeUpload']) => void
|
||||||
|
changeBackUpload:(info: UploadChangeParam ) => void
|
||||||
}
|
}
|
|
@ -30,6 +30,8 @@
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="form.title"
|
v-model:value="form.title"
|
||||||
|
:maxlength="64"
|
||||||
|
placeholder="请输入系统名称"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
|
@ -69,6 +71,7 @@
|
||||||
</template>
|
</template>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="form.apikey"
|
v-model:value="form.apikey"
|
||||||
|
placeholder="请输入高德API Key"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
|
@ -91,6 +94,7 @@
|
||||||
v-model:value="
|
v-model:value="
|
||||||
form.basePath
|
form.basePath
|
||||||
"
|
"
|
||||||
|
placeholder="请输入高德API Key"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-row :gutter="24" :span="24">
|
<a-row :gutter="24" :span="24">
|
||||||
|
@ -224,7 +228,7 @@
|
||||||
class="upload-image-content-logo"
|
class="upload-image-content-logo"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="upload-image"
|
class="upload-image-icon"
|
||||||
v-if="
|
v-if="
|
||||||
iconValue
|
iconValue
|
||||||
"
|
"
|
||||||
|
@ -293,7 +297,14 @@
|
||||||
<div
|
<div
|
||||||
class="upload-image-border-back"
|
class="upload-image-border-back"
|
||||||
>
|
>
|
||||||
<a-upload>
|
<a-upload
|
||||||
|
@beforeUpload="
|
||||||
|
beforeBackUpload
|
||||||
|
"
|
||||||
|
@change="
|
||||||
|
changeBackUpload
|
||||||
|
"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="upload-image-content-back"
|
class="upload-image-content-back"
|
||||||
>
|
>
|
||||||
|
@ -338,26 +349,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-upload>
|
</a-upload>
|
||||||
|
|
||||||
<!-- <div
|
|
||||||
v-if="
|
|
||||||
logoValue &&
|
|
||||||
logoLoading
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="upload-loading-mask"
|
|
||||||
>
|
|
||||||
<LoadingOutlined
|
|
||||||
v-if="
|
|
||||||
loading
|
|
||||||
"
|
|
||||||
style="
|
|
||||||
font-size: 28px;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="upload-tips">
|
<div class="upload-tips">
|
||||||
|
@ -387,7 +378,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-info">
|
<div class="menu-info">
|
||||||
<b>系统初始化xxx个菜单</b>
|
<b>系统初始化{{ count }}个菜单</b>
|
||||||
<div>
|
<div>
|
||||||
初始化后的菜单可在“菜单管理”页面进行维护管理
|
初始化后的菜单可在“菜单管理”页面进行维护管理
|
||||||
</div>
|
</div>
|
||||||
|
@ -645,7 +636,12 @@
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
<a-button type="primary" class="btn-style">确定</a-button>
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
class="btn-style"
|
||||||
|
@click="submitData"
|
||||||
|
>确定</a-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -659,12 +655,17 @@ import {
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { ROLEKEYS, RoleData } from './data/RoleData';
|
import { ROLEKEYS, RoleData } from './data/RoleData';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
|
||||||
import type { Rule } from 'ant-design-vue/es/form';
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
import { Form } from 'ant-design-vue';
|
import type {
|
||||||
import type { UploadChangeParam } from 'antd/lib/upload/interface';
|
FormInstance,
|
||||||
|
UploadChangeParam,
|
||||||
|
UploadProps,
|
||||||
|
} from 'ant-design-vue';
|
||||||
import { modalState, formState, logoState } from './data/interface';
|
import { modalState, formState, logoState } from './data/interface';
|
||||||
const formRef = ref<FormInstance>();
|
import BaseMenu from './data/baseMenu';
|
||||||
|
import { getSystemPermission, save } from '@/api/initHome';
|
||||||
|
const formRef = ref();
|
||||||
|
const menuRef = ref();
|
||||||
const formBasicRef = ref<FormInstance>();
|
const formBasicRef = ref<FormInstance>();
|
||||||
/**
|
/**
|
||||||
* 表单数据
|
* 表单数据
|
||||||
|
@ -673,7 +674,7 @@ const form = reactive<formState>({
|
||||||
title: '',
|
title: '',
|
||||||
headerTheme: 'light',
|
headerTheme: 'light',
|
||||||
apikey: '',
|
apikey: '',
|
||||||
basePath: '',
|
basePath: `${window.location.origin}/api`,
|
||||||
logo: '',
|
logo: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
rulesFrom: {
|
rulesFrom: {
|
||||||
|
@ -688,14 +689,14 @@ const form = reactive<formState>({
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择主题色',
|
message: '请选择主题色',
|
||||||
trigger: '[blur, change]',
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
basePath: [
|
basePath: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入base-path',
|
message: '请输入base-path',
|
||||||
trigger: 'blur, change',
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -762,7 +763,6 @@ const ModalForm = reactive<modalState>({
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
validator: validateNumber,
|
validator: validateNumber,
|
||||||
|
|
||||||
trigger: 'change',
|
trigger: 'change',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -815,17 +815,17 @@ const cancel = () => {
|
||||||
* 提交图片
|
* 提交图片
|
||||||
*/
|
*/
|
||||||
const logoData = reactive<logoState>({
|
const logoData = reactive<logoState>({
|
||||||
logoValue: '',
|
logoValue: '/public/logo.png',
|
||||||
logoLoading: false,
|
logoLoading: false,
|
||||||
inLogo: false,
|
inLogo: false,
|
||||||
inIcon: false,
|
inIcon: false,
|
||||||
inBackground: false,
|
inBackground: false,
|
||||||
iconValue: '',
|
iconValue: '/public/favicon.ico',
|
||||||
backValue: '',
|
backValue: '/public/images/login.png',
|
||||||
/**
|
/**
|
||||||
* 图片上传改变事件
|
* 图片上传改变事件
|
||||||
*/
|
*/
|
||||||
handleChangeLogo: (info: UploadChangeParam) => {
|
handleChangeLogo: (info) => {
|
||||||
if (info.file.status === 'uploading') {
|
if (info.file.status === 'uploading') {
|
||||||
logoData.logoLoading = true;
|
logoData.logoLoading = true;
|
||||||
}
|
}
|
||||||
|
@ -835,6 +835,14 @@ const logoData = reactive<logoState>({
|
||||||
logoData.logoValue = info.file.response?.result;
|
logoData.logoValue = info.file.response?.result;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 背景图片上传之前
|
||||||
|
*/
|
||||||
|
beforeBackUpload: (file) => {},
|
||||||
|
/**
|
||||||
|
* 背景图片发生改变
|
||||||
|
*/
|
||||||
|
changeBackUpload: (info) => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -847,6 +855,79 @@ const {
|
||||||
backValue,
|
backValue,
|
||||||
handleChangeLogo,
|
handleChangeLogo,
|
||||||
} = toRefs(logoData);
|
} = toRefs(logoData);
|
||||||
|
/**
|
||||||
|
* 提交基础表单
|
||||||
|
*/
|
||||||
|
const basicData = reactive({
|
||||||
|
/**
|
||||||
|
* 提交基础表单数据
|
||||||
|
*/
|
||||||
|
saveBasicInfo: async () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取菜单数据
|
||||||
|
*/
|
||||||
|
const menuDatas = reactive({
|
||||||
|
count: 0,
|
||||||
|
/**
|
||||||
|
* 获取当前系统权限信息
|
||||||
|
*/
|
||||||
|
getSystemPermissionData: async () => {
|
||||||
|
const resp = await getSystemPermission();
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const newTree = menuDatas.filterMenu(
|
||||||
|
resp.result.map((item: any) => JSON.parse(item).id),
|
||||||
|
BaseMenu,
|
||||||
|
);
|
||||||
|
const _count = menuDatas.menuCount(newTree);
|
||||||
|
menuDatas.count = _count;
|
||||||
|
console.log(menuDatas.count, 'menuDatas.count');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 过滤菜单
|
||||||
|
*/
|
||||||
|
filterMenu: (permissions: string[], menus: any[]) => {
|
||||||
|
return menus.filter((item) => {
|
||||||
|
let isShow = false;
|
||||||
|
if (item.showPage && item.showPage.length) {
|
||||||
|
isShow = item.showPage.every((pItem: any) => {
|
||||||
|
return permissions.includes(pItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
item.children = menuDatas.filterMenu(
|
||||||
|
permissions,
|
||||||
|
item.children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return isShow || !!item.children?.length;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 计算菜单数量
|
||||||
|
*/
|
||||||
|
menuCount: (menus: any[]) => {
|
||||||
|
return menus.reduce((pre, next) => {
|
||||||
|
let _count = 1;
|
||||||
|
if (next.children) {
|
||||||
|
_count = menuDatas.menuCount(next.children);
|
||||||
|
}
|
||||||
|
return pre + _count;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count } = toRefs(menuDatas);
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
menuDatas.getSystemPermissionData();
|
||||||
|
/**
|
||||||
|
* 提交所有数据
|
||||||
|
*/
|
||||||
|
const submit = () => {};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.page-container {
|
.page-container {
|
||||||
|
@ -999,6 +1080,13 @@ const {
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
.upload-image-icon {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50%;
|
||||||
|
background-size: inherit;
|
||||||
|
}
|
||||||
.upload-image-mask {
|
.upload-image-mask {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -1,21 +1,30 @@
|
||||||
const MetworkTypeMapping = new Map();
|
|
||||||
MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
|
|
||||||
MetworkTypeMapping.set('http-server-gateway', 'HTTP_SERVER');
|
|
||||||
MetworkTypeMapping.set('udp-device-gateway', 'UDP');
|
|
||||||
MetworkTypeMapping.set('coap-server-gateway', 'COAP_SERVER');
|
|
||||||
MetworkTypeMapping.set('mqtt-client-gateway', 'MQTT_CLIENT');
|
|
||||||
MetworkTypeMapping.set('mqtt-server-gateway', 'MQTT_SERVER');
|
|
||||||
MetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
|
|
||||||
|
|
||||||
const ProcotoleMapping = new Map();
|
const ProtocolMapping = new Map();
|
||||||
ProcotoleMapping.set('websocket-server', 'WebSocket');
|
ProtocolMapping.set('websocket-server', 'WebSocket');
|
||||||
ProcotoleMapping.set('http-server-gateway', 'HTTP');
|
ProtocolMapping.set('http-server-gateway', 'HTTP');
|
||||||
ProcotoleMapping.set('udp-device-gateway', 'UDP');
|
ProtocolMapping.set('udp-device-gateway', 'UDP');
|
||||||
ProcotoleMapping.set('coap-server-gateway', 'CoAP');
|
ProtocolMapping.set('coap-server-gateway', 'CoAP');
|
||||||
ProcotoleMapping.set('mqtt-client-gateway', 'MQTT');
|
ProtocolMapping.set('mqtt-client-gateway', 'MQTT');
|
||||||
ProcotoleMapping.set('mqtt-server-gateway', 'MQTT');
|
ProtocolMapping.set('mqtt-server-gateway', 'MQTT');
|
||||||
ProcotoleMapping.set('tcp-server-gateway', 'TCP');
|
ProtocolMapping.set('tcp-server-gateway', 'TCP');
|
||||||
ProcotoleMapping.set('child-device', '');
|
ProtocolMapping.set('child-device', '');
|
||||||
|
ProtocolMapping.set('OneNet', 'HTTP');
|
||||||
|
ProtocolMapping.set('Ctwing', 'HTTP');
|
||||||
|
ProtocolMapping.set('modbus-tcp', 'MODBUS_TCP');
|
||||||
|
ProtocolMapping.set('opc-ua', 'OPC_UA');
|
||||||
|
ProtocolMapping.set('edge-child-device', 'EdgeGateway');
|
||||||
|
ProtocolMapping.set('official-edge-gateway', 'MQTT');
|
||||||
|
|
||||||
|
const NetworkTypeMapping = new Map();
|
||||||
|
NetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
|
||||||
|
NetworkTypeMapping.set('http-server-gateway', 'HTTP_SERVER');
|
||||||
|
NetworkTypeMapping.set('udp-device-gateway', 'UDP');
|
||||||
|
NetworkTypeMapping.set('coap-server-gateway', 'COAP_SERVER');
|
||||||
|
NetworkTypeMapping.set('mqtt-client-gateway', 'MQTT_CLIENT');
|
||||||
|
NetworkTypeMapping.set('mqtt-server-gateway', 'MQTT_SERVER');
|
||||||
|
NetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
|
||||||
|
NetworkTypeMapping.set('official-edge-gateway', 'MQTT_SERVER');
|
||||||
|
|
||||||
|
|
||||||
const descriptionList = {
|
const descriptionList = {
|
||||||
'udp-device-gateway':
|
'udp-device-gateway':
|
||||||
|
@ -96,4 +105,4 @@ const columnsHTTP = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export { MetworkTypeMapping, ProcotoleMapping, descriptionList, columnsMQTT, columnsHTTP };
|
export { NetworkTypeMapping, ProtocolMapping, descriptionList, columnsMQTT, columnsHTTP };
|
||||||
|
|
|
@ -1,62 +1,93 @@
|
||||||
<template>
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
<a-card :bordered="false">
|
<a-card :bordered="false">
|
||||||
<TitleComponent data="自定义设备接入"></TitleComponent>
|
<div v-if="type">
|
||||||
<div>
|
<Provider
|
||||||
<a-row :gutter="[24, 24]">
|
@onClick="goProviders"
|
||||||
<a-col :span="12" v-for="item in items" :key="item.id">
|
:dataSource="dataSource"
|
||||||
<div class="provider">
|
></Provider>
|
||||||
<div class="box">
|
|
||||||
<div class="left">
|
|
||||||
<div class="images">
|
|
||||||
<img :src="backMap.get(item.id)" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="context">
|
<div v-else>
|
||||||
<div class="title">{{ item.name }}</div>
|
<div v-if="!id"><a @click="goBack">返回</a></div>
|
||||||
<div class="desc">
|
<AccessNetwork :provider="provider" :data="data" />
|
||||||
<a-tooltip :title="item.description">
|
|
||||||
{{ item.description || '' }}
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
@click="goProviders(item)"
|
|
||||||
>接入</a-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessConfigDetail">
|
<script lang="ts" setup name="AccessConfigDetail">
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||||
|
import AccessNetwork from '../components/Network.vue';
|
||||||
|
import Provider from '../components/Provider/index.vue';
|
||||||
|
import { getProviders, detail } from '@/api/link/accessConfig';
|
||||||
|
|
||||||
const items = [
|
// const router = useRouter();
|
||||||
{ id: 'mqtt-server-gateway', name: '测试1', description: '测试1' },
|
const route = useRoute();
|
||||||
{ id: 'websocket-server', name: '测试2', description: '测试' },
|
|
||||||
{ id: 'coap-server-gateway', name: '测试3', description: '测试' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const backMap = new Map();
|
const id = route.query.id;
|
||||||
backMap.set('mqtt-server-gateway', getImage('/access/mqtt.png'));
|
|
||||||
backMap.set('websocket-server', getImage('/access/websocket.png'));
|
|
||||||
backMap.set('coap-server-gateway', getImage('/access/coap.png'));
|
|
||||||
backMap.set('tcp-server-gateway', getImage('/access/tcp.png'));
|
|
||||||
backMap.set('child-device', getImage('/access/child-device.png'));
|
|
||||||
backMap.set('http-server-gateway', getImage('/access/http.png'));
|
|
||||||
backMap.set('udp-device-gateway', getImage('/access/udp.png'));
|
|
||||||
backMap.set('mqtt-client-gateway', getImage('/access/mqtt-broke.png'));
|
|
||||||
|
|
||||||
const goProviders = (value: object) => {
|
const dataSource = ref([]);
|
||||||
console.log(111, value);
|
const type = ref(false);
|
||||||
|
const loading = ref(true);
|
||||||
|
const provider = ref({});
|
||||||
|
const data = ref({});
|
||||||
|
|
||||||
|
const goProviders = (param: object) => {
|
||||||
|
provider.value = param;
|
||||||
|
type.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
provider.value = {};
|
||||||
|
type.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryProviders = async () => {
|
||||||
|
const resp = await getProviders();
|
||||||
|
if (resp.status === 200) {
|
||||||
|
dataSource.value = resp.result.filter(
|
||||||
|
(item) =>
|
||||||
|
item.channel === 'network' || item.channel === 'child-device',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProvidersData = async () => {
|
||||||
|
if (id) {
|
||||||
|
getProviders().then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
dataSource.value = response.result.filter(
|
||||||
|
(item) =>
|
||||||
|
item.channel === 'network' ||
|
||||||
|
item.channel === 'child-device',
|
||||||
|
);
|
||||||
|
detail(id).then((resp) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const dt = response.result.find(
|
||||||
|
(item) => item?.id === resp.result.provider,
|
||||||
|
);
|
||||||
|
provider.value = dt;
|
||||||
|
data.value = resp.result;
|
||||||
|
type.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loading.value = false;
|
||||||
|
} else {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
type.value = true;
|
||||||
|
queryProviders();
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loading.value = true;
|
||||||
|
getProvidersData();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
<template>
|
||||||
|
<a-card hoverable :class="['card-render', checked === data.id ? 'checked' : '']" @click="checkedChange(data.id)">
|
||||||
|
<div class="title">
|
||||||
|
<a-tooltip placement="topLeft" :title="data.name">{{ data.name }}</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<div class="desc">
|
||||||
|
<a-tooltip placement="topLeft" :title="data.description">{{ data.description }}</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="checked-icon">
|
||||||
|
<div><a-icon type="check" /></div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "AccessCard",
|
||||||
|
props: ['data', 'checked'],
|
||||||
|
methods: {
|
||||||
|
checkedChange(id){
|
||||||
|
this.$emit('checkedChange', id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.card-render {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: url("/public/images/access/access.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
min-height: 105px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: calc(100% - 88px);
|
||||||
|
overflow: hidden;
|
||||||
|
font-weight: 800;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checked-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: -22px;
|
||||||
|
bottom: -22px;
|
||||||
|
z-index: 2;
|
||||||
|
display: none;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: red;
|
||||||
|
background-color: #2f54eb;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 0 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.checked {
|
||||||
|
position: relative;
|
||||||
|
color: #2f54eb;
|
||||||
|
border-color: #2f54eb;
|
||||||
|
|
||||||
|
.checked-icon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,733 @@
|
||||||
|
<template>
|
||||||
|
<div style="margin-top: 10px">
|
||||||
|
<a-steps :current="stepCurrent">
|
||||||
|
<a-step v-for="item in steps" :key="item" :title="item" />
|
||||||
|
</a-steps>
|
||||||
|
<div class="steps-content">
|
||||||
|
<div class="steps-box" v-if="current === 0">
|
||||||
|
<div class="alert">
|
||||||
|
<a-icon type="info-circle" style="margin-right: 10px" />
|
||||||
|
选择与设备通信的网络组件
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<a-input-search
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入"
|
||||||
|
style="width: 300px"
|
||||||
|
@search="networkSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="addNetwork">新增</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="card-item">
|
||||||
|
<a-row :gutter="[24, 24]" v-if="networkList.length > 0">
|
||||||
|
<a-col
|
||||||
|
:span="8"
|
||||||
|
v-for="item in networkList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<access-card
|
||||||
|
@checkedChange="checkedChange"
|
||||||
|
:checked="networkCurrent"
|
||||||
|
:data="{
|
||||||
|
...item,
|
||||||
|
description: item.description
|
||||||
|
? item.description
|
||||||
|
: descriptionList[provider.id],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div slot="other" class="other">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<div
|
||||||
|
slot="title"
|
||||||
|
v-if="
|
||||||
|
(item.addresses || []).length >
|
||||||
|
1
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="i in item.addresses ||
|
||||||
|
[]"
|
||||||
|
:key="i.address"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<a-badge
|
||||||
|
:color="
|
||||||
|
i.health === -1
|
||||||
|
? 'red'
|
||||||
|
: 'green'
|
||||||
|
"
|
||||||
|
/>{{ i.address }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="i in (
|
||||||
|
item.addresses || []
|
||||||
|
).slice(0, 1)"
|
||||||
|
:key="i.address"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<a-badge
|
||||||
|
:color="
|
||||||
|
i.health === -1
|
||||||
|
? 'red'
|
||||||
|
: 'green'
|
||||||
|
"
|
||||||
|
:text="i.address"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
(item.addresses || [])
|
||||||
|
.length > 1
|
||||||
|
"
|
||||||
|
>...</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</access-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-box" v-else-if="current === 1">
|
||||||
|
<div class="alert">
|
||||||
|
<a-icon type="info-circle" style="margin-right: 10px" />
|
||||||
|
使用选择的消息协议,对网络组件通信数据进行编解码、认证等操作
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<a-input-search
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入"
|
||||||
|
style="width: 300px"
|
||||||
|
@search="procotolSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="addProcotol"
|
||||||
|
>新增</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="card-item">
|
||||||
|
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
||||||
|
<a-col
|
||||||
|
:span="8"
|
||||||
|
v-for="item in procotolList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<access-card
|
||||||
|
@checkedChange="procotolChange"
|
||||||
|
:checked="procotolCurrent"
|
||||||
|
:data="item"
|
||||||
|
>
|
||||||
|
</access-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-box" v-else>
|
||||||
|
<div class="card-last">
|
||||||
|
<a-row :gutter="[24, 24]">
|
||||||
|
<a-col :span="12">
|
||||||
|
<title-component data="基本信息" />
|
||||||
|
<div>
|
||||||
|
<a-form :form="form" layout="vertical">
|
||||||
|
<a-form-item label="名称">
|
||||||
|
<a-input
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入名称"
|
||||||
|
v-decorator="[
|
||||||
|
'name',
|
||||||
|
{
|
||||||
|
initialValue: data.name,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message:
|
||||||
|
'请输入名称!',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="说明">
|
||||||
|
<a-textarea
|
||||||
|
placeholder="请输入说明"
|
||||||
|
:rows="4"
|
||||||
|
v-decorator="[
|
||||||
|
'description',
|
||||||
|
{
|
||||||
|
initialValue:
|
||||||
|
data.description,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="config-right">
|
||||||
|
<div class="config-right-item">
|
||||||
|
<div class="config-right-item-title">
|
||||||
|
接入方式
|
||||||
|
</div>
|
||||||
|
<div class="config-right-item-context">
|
||||||
|
{{ provider.name }}
|
||||||
|
</div>
|
||||||
|
<div class="config-right-item-context">
|
||||||
|
{{ provider.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="config-right-item">
|
||||||
|
<div class="config-right-item-title">
|
||||||
|
消息协议
|
||||||
|
</div>
|
||||||
|
<div class="config-right-item-context">
|
||||||
|
{{
|
||||||
|
procotolList.find(
|
||||||
|
(i) => i.id === procotolCurrent,
|
||||||
|
).name
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="config-right-item-context"
|
||||||
|
v-if="config.document"
|
||||||
|
>
|
||||||
|
{{ config.document }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="config-right-item"
|
||||||
|
v-if="
|
||||||
|
networkList.find(
|
||||||
|
(i) => i.id === networkCurrent,
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
networkList.find(
|
||||||
|
(i) => i.id === networkCurrent,
|
||||||
|
).addresses || []
|
||||||
|
).length > 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="config-right-item-title">
|
||||||
|
网络组件
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="i in (networkList.find(
|
||||||
|
(i) => i.id === networkCurrent,
|
||||||
|
) &&
|
||||||
|
networkList.find(
|
||||||
|
(i) => i.id === networkCurrent,
|
||||||
|
).addresses) ||
|
||||||
|
[]"
|
||||||
|
:key="i.address"
|
||||||
|
>
|
||||||
|
<a-badge
|
||||||
|
:color="
|
||||||
|
i.health === -1
|
||||||
|
? 'red'
|
||||||
|
: 'green'
|
||||||
|
"
|
||||||
|
:text="i.address"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="config-right-item"
|
||||||
|
v-if="
|
||||||
|
config.routes &&
|
||||||
|
config.routes.length > 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="config-right-item-title">
|
||||||
|
{{
|
||||||
|
data.provider ===
|
||||||
|
'mqtt-server-gateway' ||
|
||||||
|
data.provider ===
|
||||||
|
'mqtt-client-gateway'
|
||||||
|
? 'topic'
|
||||||
|
: 'URL信息'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
:pagination="false"
|
||||||
|
:rowKey="generateUUID()"
|
||||||
|
:data-source="config.routes || []"
|
||||||
|
bordered
|
||||||
|
:columns="columnsMQTT"
|
||||||
|
:scroll="{ y: 300 }"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
slot="stream"
|
||||||
|
slot-scope="text, record"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
record.upstream &&
|
||||||
|
record.downstream
|
||||||
|
"
|
||||||
|
>上行、下行</span
|
||||||
|
>
|
||||||
|
<span v-else-if="record.upstream"
|
||||||
|
>上行</span
|
||||||
|
>
|
||||||
|
<span v-else-if="record.downstream"
|
||||||
|
>下行</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-action">
|
||||||
|
<a-button
|
||||||
|
v-if="[0, 1].includes(current)"
|
||||||
|
type="primary"
|
||||||
|
@click="next"
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</a-button>
|
||||||
|
<a-button v-if="current === 2" type="primary" @click="save">
|
||||||
|
保存
|
||||||
|
</a-button>
|
||||||
|
<a-button v-if="current > 0" style="margin-left: 8px" @click="prev">
|
||||||
|
上一步
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="AccessNetwork">
|
||||||
|
import {
|
||||||
|
getNetworkList,
|
||||||
|
getProtocolList,
|
||||||
|
getConfigView,
|
||||||
|
save,
|
||||||
|
update,
|
||||||
|
getChildConfigView,
|
||||||
|
} from '@/api/link/accessConfig';
|
||||||
|
import {
|
||||||
|
descriptionList,
|
||||||
|
NetworkTypeMapping,
|
||||||
|
ProtocolMapping,
|
||||||
|
} from '../Detail/data';
|
||||||
|
import AccessCard from './AccessCard/index.vue';
|
||||||
|
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||||
|
import { message, Form } from 'ant-design-vue';
|
||||||
|
|
||||||
|
function generateUUID() {
|
||||||
|
var d = new Date().getTime();
|
||||||
|
if (
|
||||||
|
typeof performance !== 'undefined' &&
|
||||||
|
typeof performance.now === 'function'
|
||||||
|
) {
|
||||||
|
d += performance.now(); //use high-precision timer if available
|
||||||
|
}
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
||||||
|
/[xy]/g,
|
||||||
|
function (c) {
|
||||||
|
var r = (d + Math.random() * 16) % 16 | 0;
|
||||||
|
d = Math.floor(d / 16);
|
||||||
|
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
provider: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const current = ref(0);
|
||||||
|
const stepCurrent = ref(0);
|
||||||
|
const steps = ref(['网络组件', '消息协议', '完成']);
|
||||||
|
const networkList = ref([]);
|
||||||
|
const procotolList = ref([]);
|
||||||
|
const allProcotolList = ref([]);
|
||||||
|
const networkCurrent = ref('');
|
||||||
|
const procotolCurrent = ref('');
|
||||||
|
let config = ref({});
|
||||||
|
let columnsMQTT = ref([]);
|
||||||
|
const form = reactive({
|
||||||
|
name: 'access',
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryNetworkList = async (id: string, params: object, data = {}) => {
|
||||||
|
console.log('queryNetworkList',NetworkTypeMapping.get(id), data, params);
|
||||||
|
|
||||||
|
const resp = await getNetworkList(NetworkTypeMapping.get(id), data, params);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
networkList.value = resp.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// const queryProcotolList=async(id:string, params:object) =>{
|
||||||
|
const queryProcotolList = async (id: string, params = {}) => {
|
||||||
|
const resp = await getProtocolList(ProtocolMapping.get(id), {
|
||||||
|
...params,
|
||||||
|
'sorts[0].name': 'createTime',
|
||||||
|
'sorts[0].order': 'desc',
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
procotolList.value = resp.result;
|
||||||
|
allProcotolList.value = resp.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNetwork = () => {
|
||||||
|
// const url = this.$store.state.permission.routes['Link/Type/Detail']
|
||||||
|
const url = '/demo';
|
||||||
|
const tab = window.open(
|
||||||
|
`${window.location.origin + window.location.pathname}#${url}?type=${
|
||||||
|
NetworkTypeMapping.get(props.provider?.id) || ''
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
tab.onTabSaveSuccess = (value) => {
|
||||||
|
if (value.success) {
|
||||||
|
networkCurrent.value = value.result.id;
|
||||||
|
queryNetworkList(props.provider?.id, {
|
||||||
|
include: networkCurrent.value || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const addProcotol = () => {
|
||||||
|
// const url = this.$store.state.permission.routes['Link/Protocol']
|
||||||
|
const url = '/demo';
|
||||||
|
const tab = window.open(
|
||||||
|
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
||||||
|
);
|
||||||
|
tab.onTabSaveSuccess = (value) => {
|
||||||
|
if (value.success) {
|
||||||
|
procotolCurrent.value = value.result?.id;
|
||||||
|
queryProcotolList(props.provider?.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkedChange = (id: string) => {
|
||||||
|
networkCurrent.value = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const networkSearch = (value: string) => {
|
||||||
|
console.log('networkSearch',
|
||||||
|
props.provider.id,
|
||||||
|
{
|
||||||
|
include: networkCurrent.value || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'name$LIKE',
|
||||||
|
value: `%${value}%`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryNetworkList(
|
||||||
|
props.provider.id,
|
||||||
|
{
|
||||||
|
include: networkCurrent.value || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'name$LIKE',
|
||||||
|
value: `%${value}%`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const procotolChange = (id: string) => {
|
||||||
|
if (!props.data.id) {
|
||||||
|
procotolCurrent.value = id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const procotolSearch = (value: string) => {
|
||||||
|
if (value) {
|
||||||
|
const list = allProcotolList.value.filter((i) => {
|
||||||
|
return (
|
||||||
|
i.name &&
|
||||||
|
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
procotolList.value = list;
|
||||||
|
} else {
|
||||||
|
procotolList.value = allProcotolList.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveData = () => {
|
||||||
|
form.validateFields(async (err, values) => {
|
||||||
|
if (!err) {
|
||||||
|
let resp = undefined;
|
||||||
|
if (props.data && props.data.id) {
|
||||||
|
resp = await update({
|
||||||
|
...props.data,
|
||||||
|
name: values.name,
|
||||||
|
description: values.description,
|
||||||
|
protocol: procotolCurrent.value,
|
||||||
|
channel: 'network', // 网络组件
|
||||||
|
channelId: networkCurrent.value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resp = await save({
|
||||||
|
name: values.name,
|
||||||
|
description: values.description,
|
||||||
|
provider: props.provider.id,
|
||||||
|
protocol: procotolCurrent.value,
|
||||||
|
transport:
|
||||||
|
props.provider?.id === 'child-device'
|
||||||
|
? 'Gateway'
|
||||||
|
: ProtocolMapping.get(props.provider.id),
|
||||||
|
channel: 'network', // 网络组件
|
||||||
|
channelId: networkCurrent.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
// 回到列表页面
|
||||||
|
if (window.onTabSaveSuccess) {
|
||||||
|
window.onTabSaveSuccess(resp);
|
||||||
|
setTimeout(() => window.close(), 300);
|
||||||
|
} else {
|
||||||
|
// this.$store.dispatch('jumpPathByKey', { key: MenuKeys['Link/AccessConfig'] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const next = async () => {
|
||||||
|
if (current.value === 0) {
|
||||||
|
if (!networkCurrent.value) {
|
||||||
|
message.error('请选择网络组件!');
|
||||||
|
} else {
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
current.value -= current.value;
|
||||||
|
}
|
||||||
|
} else if (current.value === 1) {
|
||||||
|
if (!procotolCurrent.value) {
|
||||||
|
message.error('请选择消息协议!');
|
||||||
|
} else {
|
||||||
|
const resp =
|
||||||
|
props.provider.channel !== 'child-device'
|
||||||
|
? await getConfigView(
|
||||||
|
procotolCurrent.value,
|
||||||
|
ProtocolMapping.get(props.provider.id),
|
||||||
|
)
|
||||||
|
: await getChildConfigView(procotolCurrent.value);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
config.value = resp.result;
|
||||||
|
current.value += current.value;
|
||||||
|
columnsMQTT = [
|
||||||
|
{
|
||||||
|
title: '分组',
|
||||||
|
dataIndex: 'group',
|
||||||
|
key: 'group',
|
||||||
|
ellipsis: true,
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
customRender: (value, row, index) => {
|
||||||
|
const obj = {
|
||||||
|
children: value,
|
||||||
|
attrs: {},
|
||||||
|
};
|
||||||
|
const list = (config && config.routes) || [];
|
||||||
|
const arr = list.filter((res) => {
|
||||||
|
return res.group == row.group;
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
index == 0 ||
|
||||||
|
list[index - 1].group !== row.group
|
||||||
|
) {
|
||||||
|
obj.attrs.rowSpan = arr.length;
|
||||||
|
} else {
|
||||||
|
obj.attrs.rowSpan = 0;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'topic',
|
||||||
|
dataIndex: 'topic',
|
||||||
|
key: 'topic',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '上下行',
|
||||||
|
dataIndex: 'stream',
|
||||||
|
key: 'stream',
|
||||||
|
ellipsis: true,
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
scopedSlots: { customRender: 'stream' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prev = () => {
|
||||||
|
const currentValue = current.value;
|
||||||
|
current.value -= currentValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.data && props.data.id) {
|
||||||
|
if (props.data.provider !== 'child-device') {
|
||||||
|
procotolCurrent.value = props.data.protocol;
|
||||||
|
current.value = 0;
|
||||||
|
networkCurrent.value = props.data.channelId;
|
||||||
|
console.log(11111111,props.provider.id, {
|
||||||
|
include: networkCurrent.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
queryNetworkList(props.provider.id, {
|
||||||
|
include: networkCurrent.value,
|
||||||
|
});
|
||||||
|
procotolCurrent.value = props.data.protocol;
|
||||||
|
steps.value = ['网络组件', '消息协议', '完成'];
|
||||||
|
} else {
|
||||||
|
steps.value = ['消息协议', '完成'];
|
||||||
|
current.value = 1;
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props.provider?.id) {
|
||||||
|
if (props.provider.channel !== 'child-device') {
|
||||||
|
console.log(3333333, props.provider.id, {
|
||||||
|
include: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
queryNetworkList(props.provider.id, {
|
||||||
|
include: '',
|
||||||
|
});
|
||||||
|
steps.value = ['网络组件', '消息协议', '完成'];
|
||||||
|
current.value = 0;
|
||||||
|
} else {
|
||||||
|
console.log(444444,props.provider.id);
|
||||||
|
|
||||||
|
steps.value = ['消息协议', '完成'];
|
||||||
|
current.value = 1;
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.modelValue,
|
||||||
|
// (v) => {
|
||||||
|
// keystoreBase64.value = v;
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// deep: true,
|
||||||
|
// immediate: true,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// watch: {
|
||||||
|
// current(val) {
|
||||||
|
// if (this.provider.channel !== 'child-device') {
|
||||||
|
// this.stepCurrent = val
|
||||||
|
// } else {
|
||||||
|
// this.stepCurrent = val - 1
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.steps-content {
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
.steps-box {
|
||||||
|
min-height: 400px;
|
||||||
|
.card-item {
|
||||||
|
padding-right: 5px;
|
||||||
|
max-height: 480px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.card-last {
|
||||||
|
padding-right: 5px;
|
||||||
|
max-height: 580px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.steps-action {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
margin: 15px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other {
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.item {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-right {
|
||||||
|
padding: 20px;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
|
.config-right-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.config-right-item-title {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-right-item-context {
|
||||||
|
margin: 5px 0;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<TitleComponent data="自定义设备接入"></TitleComponent>
|
||||||
|
<div>
|
||||||
|
<a-row :gutter="[24, 24]">
|
||||||
|
<a-col :span="12" v-for="item in dataSource" :key="item.id">
|
||||||
|
<div class="provider">
|
||||||
|
<div class="box">
|
||||||
|
<div class="left">
|
||||||
|
<div class="images">
|
||||||
|
<img :src="backMap.get(item.id)" />
|
||||||
|
</div>
|
||||||
|
<div class="context">
|
||||||
|
<div class="title">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
<div class="desc">
|
||||||
|
<a-tooltip :title="item.description">
|
||||||
|
{{ item.description || '' }}
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a-button type="primary" @click="click(item)"
|
||||||
|
>接入</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="AccessConfigProvider">
|
||||||
|
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataSource: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['onClick']);
|
||||||
|
|
||||||
|
const backMap = new Map();
|
||||||
|
backMap.set('mqtt-server-gateway', getImage('/access/mqtt.png'));
|
||||||
|
backMap.set('websocket-server', getImage('/access/websocket.png'));
|
||||||
|
backMap.set('modbus-tcp', getImage('/access/modbus.png'));
|
||||||
|
backMap.set('coap-server-gateway', getImage('/access/coap.png'));
|
||||||
|
backMap.set('tcp-server-gateway', getImage('/access/tcp.png'));
|
||||||
|
backMap.set('Ctwing', getImage('/access/ctwing.png'));
|
||||||
|
backMap.set('child-device', getImage('/access/child-device.png'));
|
||||||
|
backMap.set('opc-ua', getImage('/access/opc-ua.png'));
|
||||||
|
backMap.set('http-server-gateway', getImage('/access/http.png'));
|
||||||
|
backMap.set('fixed-media', getImage('/access/video-device.png'));
|
||||||
|
backMap.set('udp-device-gateway', getImage('/access/udp.png'));
|
||||||
|
backMap.set('OneNet', getImage('/access/onenet.png'));
|
||||||
|
backMap.set('gb28181-2016', getImage('/access/gb28181.png'));
|
||||||
|
backMap.set('mqtt-client-gateway', getImage('/access/mqtt-broke.png'));
|
||||||
|
backMap.set('edge-child-device', getImage('/access/child-device.png'));
|
||||||
|
backMap.set('official-edge-gateway', getImage('/access/edge.png'));
|
||||||
|
|
||||||
|
const click = (value: object) => {
|
||||||
|
emit('onClick', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.provider {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
background: url('/public/images/access/background.png') no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 40px;
|
||||||
|
display: block;
|
||||||
|
width: 15%;
|
||||||
|
min-width: 64px;
|
||||||
|
height: 2px;
|
||||||
|
background-image: url('/public/images/access/rectangle.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
// border: 1px #8da1f4 solid;
|
||||||
|
// border-bottom-left-radius: 10%;
|
||||||
|
// border-bottom-right-radius: 10%;
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 24px rgba(#000, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
display: flex;
|
||||||
|
width: calc(100% - 70px);
|
||||||
|
.images {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.context {
|
||||||
|
width: calc(100% - 84px);
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,10 +2,17 @@
|
||||||
<a-button type="primary" @click="handlAdd">新增</a-button>
|
<a-button type="primary" @click="handlAdd">新增</a-button>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="AccessConfigPage">
|
<script lang="ts" setup name="AccessConfigPage">
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const handlAdd = (e: any) => {
|
// const handlAdd = () => {
|
||||||
console.log(111,e);
|
// router.push({
|
||||||
|
// path: '/link/accessConfig/detail/add',
|
||||||
|
// query: {
|
||||||
|
// id: '1610475400026861568',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
const handlAdd = () => {
|
||||||
|
router.push('/link/accessConfig/detail/add');
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -64,16 +64,14 @@ const handleChange = (info: UploadChangeParam) => {
|
||||||
message.success('上传成功!');
|
message.success('上传成功!');
|
||||||
const result = info.file.response?.result;
|
const result = info.file.response?.result;
|
||||||
keystoreBase64.value = result;
|
keystoreBase64.value = result;
|
||||||
console.log(1114, result);
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
emit('change', result);
|
emit('change', result);
|
||||||
emit('update:modelValue', result);
|
emit('update:modelValue', result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const textChange = (val: any) => {
|
const textChange = (val: any) => {
|
||||||
val.name = props.name;
|
emit('change', keystoreBase64.value);
|
||||||
emit('change', val);
|
emit('update:modelValue', keystoreBase64.value);
|
||||||
// emit('update:modelValue', val);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
:label-col="{ span: 8 }"
|
:label-col="{ span: 8 }"
|
||||||
:wrapper-col="{ span: 16 }"
|
:wrapper-col="{ span: 16 }"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@finish="onFinish"
|
|
||||||
:rules="formRules"
|
|
||||||
>
|
>
|
||||||
<a-form-item label="证书标准" name="type">
|
<a-form-item label="证书标准" v-bind="validateInfos.type">
|
||||||
<a-radio-group v-model:value="formData.type">
|
<a-radio-group v-model:value="formData.type">
|
||||||
<a-radio-button
|
<a-radio-button
|
||||||
class="form-radio-button"
|
class="form-radio-button"
|
||||||
|
@ -24,25 +22,29 @@
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="证书名称" name="name">
|
<a-form-item label="证书名称" v-bind="validateInfos.name">
|
||||||
<a-input
|
<a-input
|
||||||
placeholder="请输入证书名称"
|
placeholder="请输入证书名称"
|
||||||
v-model:value="formData.name"
|
v-model:value="formData.name"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="证书文件" name="cert">
|
<a-form-item
|
||||||
|
label="证书文件"
|
||||||
|
v-bind="validateInfos['configs.cert']"
|
||||||
|
>
|
||||||
<CertificateFile
|
<CertificateFile
|
||||||
name="cert"
|
name="cert"
|
||||||
v-model:modelValue="formData.cert"
|
v-model:modelValue="formData.configs.cert"
|
||||||
@change="changeFileValue"
|
|
||||||
placeholder='证书格式以"-----BEGIN CERTIFICATE-----"开头,以"-----END CERTIFICATE-----"结尾"'
|
placeholder='证书格式以"-----BEGIN CERTIFICATE-----"开头,以"-----END CERTIFICATE-----"结尾"'
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="证书私钥" name="key">
|
<a-form-item
|
||||||
|
label="证书私钥"
|
||||||
|
v-bind="validateInfos['configs.key']"
|
||||||
|
>
|
||||||
<CertificateFile
|
<CertificateFile
|
||||||
name="key"
|
name="key"
|
||||||
v-model:modelValue="formData.key"
|
v-model:modelValue="formData.configs.key"
|
||||||
@change="changeFileValue"
|
|
||||||
placeholder='证书私钥格式以"-----BEGIN (RSA|EC) PRIVATE KEY-----"开头,以"-----END(RSA|EC) PRIVATE KEY-----"结尾。'
|
placeholder='证书私钥格式以"-----BEGIN (RSA|EC) PRIVATE KEY-----"开头,以"-----END(RSA|EC) PRIVATE KEY-----"结尾。'
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -61,6 +63,8 @@
|
||||||
class="form-submit"
|
class="form-submit"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@click.prevent="onSubmit"
|
||||||
|
:loading="loading"
|
||||||
>保存</a-button
|
>保存</a-button
|
||||||
>
|
>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -89,7 +93,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="CertificateDetail">
|
<script lang="ts" setup name="CertificateDetail">
|
||||||
import { message } from 'ant-design-vue';
|
import { message, Form } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import CertificateFile from './CertificateFile.vue';
|
import CertificateFile from './CertificateFile.vue';
|
||||||
import type { UploadChangeParam } from 'ant-design-vue';
|
import type { UploadChangeParam } from 'ant-design-vue';
|
||||||
|
@ -101,57 +105,65 @@ import {
|
||||||
} from '@/utils/variable';
|
} from '@/utils/variable';
|
||||||
import { save } from '@/api/link/certificate';
|
import { save } from '@/api/link/certificate';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
|
||||||
|
const fileLoading = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
type: 'common',
|
type: 'common',
|
||||||
name: '',
|
name: '',
|
||||||
|
configs: {
|
||||||
cert: '',
|
cert: '',
|
||||||
key: '',
|
key: '',
|
||||||
// configs: {
|
},
|
||||||
// cert: '',
|
|
||||||
// key: '',
|
|
||||||
// },
|
|
||||||
description: '',
|
description: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const formRules = {
|
const { resetFields, validate, validateInfos } = useForm(
|
||||||
|
formData,
|
||||||
|
reactive({
|
||||||
type: [{ required: true, message: '请选择证书标准', trigger: 'blur' }],
|
type: [{ required: true, message: '请选择证书标准', trigger: 'blur' }],
|
||||||
name: [
|
name: [
|
||||||
{ required: true, message: '请输入证书名称', trigger: 'blur' },
|
{ required: true, message: '请输入证书名称', trigger: 'blur' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
],
|
],
|
||||||
cert: [{ required: true, message: '请输入或上传文件', trigger: 'blur' }],
|
'configs.cert': [
|
||||||
key: [{ required: true, message: '请输入或上传文件', trigger: 'blur' }],
|
{ required: true, message: '请输入或上传文件', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
'configs.key': [
|
||||||
|
{ required: true, message: '请输入或上传文件', trigger: 'blur' },
|
||||||
|
],
|
||||||
description: [{ max: 200, message: '最多可输入200个字符' }],
|
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||||
};
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const onFinish = async (values: any) => {
|
const onSubmit = () => {
|
||||||
values.configs = {
|
validate()
|
||||||
cert: formData.cert,
|
.then(async (res) => {
|
||||||
key: formData.key,
|
const params = toRaw(formData);
|
||||||
};
|
loading.value = true;
|
||||||
delete values.cert;
|
const response = await save(params);
|
||||||
delete values.key;
|
|
||||||
|
|
||||||
const response = await save(values)
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
message.success('操作成功')
|
message.success('操作成功');
|
||||||
|
router.push('/link/certificate');
|
||||||
}
|
}
|
||||||
|
loading.value = false;
|
||||||
};
|
})
|
||||||
|
.catch((err) => {
|
||||||
const changeFileValue = (v: any) => {
|
loading.value = false;
|
||||||
formData[v.name] = v.data;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (info: UploadChangeParam) => {
|
const handleChange = (info: UploadChangeParam) => {
|
||||||
loading.value = true;
|
fileLoading.value = true;
|
||||||
if (info.file.status === 'done') {
|
if (info.file.status === 'done') {
|
||||||
message.success('上传成功!');
|
message.success('上传成功!');
|
||||||
const result = info.file.response?.result;
|
const result = info.file.response?.result;
|
||||||
formData.cert = result;
|
formData.configs.cert = result;
|
||||||
loading.value = false;
|
fileLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="CertificatePage">
|
<script lang="ts" setup name="CertificatePage">
|
||||||
|
|
||||||
const handlAdd = (e: any) => {
|
const router = useRouter();
|
||||||
console.log(111,e);
|
|
||||||
|
|
||||||
|
const handlAdd = () => {
|
||||||
|
router.push('/link/certificate/detail/add');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
<template>
|
||||||
|
<div class="api-does-container">
|
||||||
|
<div class="top">
|
||||||
|
<h5>{{ selectApi.summary }}</h5>
|
||||||
|
<div class="input">
|
||||||
|
<InputCard :value="selectApi.method" />
|
||||||
|
<a-input :value="selectApi?.url" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="display: flex; justify-content: space-between">
|
||||||
|
<span class="label">请求数据类型</span>
|
||||||
|
<span class="value">{{
|
||||||
|
getContent(selectApi.requestBody) ||
|
||||||
|
'application/x-www-form-urlencoded'
|
||||||
|
}}</span>
|
||||||
|
<span class="label">响应数据类型</span>
|
||||||
|
<span class="value">{{ `["/"]` }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="api-card">
|
||||||
|
<h5>请求参数</h5>
|
||||||
|
<div class="content">
|
||||||
|
<JTable
|
||||||
|
:columns="columns.request"
|
||||||
|
:dataSource="selectApi.parameters"
|
||||||
|
noPagination
|
||||||
|
model="TABLE"
|
||||||
|
>
|
||||||
|
<template #required="slotProps">
|
||||||
|
<span>{{ slotProps.row.required + '' }}</span>
|
||||||
|
</template>
|
||||||
|
<template #type="slotProps">
|
||||||
|
<span>{{ slotProps.row.schema.type }}</span>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { apiDetailsType } from '../index';
|
||||||
|
import InputCard from './InputCard.vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
selectApi: {
|
||||||
|
type: Object as PropType<apiDetailsType>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { selectApi } = toRefs(props);
|
||||||
|
|
||||||
|
const columns = {
|
||||||
|
request: [
|
||||||
|
{
|
||||||
|
title: '参数名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '参数说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求类型',
|
||||||
|
dataIndex: 'in',
|
||||||
|
key: 'in',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否必须',
|
||||||
|
dataIndex: 'required',
|
||||||
|
key: 'required',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '参数类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(selectApi.value);
|
||||||
|
const getContent = (data: any) => {
|
||||||
|
if (!data) return '';
|
||||||
|
return Object.keys(data.content)[0];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.api-does-container {
|
||||||
|
.top {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-card {
|
||||||
|
h5 {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #1d39c4;
|
||||||
|
border-radius: 0 3px 3px 0;
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
|
||||||
|
:deep(.jtable-body) {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.jtable-body-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div class="api-test-container">
|
||||||
|
<div class="top">
|
||||||
|
<h5>{{ selectApi.summary }}</h5>
|
||||||
|
<div class="input">
|
||||||
|
<InputCard :value="selectApi.method" />
|
||||||
|
<a-input :value="selectApi?.url" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { apiDetailsType } from '../index';
|
||||||
|
import InputCard from './InputCard.vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
selectApi: {
|
||||||
|
type: Object as PropType<apiDetailsType>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { selectApi } = toRefs(props);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.api-test-container {
|
||||||
|
.top {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div class="choose-api-container">
|
||||||
|
<JTable
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="props.tableData"
|
||||||
|
:rowSelection="rowSelection"
|
||||||
|
noPagination
|
||||||
|
model="TABLE"
|
||||||
|
>
|
||||||
|
<template #url="slotProps">
|
||||||
|
<span
|
||||||
|
style="color: #1d39c4; cursor: pointer"
|
||||||
|
@click="jump(slotProps.row)"
|
||||||
|
>{{ slotProps.row.url }}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
|
||||||
|
<a-button type="primary">保存</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TableProps } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:clickApi'])
|
||||||
|
const props = defineProps({
|
||||||
|
tableData: Array,
|
||||||
|
clickApi: Object
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'API',
|
||||||
|
dataIndex: 'url',
|
||||||
|
key: 'url',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'summary',
|
||||||
|
key: 'summary',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const rowSelection: TableProps['rowSelection'] = {
|
||||||
|
// onChange: (selectedRowKeys, selectedRows) => {
|
||||||
|
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
|
const jump = (row:object) => {
|
||||||
|
emits('update:clickApi',row)
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.choose-api-container {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
:deep(.jtable-body-header) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<span class="input-card-container" :class="props.value">
|
||||||
|
{{ props.value?.toLocaleUpperCase() }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
value: String,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.input-card-container {
|
||||||
|
padding: 4px 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&.get {
|
||||||
|
background-color: #1890ff;
|
||||||
|
}
|
||||||
|
&.put {
|
||||||
|
background-color: #fa8c16;
|
||||||
|
}
|
||||||
|
&.post {
|
||||||
|
background-color: #52c41a;
|
||||||
|
}
|
||||||
|
&.delete {
|
||||||
|
background-color: #f5222d;
|
||||||
|
}
|
||||||
|
&.patch {
|
||||||
|
background-color: #a0d911;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,51 +15,62 @@
|
||||||
import { TreeProps } from 'ant-design-vue';
|
import { TreeProps } from 'ant-design-vue';
|
||||||
|
|
||||||
import { getTreeOne_api, getTreeTwo_api } from '@/api/system/apiPage';
|
import { getTreeOne_api, getTreeTwo_api } from '@/api/system/apiPage';
|
||||||
|
import { treeNodeTpye } from '../index';
|
||||||
|
|
||||||
type treeNodeTpye = {
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
children?: treeNodeTpye[];
|
|
||||||
};
|
|
||||||
const emits = defineEmits(['select']);
|
const emits = defineEmits(['select']);
|
||||||
|
|
||||||
const treeData: TreeProps['treeData'] = ref([]);
|
const treeData = ref<TreeProps['treeData']>([]);
|
||||||
|
|
||||||
const getTreeData = () => {
|
const getTreeData = () => {
|
||||||
let tree: treeNodeTpye[] = [];
|
let tree: treeNodeTpye[] = [];
|
||||||
getTreeOne_api().then((resp) => {
|
getTreeOne_api().then((resp: any) => {
|
||||||
tree = resp.urls.map((item) => ({
|
tree = resp.urls.map((item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
key: item.url,
|
key: item.url,
|
||||||
}));
|
}));
|
||||||
const allPromise = tree.map((item) => getTreeTwo_api(item.name));
|
const allPromise = tree.map((item) => getTreeTwo_api(item.name));
|
||||||
Promise.all(allPromise).then((values) => {
|
Promise.all(allPromise).then((values) => {
|
||||||
values.forEach((item, i) => {
|
values.forEach((item: any, i) => {
|
||||||
tree[i].children = combData(item.paths);
|
tree[i].children = combData(item?.paths);
|
||||||
});
|
});
|
||||||
console.log(tree);
|
treeData.value = tree;
|
||||||
treeData.value = tree
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const clickSelectItem = (key, { node }) => {
|
const clickSelectItem: TreeProps['onSelect'] = (key, node: any) => {
|
||||||
emits('select', node);
|
emits('select', node.node.dataRef);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTreeData();
|
getTreeData();
|
||||||
});
|
});
|
||||||
|
|
||||||
const combData = (dataSource: object): object[] => {
|
const combData = (dataSource: object) => {
|
||||||
const apiList: object[] = [];
|
const apiList: treeNodeTpye[] = [];
|
||||||
const keys = Object.keys(dataSource);
|
const keys = Object.keys(dataSource);
|
||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const method = Object.keys(dataSource[key] || {})[0];
|
const method = Object.keys(dataSource[key] || {})[0];
|
||||||
const name = dataSource[key][method].tags[0];
|
const name = dataSource[key][method].tags[0];
|
||||||
let apiObj = apiList.find((item) => item.name === name);
|
let apiObj: treeNodeTpye | undefined = apiList.find(
|
||||||
if (!apiObj) {
|
(item) => item.name === name,
|
||||||
apiObj = { name, link: key, methods: dataSource[key], key };
|
);
|
||||||
|
if (apiObj) {
|
||||||
|
apiObj.apiList?.push({
|
||||||
|
url: key,
|
||||||
|
method: dataSource[key],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
apiObj = {
|
||||||
|
name,
|
||||||
|
key: name,
|
||||||
|
apiList: [
|
||||||
|
{
|
||||||
|
url: key,
|
||||||
|
method: dataSource[key],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
apiList.push(apiObj);
|
apiList.push(apiObj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -68,4 +79,17 @@ const combData = (dataSource: object): object[] => {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less">
|
||||||
|
.left-tree-container {
|
||||||
|
border-right: 1px solid #e9e9e9;
|
||||||
|
height: calc(100vh - 150px);
|
||||||
|
overflow-y: auto;
|
||||||
|
.ant-tree-list {
|
||||||
|
.ant-tree-list-holder-inner {
|
||||||
|
.ant-tree-switcher-noop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
export type treeNodeTpye = {
|
||||||
|
name: string;
|
||||||
|
key: string;
|
||||||
|
link?: string;
|
||||||
|
apiList?: object[];
|
||||||
|
children?: treeNodeTpye[];
|
||||||
|
};
|
||||||
|
export type methodType = {
|
||||||
|
[key: string]: object
|
||||||
|
}
|
||||||
|
export type apiObjType = {
|
||||||
|
url: string,
|
||||||
|
method: methodType
|
||||||
|
}
|
||||||
|
|
||||||
|
export type apiDetailsType = {
|
||||||
|
url: string;
|
||||||
|
method: string;
|
||||||
|
summary: string;
|
||||||
|
parameters: [];
|
||||||
|
requestBody?: any;
|
||||||
|
}
|
|
@ -1,11 +1,74 @@
|
||||||
<template>
|
<template>
|
||||||
<a-card class="api-page-container">
|
<a-card class="api-page-container">
|
||||||
<LeftTree />
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="5">
|
||||||
|
<LeftTree @select="treeSelect" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="19">
|
||||||
|
<ChooseApi
|
||||||
|
v-show="!selectedApi.url"
|
||||||
|
v-model:click-api="selectedApi"
|
||||||
|
:table-data="tableData"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="api-details"
|
||||||
|
v-show="selectedApi.url && tableData.length > 0"
|
||||||
|
>
|
||||||
|
<a-button @click="selectedApi = initSelectedApi"
|
||||||
|
>返回</a-button
|
||||||
|
>
|
||||||
|
<a-tabs v-model:activeKey="activeKey" type="card">
|
||||||
|
<a-tab-pane key="does" tab="文档">
|
||||||
|
<ApiDoes :select-api="selectedApi" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="test" tab="调试">
|
||||||
|
<ApiTest :select-api="selectedApi" />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts" name="apiPage">
|
||||||
|
import { treeNodeTpye, apiObjType, apiDetailsType } from './index';
|
||||||
import LeftTree from './components/LeftTree.vue';
|
import LeftTree from './components/LeftTree.vue';
|
||||||
|
import ChooseApi from './components/ChooseApi.vue';
|
||||||
|
import ApiDoes from './components/ApiDoes.vue';
|
||||||
|
import ApiTest from './components/ApiTest.vue';
|
||||||
|
|
||||||
|
const tableData = ref([]);
|
||||||
|
const treeSelect = (node: treeNodeTpye) => {
|
||||||
|
if (!node.apiList) return;
|
||||||
|
const apiList: apiObjType[] = node.apiList as apiObjType[];
|
||||||
|
const table: any = [];
|
||||||
|
// 将对象形式的数据转换为表格需要的形式
|
||||||
|
apiList?.forEach((apiItem) => {
|
||||||
|
const { method, url } = apiItem;
|
||||||
|
for (const key in method) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(method, key)) {
|
||||||
|
table.push({
|
||||||
|
...method[key],
|
||||||
|
url,
|
||||||
|
method: key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tableData.value = table;
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeKey = ref('does');
|
||||||
|
const initSelectedApi = {
|
||||||
|
url: '',
|
||||||
|
method: '',
|
||||||
|
summary: '',
|
||||||
|
};
|
||||||
|
const selectedApi = ref<apiDetailsType>(initSelectedApi);
|
||||||
|
|
||||||
|
watch(tableData, () => (selectedApi.value = initSelectedApi));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
Loading…
Reference in New Issue