feat: 应用管理
This commit is contained in:
parent
063c6bbe8d
commit
1740f85d2d
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,9 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
|
||||||
|
// 获取应用管理列表
|
||||||
|
export const getApplyList_api = (data: any) => server.post(`/application/_query/`, data)
|
||||||
|
// 修改应用状态
|
||||||
|
export const changeApplyStatus_api = (id:string,data: any) => server.put(`/application/${id}`, data)
|
||||||
|
// 删除应用
|
||||||
|
export const delApply_api = (id:string) => server.remove(`/application/${id}`)
|
|
@ -0,0 +1,211 @@
|
||||||
|
<template>
|
||||||
|
<div class="does-container">
|
||||||
|
<div v-show="props.type === 'internal-standalone'">
|
||||||
|
<h1>1.概述</h1>
|
||||||
|
<div>
|
||||||
|
内部独立应用适用于将<span>官方开发</span>的其他应用与<span
|
||||||
|
>物联网平台相互集成</span
|
||||||
|
>
|
||||||
|
,例如将可视化平台集成至物联网平台,或者将物联网平台集成至可视化平台。以实现多处访问、集中管控的业务场景。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
内部独立应用的<span>后端服务</span>相互<span>独立运行</span>,互不影响。
|
||||||
|
</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image width="100%" :src="img1" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>2.接入方式说明</h1>
|
||||||
|
<div>1、页面集成</div>
|
||||||
|
<div>
|
||||||
|
集成其他应用的<span>前端页面</span>至物联网平台中。为实现应用与物联网平台数据互联互通,
|
||||||
|
<span>通常还需要配置API服务</span>。
|
||||||
|
</div>
|
||||||
|
<div>2、API客户端</div>
|
||||||
|
<div>
|
||||||
|
<span>物联网平台</span>请求<span>其他应用</span>
|
||||||
|
的接口,以实现将物联网平台集成至其他应用系统。如需实现<span
|
||||||
|
>其他应用</span
|
||||||
|
>
|
||||||
|
登录后可以访问<span>物联网平台</span>页面,<span>还需要配置单点登录</span>。
|
||||||
|
</div>
|
||||||
|
<div>3、API服务</div>
|
||||||
|
<div>
|
||||||
|
<span>外部应用</span
|
||||||
|
>请求<span>物联网平台</span>的接口,实现物联网平台的服务调用能力,
|
||||||
|
<span>通常还需要配置页面集成</span>。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
配置API服务后,系统将<span>自动创建</span>对应的<span>“第三方应用”用户</span>。用户的
|
||||||
|
<span>账号/密码</span>分别对应appid/secureKey。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
第三方用户<span>可调用的API服务</span>在其应用管理卡片的<span
|
||||||
|
>其他-{'>'}赋权</span
|
||||||
|
>
|
||||||
|
页面,进行<span>自定义配置</span>。
|
||||||
|
</div>
|
||||||
|
<div>4、单点登录</div>
|
||||||
|
<div>通过<span>第三方平台账号</span>登录到物联网平台。</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="props.type === 'internal-integrated'">
|
||||||
|
<h1>1.概述</h1>
|
||||||
|
<div>
|
||||||
|
内部集成应用适用于将<span>官方开发</span>的其他应用与<span
|
||||||
|
>物联网平台相互集成</span
|
||||||
|
>
|
||||||
|
,例如将可视化平台集成至物联网平台,或者将物联网平台集成至可视化平台。以实现多处访问、集中管控的业务场景。
|
||||||
|
</div>
|
||||||
|
<div>内部独立应用的<span>后端服务在同一个环境运行</span>。</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image width="100%" :src="img2" />
|
||||||
|
</div>
|
||||||
|
<h1>2.接入方式说明</h1>
|
||||||
|
<div>1、页面集成</div>
|
||||||
|
<div>
|
||||||
|
集成其他应用的<span>前端页面</span>
|
||||||
|
至物联网平台中。集成后系统顶部将新增对应的应用管理菜单
|
||||||
|
</div>
|
||||||
|
<div>2、API客户端</div>
|
||||||
|
<div>
|
||||||
|
<span>物联网平台</span
|
||||||
|
>去请求其他应用的接口,以实现将物联网平台集成至其他应用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="props.type === 'dingtalk-ent-app'">
|
||||||
|
<div class="url">
|
||||||
|
钉钉开放平台:
|
||||||
|
<a
|
||||||
|
href="https://open-dev.dingtalk.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
https://open-dev.dingtalk.com
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h1>1.概述</h1>
|
||||||
|
<div>钉钉企业内部应用适用于通过钉钉登录<span>物联网平台</span></div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image width="100%" :src="img4" />
|
||||||
|
</div>
|
||||||
|
<h1>2.接入方式说明</h1>
|
||||||
|
<div>1、单点登录</div>
|
||||||
|
<div>通过钉钉账号登录到物联网平台。</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="props.type === 'wechat-webapp'">
|
||||||
|
<div class="url">
|
||||||
|
微信开放平台:
|
||||||
|
<a
|
||||||
|
href="https://open.weixin.qq.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
https://open.weixin.qq.com
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h1>1.概述</h1>
|
||||||
|
<div>微信网站应用适用于通过微信授权登录<span>物联网平台</span></div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image width="100%" :src="img3" />
|
||||||
|
</div>
|
||||||
|
<h1>2.接入方式说明</h1>
|
||||||
|
<div>1、单点登录</div>
|
||||||
|
<div>通过微信账号登录到物联网平台。</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="props.type === 'third-party'">
|
||||||
|
<h1>1. 概述</h1>
|
||||||
|
<div>
|
||||||
|
第三方应用适用于<span>第三方应用</span>与<span
|
||||||
|
>物联网平台相互集成</span
|
||||||
|
>
|
||||||
|
。例如将公司业务管理系统集成至物联网平台,或者将物联网平台集成至业务管理系统。以实现多处访问、集中管控的业务场景。
|
||||||
|
</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image width="100%" :src="img5" />
|
||||||
|
</div>
|
||||||
|
<h1>2.接入方式说明</h1>
|
||||||
|
<div>1、页面集成</div>
|
||||||
|
<div>
|
||||||
|
集成其他应用的<span>前端页面</span>至物联网平台中。为实现应用与物联网平台数据互联互通,
|
||||||
|
<span>还需要配置API服务</span>。
|
||||||
|
</div>
|
||||||
|
<div>2、API客户端</div>
|
||||||
|
<div>
|
||||||
|
<span>物联网平台</span>请求<span>第三方应用</span>
|
||||||
|
的接口,以实现将物联网平台集成至其他应用。如需实现<span>第三方应用</span>登录后可以访问
|
||||||
|
<span>物联网平台</span>页面,<span>还需要配置单点登录</span>。
|
||||||
|
</div>
|
||||||
|
<div>3、API服务</div>
|
||||||
|
<div>
|
||||||
|
<span>第三方应用</span
|
||||||
|
>通过API服务配置,请求物联网平台接口,实现<span
|
||||||
|
>物联网平台</span
|
||||||
|
>
|
||||||
|
的服务调用能力,<span>通常还需要配置页面集成</span>。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
配置API服务后,系统将<span>自动创建</span>对应的<span>“第三方应用”用户</span>。用户的
|
||||||
|
<span>账号/密码</span>分别对应appid/secureKey。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
第三方用户<span>可调用的API服务</span>在其应用管理卡片的<span
|
||||||
|
>其他-{'>'}赋权</span
|
||||||
|
>
|
||||||
|
页面,进行<span>自定义配置</span>。
|
||||||
|
</div>
|
||||||
|
<div>4、单点登录</div>
|
||||||
|
<div>通过<span>第三方平台账号</span>登录到物联网平台。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import type { applyType } from '../typing';
|
||||||
|
const props = defineProps<{
|
||||||
|
type: applyType;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const img1 = getImage('/apply/1.png');
|
||||||
|
const img2 = getImage('/apply/2.png');
|
||||||
|
const img3 = getImage('/apply/3.png');
|
||||||
|
const img4 = getImage('/apply/4.png');
|
||||||
|
const img5 = getImage('/apply/5.png');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.does-container {
|
||||||
|
padding: 24px;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: rgba(#000, 0.8);
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
|
||||||
|
.url {
|
||||||
|
padding: 8px 16px;
|
||||||
|
color: #2f54eb;
|
||||||
|
background-color: rgba(#a7bdf7, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 16px 0;
|
||||||
|
color: rgba(#000, 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 6px 0;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<div class="form-label-container">
|
||||||
|
<span class="text">{{ props.text }}</span>
|
||||||
|
<span class="required">*</span>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ props.tooltip }}</template>
|
||||||
|
<AIcon type="QuestionCircleOutlined" style="color: #00000073;cursor: inherit;" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{
|
||||||
|
text: string;
|
||||||
|
tooltip: string;
|
||||||
|
required?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.form-label-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.required {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: SimSun, sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,505 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<a-card class="save-container">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form.data"
|
||||||
|
layout="vertical"
|
||||||
|
class="form"
|
||||||
|
>
|
||||||
|
<a-form-item label="名称" name="name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="应用" name="provider">
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="form.data.provider"
|
||||||
|
class="radio-container"
|
||||||
|
@change="form.data.integrationModes = []"
|
||||||
|
>
|
||||||
|
<a-radio-button value="internal-standalone">
|
||||||
|
<div>
|
||||||
|
<a-image
|
||||||
|
:preview="false"
|
||||||
|
:src="
|
||||||
|
getImage('/apply/provider1.png')
|
||||||
|
"
|
||||||
|
width="64px"
|
||||||
|
height="64px"
|
||||||
|
/>
|
||||||
|
<p>内部独立应用</p>
|
||||||
|
</div>
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="internal-integrated">
|
||||||
|
<div>
|
||||||
|
<a-image
|
||||||
|
:preview="false"
|
||||||
|
:src="
|
||||||
|
getImage('/apply/provider2.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<p>内部集成应用</p>
|
||||||
|
</div>
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="wechat-webapp">
|
||||||
|
<div>
|
||||||
|
<a-image
|
||||||
|
:preview="false"
|
||||||
|
:src="
|
||||||
|
getImage('/apply/provider3.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<p>微信网站应用</p>
|
||||||
|
</div>
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="dingtalk-ent-app">
|
||||||
|
<div>
|
||||||
|
<a-image
|
||||||
|
:preview="false"
|
||||||
|
:src="
|
||||||
|
getImage('/apply/provider4.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<p>钉钉企业内部应用</p>
|
||||||
|
</div>
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="third-party">
|
||||||
|
<div>
|
||||||
|
<a-image
|
||||||
|
:preview="false"
|
||||||
|
:src="
|
||||||
|
getImage('/apply/provider5.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<p>第三方应用</p>
|
||||||
|
</div>
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="接入方式" name="integrationModes">
|
||||||
|
<a-checkbox-group
|
||||||
|
v-model:value="form.data.integrationModes"
|
||||||
|
:options="joinOptions"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-collapse
|
||||||
|
v-model:activeKey="form.integrationModesISO"
|
||||||
|
>
|
||||||
|
<a-collapse-panel
|
||||||
|
key="page"
|
||||||
|
v-show="
|
||||||
|
form.data.integrationModes.includes('page')
|
||||||
|
"
|
||||||
|
header="页面集成"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:name="['page', 'baseUrl']"
|
||||||
|
class="resetLabel"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="接入地址"
|
||||||
|
required
|
||||||
|
tooltip="填写访问其它平台的地址"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.data.page.baseUrl"
|
||||||
|
placeholder="请输入接入地址"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="路由方式"
|
||||||
|
:name="['page', 'routeType']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.data.page.routeType"
|
||||||
|
style="width: 120px"
|
||||||
|
>
|
||||||
|
<a-select-option value="hash"
|
||||||
|
>hash</a-select-option
|
||||||
|
>
|
||||||
|
<a-select-option value="history"
|
||||||
|
>history</a-select-option
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel
|
||||||
|
key="apiClient"
|
||||||
|
v-show="
|
||||||
|
form.data.integrationModes.includes(
|
||||||
|
'apiClient',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
header="API客户端"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
class="resetLabel"
|
||||||
|
:name="['apiClient', 'baseUrl']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="接口地址"
|
||||||
|
required
|
||||||
|
tooltip="访问Api服务的地址"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.baseUrl
|
||||||
|
"
|
||||||
|
placeholder="请输入接入地址"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="resetLabel"
|
||||||
|
:name="[
|
||||||
|
'apiClient',
|
||||||
|
'authConfig',
|
||||||
|
'oauth2',
|
||||||
|
'authorizationUrl',
|
||||||
|
]"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="授权地址"
|
||||||
|
required
|
||||||
|
tooltip="认证授权地址"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.authConfig
|
||||||
|
.oauth2.authorizationUrl
|
||||||
|
"
|
||||||
|
placeholder="请输入授权地址"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="resetLabel"
|
||||||
|
:name="[
|
||||||
|
'apiClient',
|
||||||
|
'authConfig',
|
||||||
|
'oauth2',
|
||||||
|
'tokenUrl',
|
||||||
|
]"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="token地址"
|
||||||
|
required
|
||||||
|
tooltip="设置token令牌的地址"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.authConfig
|
||||||
|
.oauth2.tokenUrl
|
||||||
|
"
|
||||||
|
placeholder="请输入token地址"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="回调地址"
|
||||||
|
:name="[
|
||||||
|
'apiClient',
|
||||||
|
'authConfig',
|
||||||
|
'oauth2',
|
||||||
|
'redirectUri',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="回调地址"
|
||||||
|
tooltip="授权完成后跳转到具体页面的回调地址"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.authConfig
|
||||||
|
.oauth2.redirectUri
|
||||||
|
"
|
||||||
|
placeholder="请输入回调地址"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="resetLabel"
|
||||||
|
:name="[
|
||||||
|
'apiClient',
|
||||||
|
'authConfig',
|
||||||
|
'oauth2',
|
||||||
|
'clientId',
|
||||||
|
]"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="appId"
|
||||||
|
required
|
||||||
|
tooltip="第三方应用唯一标识"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.authConfig
|
||||||
|
.oauth2.clientId
|
||||||
|
"
|
||||||
|
placeholder="请输入appId"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="resetLabel"
|
||||||
|
:name="[
|
||||||
|
'apiClient',
|
||||||
|
'authConfig',
|
||||||
|
'oauth2',
|
||||||
|
'clientSecret',
|
||||||
|
]"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<FormLabel
|
||||||
|
text="appKey"
|
||||||
|
required
|
||||||
|
tooltip="第三方应用唯一标识的密钥"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
form.data.apiClient.authConfig
|
||||||
|
.oauth2.clientSecret
|
||||||
|
"
|
||||||
|
placeholder="请输入appKey"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="请求头"> </a-form-item>
|
||||||
|
<a-form-item label="参数"> </a-form-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel
|
||||||
|
key="apiServer"
|
||||||
|
v-show="
|
||||||
|
form.data.integrationModes.includes(
|
||||||
|
'apiServer',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
header="API服务"
|
||||||
|
>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel
|
||||||
|
key="ssoClient"
|
||||||
|
v-show="
|
||||||
|
form.data.integrationModes.includes(
|
||||||
|
'ssoClient',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
header="单点登录"
|
||||||
|
>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
<a-form-item label="说明" name="description">
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="form.data.description"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
showCount
|
||||||
|
:maxlength="200"
|
||||||
|
:rows="5"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<a-button v-if="!routeQuery.view">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10"><Does :type="form.data.provider" /></a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Does from './components/Does.vue';
|
||||||
|
import FormLabel from './components/FormLabel.vue';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import type { applyType, formType } from './typing';
|
||||||
|
const routeQuery = useRoute().query;
|
||||||
|
|
||||||
|
const initForm: formType = {
|
||||||
|
name: '',
|
||||||
|
provider: 'internal-standalone',
|
||||||
|
integrationModes: [],
|
||||||
|
config: '',
|
||||||
|
description: '',
|
||||||
|
page: {
|
||||||
|
baseUrl: '',
|
||||||
|
routeType: 'hash',
|
||||||
|
},
|
||||||
|
apiClient: {
|
||||||
|
baseUrl: '',
|
||||||
|
authConfig: {
|
||||||
|
type: '',
|
||||||
|
oauth2: {
|
||||||
|
authorizationUrl: '',
|
||||||
|
tokenUrl: '',
|
||||||
|
redirectUri: '',
|
||||||
|
clientId: '',
|
||||||
|
clientSecret: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const form = reactive({
|
||||||
|
data: { ...initForm },
|
||||||
|
integrationModesISO: [] as string[],
|
||||||
|
});
|
||||||
|
const joinOptions = computed(() => {
|
||||||
|
if (form.data.provider === 'internal-standalone')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '页面集成',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'API客户端',
|
||||||
|
value: 'apiClient',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'API服务',
|
||||||
|
value: 'apiServer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '单点登录',
|
||||||
|
value: 'ssoClient',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
else if (form.data.provider === 'internal-integrated')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '页面集成',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'API客户端',
|
||||||
|
value: 'apiClient',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
else if (form.data.provider === 'wechat-webapp')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '单点登录',
|
||||||
|
value: 'ssoClient',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
else if (form.data.provider === 'dingtalk-ent-app')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '单点登录',
|
||||||
|
value: 'ssoClient',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
else if (form.data.provider === 'third-party')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '页面集成',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'API客户端',
|
||||||
|
value: 'apiClient',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'API服务',
|
||||||
|
value: 'apiServer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '单点登录',
|
||||||
|
value: 'ssoClient',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.save-container {
|
||||||
|
.form {
|
||||||
|
.ant-form-item {
|
||||||
|
&.resetLabel {
|
||||||
|
:deep(.ant-form-item-required) {
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.radio-container {
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
padding: 0 15px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
:deep(.ant-image) {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,30 @@
|
||||||
|
export type applyType = 'internal-standalone'
|
||||||
|
| 'wechat-webapp'
|
||||||
|
| 'internal-integrated'
|
||||||
|
| 'dingtalk-ent-app'
|
||||||
|
| 'third-party'
|
||||||
|
|
||||||
|
export type formType = {
|
||||||
|
name: string;
|
||||||
|
provider: applyType;
|
||||||
|
integrationModes: string[];
|
||||||
|
config: string;
|
||||||
|
description: string;
|
||||||
|
page: {
|
||||||
|
baseUrl:string,
|
||||||
|
routeType:'hash' | 'history'
|
||||||
|
},
|
||||||
|
apiClient: {
|
||||||
|
baseUrl: string,
|
||||||
|
authConfig: {
|
||||||
|
type:string,
|
||||||
|
oauth2 :{
|
||||||
|
authorizationUrl:string,
|
||||||
|
tokenUrl: string,
|
||||||
|
redirectUri:string,
|
||||||
|
clientId:string,
|
||||||
|
clientSecret:string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,399 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container> 应用管理
|
<page-container class="apply-container">
|
||||||
|
<div class="apply-container">
|
||||||
|
<Search :columns="columns" @search="search" />
|
||||||
|
|
||||||
|
<JTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="getApplyList_api"
|
||||||
|
:defaultParams="{
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
}"
|
||||||
|
:params="params"
|
||||||
|
:gridColumn="3"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<PermissionButton
|
||||||
|
:uhasPermission="`${permission}:add`"
|
||||||
|
type="primary"
|
||||||
|
@click="() => table.toSave()"
|
||||||
|
>
|
||||||
|
<AIcon type="PlusOutlined" />新增
|
||||||
|
</PermissionButton>
|
||||||
|
<p style="margin: 0 0 0 30px; color: #0000008c">
|
||||||
|
<AIcon
|
||||||
|
type="ExclamationCircleOutlined"
|
||||||
|
style="margin-right: 12px"
|
||||||
|
/>
|
||||||
|
应用管理将多个应用系统的登录简化为一次登录,实现多处访问、集中管控的业务场景。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox
|
||||||
|
:value="slotProps"
|
||||||
|
:actions="table.getActions(slotProps, 'card')"
|
||||||
|
v-bind="slotProps"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
enabled: 'success',
|
||||||
|
disabled: 'error',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #img>
|
||||||
|
<slot name="img">
|
||||||
|
<img :src="getImage('/apply.png')" />
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<h3 class="card-item-content-title">
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</h3>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
类型
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
table.getTypeLabel(
|
||||||
|
slotProps.provider,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
说明
|
||||||
|
</div>
|
||||||
|
<div>{{ slotProps.description }}</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
<template #actions="item">
|
||||||
|
<a-tooltip
|
||||||
|
v-bind="item.tooltip"
|
||||||
|
:title="item.disabled && item.tooltip.title"
|
||||||
|
>
|
||||||
|
<a-dropdown
|
||||||
|
placement="bottomRight"
|
||||||
|
v-if="item.key === 'others'"
|
||||||
|
>
|
||||||
|
<a-button>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item.text }}</span>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item
|
||||||
|
v-for="(o, i) in item.children"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click="o.onClick"
|
||||||
|
>
|
||||||
|
<AIcon :type="o.icon" />
|
||||||
|
<span>{{ o.text }}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
<PermissionButton
|
||||||
|
v-else
|
||||||
|
:uhasPermission="item.permission"
|
||||||
|
:tooltip="item.tooltip"
|
||||||
|
:pop-confirm="item.popConfirm"
|
||||||
|
@click="item.onClick"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span v-if="item.key !== 'delete'">{{
|
||||||
|
item.text
|
||||||
|
}}</span>
|
||||||
|
</PermissionButton>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #provider="slotProps">
|
||||||
|
{{ table.getTypeLabel(slotProps.provider) }}
|
||||||
|
</template>
|
||||||
|
<template #status="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.state.value"
|
||||||
|
:text="slotProps.state.text"
|
||||||
|
:statusNames="{
|
||||||
|
enabled: 'success',
|
||||||
|
disabled: 'error',
|
||||||
|
}"
|
||||||
|
></BadgeStatus>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<PermissionButton
|
||||||
|
v-for="i in table.getActions(slotProps, 'table')"
|
||||||
|
:uhasPermission="i.permission"
|
||||||
|
type="link"
|
||||||
|
:tooltip="i.tooltip"
|
||||||
|
:pop-confirm="i.popConfirm"
|
||||||
|
@click="i.onClick"
|
||||||
|
:disabled="i.disabled"
|
||||||
|
>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts" name="Apply">
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
|
import {
|
||||||
|
getApplyList_api,
|
||||||
|
changeApplyStatus_api,
|
||||||
|
delApply_api,
|
||||||
|
} from '@/api/system/apply';
|
||||||
|
import { ActionsType } from '@/components/Table';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
<style scoped></style>
|
const menuStory = useMenuStore();
|
||||||
|
const permission = 'system/User';
|
||||||
|
const typeOptions = [
|
||||||
|
{
|
||||||
|
label: '内部独立应用',
|
||||||
|
value: 'internal-standalone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '微信网站应用',
|
||||||
|
value: 'wechat-webapp',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '内部集成应用',
|
||||||
|
value: 'internal-integrated',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '钉钉企业内部应用',
|
||||||
|
value: 'dingtalk-ent-app',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '第三方应用',
|
||||||
|
value: 'third-party',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'provider',
|
||||||
|
key: 'provider',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: typeOptions,
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
rename: 'status',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '正常',
|
||||||
|
value: 'enabled',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
value: 'disabled',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const params = ref({});
|
||||||
|
const search = (newParams: any) => (params.value = {...newParams});
|
||||||
|
|
||||||
|
const tableRef = ref();
|
||||||
|
const table = {
|
||||||
|
refresh: () => {
|
||||||
|
tableRef.value.reload();
|
||||||
|
},
|
||||||
|
toSave: (id?: string, view = false) => {
|
||||||
|
if (id) menuStory.jumpPage('system/Apply/Save', {}, { id, view });
|
||||||
|
else menuStory.jumpPage('system/Apply/Save');
|
||||||
|
},
|
||||||
|
changeStatus: (row: any) => {
|
||||||
|
const state = row.state.value === 'enabled' ? 'disabled' : 'enabled';
|
||||||
|
changeApplyStatus_api(row.id, { state }).then((resp: any) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功');
|
||||||
|
table.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clickDel: (row: any) => {
|
||||||
|
delApply_api(row.id).then((resp: any) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功');
|
||||||
|
table.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getActions: (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
) => {
|
||||||
|
if (!data) return [];
|
||||||
|
const disabled = data.state.value === 'enabled';
|
||||||
|
|
||||||
|
const result = [
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'edit',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => table.toSave(data.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'action',
|
||||||
|
text: disabled ? '禁用' : '启用',
|
||||||
|
tooltip: {
|
||||||
|
title: disabled ? '禁用' : '启用',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: `确认${disabled ? '禁用' : '启用'}`,
|
||||||
|
onConfirm: () => table.changeStatus(data),
|
||||||
|
},
|
||||||
|
icon: disabled ? 'StopOutlined' : 'PlayCircleOutlined',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
tooltip: {
|
||||||
|
title: disabled ? '请先禁用再删除' : '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: () => table.clickDel(data),
|
||||||
|
},
|
||||||
|
disabled,
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
] as ActionsType[];
|
||||||
|
const otherServers = data.integrationModes.map(
|
||||||
|
(item: any) => item.value as string,
|
||||||
|
);
|
||||||
|
const others = {
|
||||||
|
key: 'others',
|
||||||
|
text: '其他',
|
||||||
|
icon: 'EllipsisOutlined',
|
||||||
|
children: [] as ActionsType[],
|
||||||
|
};
|
||||||
|
// 有集成菜单权限
|
||||||
|
if (otherServers.includes('page'))
|
||||||
|
others.children?.push({
|
||||||
|
permission: true,
|
||||||
|
key: 'page',
|
||||||
|
text: '集成菜单',
|
||||||
|
tooltip: {
|
||||||
|
title: '集成菜单',
|
||||||
|
},
|
||||||
|
icon: 'MenuUnfoldOutlined',
|
||||||
|
onClick: () => {},
|
||||||
|
});
|
||||||
|
// 有api操作权限
|
||||||
|
if (otherServers.includes('apiServer'))
|
||||||
|
others.children?.push(
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'empowerment',
|
||||||
|
text: '赋权',
|
||||||
|
tooltip: {
|
||||||
|
title: '赋权',
|
||||||
|
},
|
||||||
|
icon: 'icon-fuquan',
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'viewApi',
|
||||||
|
text: '查看API',
|
||||||
|
tooltip: {
|
||||||
|
title: '查看API',
|
||||||
|
},
|
||||||
|
icon: 'icon-chakanAPI',
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// 其他不为空
|
||||||
|
if (others.children.length > 0) {
|
||||||
|
if (type === 'card') {
|
||||||
|
result.splice(result.length - 1, 0, others);
|
||||||
|
} else {
|
||||||
|
result.splice(result.length - 1, 0, ...others.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
getTypeLabel: (val: string) => {
|
||||||
|
if (!val) return '';
|
||||||
|
return typeOptions.find((item) => item.value === val)?.label;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.apply-container {
|
||||||
|
:deep(.ant-table-cell) {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue