| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- <template>
- <Navbar title="新建认证源" :is-go-back="true" />
- <a-card
- :bordered="false"
- style="margin-top: 20px; box-shadow: 0 2px 4px 0 rgba(54, 58, 80, 0.32)"
- >
- <a-steps class="steps" :current="current" type="navigation">
- <a-step v-for="item in steps" :key="item.title" :title="item.title" />
- </a-steps>
- <a-divider />
- <div class="steps-content">
- <div v-if="steps[current].contentTemplate === 'First'">
- <div class="box">
- <div style="display: flex">
- <label class="labelStyle">选择认证源 <span style="color: red">*</span></label>
- <ul class="selectUl">
- <li
- v-for="(item, index) in selectedItem"
- :key="index"
- :class="{ active: activeIndex === index }"
- @click="setActiveItem(index)"
- >
- <img class="picture" :src="item.imgSrc" />
- <div class="info">
- <h3 style="font-size: 14px; font-weight: 700">
- {{ item.title }}
- </h3>
- <p v-tooltip.bottom="item.tooltip" class="text">
- {{ item.tooltip }}
- </p>
- </div>
- </li>
- </ul>
- </div>
- </div>
- </div>
- <div v-else-if="steps[current].contentTemplate === 'Second'">
- <a-form
- :model="identityInfo"
- :label-col="labelCol"
- :wrapper-col="wrapperCol"
- autocomplete="off"
- >
- <a-form-item
- has-feedback
- label="认证源名称"
- name="name"
- :rules="[{ required: true, message: '请输入认证源名称!' }]"
- >
- <a-input
- v-model:value="identityInfo.name"
- placeholder="必填,请输入认证源名称"
- />
- </a-form-item>
- <a-form-item
- label="认证源LOGO"
- name="logo"
- :rules="[{ required: true, message: '请选择LOGO!' }]"
- >
- <div v-tooltip.top="'点击查看大图'">
- <a-image :width="48" :src="logoUrl" />
- </div>
- <a-upload
- v-model:file-list="identityInfo.logo"
- :max-count="1"
- :before-upload="beforeUpload"
- :show-upload-list="false"
- @change="logoFileChange"
- >
- <a style="margin-left: 6px">去上传</a>
- </a-upload>
- <span>
- <a style="margin-left: 6px" @click="deleteLogo">删除</a>
- </span>
- </a-form-item>
- <a-form-item
- label="认证源描述"
- name="description"
- style="margin-top: 6px"
- >
- <a-textarea
- v-model:value="identityInfo.description"
- :rows="4"
- placeholder="非必填,请输入认证源描述"
- />
- </a-form-item>
- </a-form>
- </div>
- <div v-else-if="steps[current].contentTemplate === 'Third'">
- <a-form
- name="basic"
- :label-col="labelCol"
- :wrapper-col="wrapperCol"
- :model="identitySetting"
- autocomplete="off"
- >
- <a-form-item
- has-feedback
- label="身份提供商 id"
- name="entityID"
- :rules="[{ required: true, message: '请输入身份提供商 id!' }]"
- >
- <a-input
- v-model:value="identitySetting.entityID"
- placeholder="必填,请输入身份提供商 id"
- />
- </a-form-item>
- <a-form-item
- has-feedback
- label="SSO 地址"
- name="ssoUrl"
- :rules="[{ required: true, message: '请输入 SSO 地址' }]"
- >
- <a-input
- v-model:value="identitySetting.ssoUrl"
- placeholder="必填,请输入 SSO 地址"
- />
- </a-form-item>
- <a-form-item
- has-feedback
- label="证书"
- name="certificate"
- :rules="[{ required: true, message: '请输入证书' }]"
- >
- <a-textarea
- v-model:value="identitySetting.certificate"
- :rows="4"
- placeholder="必填,请输入证书"
- @blur="certificateBlur"
- />
- </a-form-item>
- <a-form-item
- label="退出 URL"
- name="loginOutUrl"
- style="margin-top: 10px"
- >
- <a-input
- v-model:value="identitySetting.loginOutUrl"
- placeholder="非必填,请输入退出 URL"
- />
- </a-form-item>
- </a-form>
- </div>
- <div v-else>
- <a-form
- name="basic"
- :label-col="{ style: { width: '186px' } }"
- :wrapper-col="wrapperCol"
- :model="identitySetting"
- autocomplete="off"
- >
- <a-form-item label="uid(用户ID)" name="uid">
- <a-input
- v-model:value="identitySetting.uid"
- placeholder="选填,IDP 中用户ID"
- />
- </a-form-item>
- <a-form-item label="clientId(公司ID)" name="clientId">
- <a-input-number
- v-model:value="identitySetting.clientId"
- :controls="false"
- style="width: 100%"
- placeholder="选填,IDP 中公司ID"
- />
- </a-form-item>
- <a-form-item label="userName(用户姓名)" name="userName">
- <a-input
- v-model:value="identitySetting.userName"
- placeholder="选填,IDP 中用户姓名"
- />
- </a-form-item>
- <a-form-item label="userNo(员工编号)" name="userNo">
- <a-input
- v-model:value="identitySetting.userNo"
- placeholder="选填,IDP 中员工编号"
- />
- </a-form-item>
- <a-form-item label="nickName(员工昵称)" name="nickName">
- <a-input
- v-model:value="identitySetting.nickName"
- placeholder="选填,IDP 中员工昵称"
- />
- </a-form-item>
- <a-form-item label="email(员工邮箱)" name="email">
- <a-input
- v-model:value="identitySetting.email"
- placeholder="选填,IDP 中员工邮箱"
- />
- </a-form-item>
- <a-form-item label="phoneNumber(员工电话)" name="phoneNumber">
- <a-input
- v-model:value="identitySetting.phoneNumber"
- placeholder="选填,IDP 中员工电话"
- />
- </a-form-item>
- <a-form-item label="roleTemplateNo(角色模板)" name="roleTemplateNo">
- <a-input
- v-model:value="identitySetting.roleTemplateNo"
- placeholder="选填,如果有多个角色模板编号,使用逗号分隔"
- />
- </a-form-item>
- </a-form>
- <div class="proDog-setting">
- <h3>Prodog 配置</h3>
- <a-form
- name="basic"
- :label-col="{ style: { width: '186px' } }"
- :wrapper-col="wrapperCol"
- :rules="rules"
- :model="identitySetting"
- autocomplete="off"
- >
- <a-form-item has-feedback label="Prodog 实体ID" name="spEntityID">
- <a-input
- v-model:value="identitySetting.spEntityID"
- placeholder="必填, Prodog 实体 ID "
- />
- </a-form-item>
- <a-form-item
- has-feedback
- label="Prodog断言解析地址"
- name="spAssertionConsumeService"
- >
- <a-input
- v-model:value="identitySetting.spAssertionConsumeService"
- placeholder="必填,Prodog 断言解析地址"
- />
- </a-form-item>
- <a-form-item
- has-feedback
- label="Prodog断言解析成功跳转地址"
- name="spAssertionConsumeSuccessRedirectService"
- >
- <a-input
- v-model:value="
- identitySetting.spAssertionConsumeSuccessRedirectService
- "
- placeholder="必填,Prodog 断言解析成功跳转地址"
- />
- </a-form-item>
- </a-form>
- </div>
- </div>
- </div>
- <div class="steps-action">
- <a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
- <a-button v-if="current === 0" disabled @click="prev"> 上一步 </a-button>
- <a-button
- v-if="current === 0"
- type="primary"
- style="margin-left: 8px"
- @click="next"
- >
- 下一步
- </a-button>
- <a-button
- v-if="current === 1"
- type="primary"
- style="margin-left: 8px"
- :disabled="
- !identityInfo.name ||
- logoUrl === '/static/assets/client-base-v4/image/logo.png'
- ? true
- : false
- "
- @click="next"
- >
- 下一步
- </a-button>
- <a-button
- v-if="current === 2"
- type="primary"
- style="margin-left: 8px"
- :disabled="
- !identitySetting.entityID ||
- !identitySetting.ssoUrl ||
- !identitySetting.certificate
- ? true
- : false
- "
- @click="next"
- >
- 下一步
- </a-button>
- <a-button
- v-if="current == steps.length - 1"
- type="primary"
- :disabled="
- !identitySetting.spEntityID || service || redirect ? true : false
- "
- style="margin-left: 8px"
- @click="createIdentity"
- >
- 完成
- </a-button>
- </div>
- </a-card>
- </template>
- <script setup>
- import Common from '../common/Common';
- import { message } from 'ant-design-vue';
- import { ref, reactive, onMounted } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { getImageSrc } from '../common/image-src';
- import {
- getBase64,
- imageToBase64,
- base64toFile,
- saveUpdateAuth,
- queryById,
- } from './configData.js';
- const route = useRoute();
- const router = useRouter();
- const current = ref(0); // 当前步骤
- const activeIndex = ref(0); // 所选认证源
- const logoUrl = ref('/static/assets/client-base-v4/image/logo.png'); // logo地址
- // 步骤一步骤二数据
- const identityInfo = reactive({
- id: '',
- name: '',
- logo: [],
- file: '',
- active: true,
- description: '',
- authType: 'OAUTH',
- });
- // 步骤三步骤四数据
- const identitySetting = ref({
- entityID: '',
- ssoUrl: '',
- certificate: '',
- loginOutUrl: '',
- uid: '',
- userName: '',
- userNo: '',
- nickName: '',
- email: '',
- phoneNumber: '',
- roleTemplateNo: '',
- spEntityID: 'com.leanwo.prodog.sp',
- spAssertionConsumeService: 'http://xxxx:xx/api/saml/sso/${id}',
- spAssertionConsumeSuccessRedirectService:
- 'http://xxxx:xx/index.html#/samlLogin',
- });
- const service = ref(false);
- const redirect = ref(false);
- const logoName = ref('');
- const logoClassName = ref('');
- // 设置form样式
- const labelCol = ref({
- style: {
- width: '120px',
- },
- });
- const wrapperCol = ref({
- span: 8,
- });
- // 验证断言解析地址结束字符是否正确
- let validateService = async (_rule, value) => {
- if (!value) {
- return Promise.reject('请输入 Prodog 断言解析地址');
- }
- if (!value.endsWith('/api/saml/sso/${id}')) {
- service.value = true;
- return Promise.reject('断言解析地址必须以/api/saml/sso/${ id }结束');
- } else {
- service.value = false;
- }
- };
- // 验证断言解析成功跳转地址结束字符是否正确
- let redirectService = async (_rule, value) => {
- if (!value) {
- return Promise.reject('请输入 Prodog 断言解析成功跳转地址');
- }
- if (!value.endsWith('index.html#/samlLogin')) {
- redirect.value = true;
- return Promise.reject(
- '断言解析成功跳转地址必须以index.html#/samlLogin结束',
- );
- } else {
- redirect.value = false;
- }
- };
- const rules = {
- spEntityID: [
- {
- required: true,
- message: '请输入 Prodog 实体 ID',
- },
- ],
- spAssertionConsumeService: [
- {
- required: true,
- validator: validateService,
- trigger: 'change',
- },
- ],
- spAssertionConsumeSuccessRedirectService: [
- {
- required: true,
- validator: redirectService,
- trigger: 'change',
- },
- ],
- };
- // 获取更新Id
- onMounted(() => {
- const { identityId } = route.query;
- if (identityId) {
- identityInfo.id = identityId;
- queryAuthById(identityId);
- }
- });
- // 根据id查询详情(更新时数据回显)
- const queryAuthById = id => {
- const params = new FormData();
- params.append('id', id);
- queryById(params).then(
- success => {
- if (success.errorCode === 0) {
- const {
- active,
- authType,
- attribute,
- className,
- description,
- logo,
- name,
- } = success.data;
- logoUrl.value = getImageSrc(className, logo);
- imgToBase64();
- logoName.value = logo;
- identityInfo.name = name;
- identityInfo.active = active;
- logoClassName.value = className;
- identityInfo.description = description;
- const datas = JSON.parse(attribute);
- identitySetting.value = datas;
- if (authType === 'OAuth2.0') {
- activeIndex.value = 0;
- identityInfo.authType = 'OAUTH';
- } else {
- activeIndex.value = 1;
- identityInfo.authType = 'SAML';
- }
- } else {
- message.error(success.errorMessage);
- }
- },
- error => {
- Common.processException(error);
- },
- );
- };
- // 图片转file
- const imgToBase64 = () => {
- var image = new Image();
- image.crossOrigin = '';
- image.src = logoUrl.value;
- image.onload = function () {
- let base64 = imageToBase64(image); //图片转base64
- identityInfo.file = base64toFile(base64, logoName.value); //base64转File
- };
- };
- // 新建或更新认证源
- const createIdentity = () => {
- const jsonStr = JSON.stringify(identitySetting.value);
- const formData = new FormData();
- formData.append('attribute', jsonStr);
- formData.append('id', identityInfo.id);
- formData.append('name', identityInfo.name);
- formData.append('logo', identityInfo.file);
- formData.append('active', identityInfo.active);
- formData.append('authType', identityInfo.authType);
- formData.append('description', identityInfo.description);
- saveUpdateAuth(formData).then(
- success => {
- if (success.errorCode === 0) {
- if (!identityInfo.id) {
- message.success('新建认证源成功!');
- } else {
- message.success('更新认证源成功!');
- }
- router.push('/desktop/identityManager');
- } else {
- message.error(success.errorMessage);
- }
- },
- error => {
- Common.processException(error);
- },
- );
- };
- // 获取logo文件
- const logoFileChange = async e => {
- identityInfo.file = e.file;
- logoUrl.value = await getBase64(e.fileList[0].originFileObj);
- };
- // 删除logo文件
- const deleteLogo = () => {
- identityInfo.file = '';
- identityInfo.logo.splice(0, 1);
- logoUrl.value = '/static/assets/client-base-v4/image/logo.png';
- message.warning('请上传logo!');
- };
- // 证书base64格式失去焦点后清除空格换行、回车
- const certificateBlur = e => {
- identitySetting.value.certificate = e.target.value.replace(/[\s\n\r]+/g, '');
- };
- // 禁用antd自动上传
- const beforeUpload = () => {
- return false;
- };
- // 步骤条配置
- const steps = ref([
- {
- title: '选择认证源协议类型',
- contentTemplate: 'First',
- },
- {
- title: '定义认证源名称及logo',
- contentTemplate: 'Second',
- },
- {
- title: '认证源基础配置',
- contentTemplate: 'Third',
- },
- {
- title: '账号关联',
- contentTemplate: 'Last',
- },
- ]);
- // 选择认证源数据
- const selectedItem = ref([
- {
- title: 'OAuth 2.0',
- imgSrc: '/static/assets/client-base-v4/image/oAuth.png',
- tooltip: 'OAuth 2.0是行业标准的授权协议。',
- },
- {
- title: 'SAML',
- imgSrc: '/static/assets/client-base-v4/image/saml.png',
- tooltip: 'SAML是安全断言标记语言,是一个基于XML的开源标准数据格式。',
- },
- ]);
- // 下一步
- const next = () => {
- current.value++;
- };
- // 上一步
- const prev = () => {
- current.value--;
- };
- // 设置当前点击的认证源为活动项
- const setActiveItem = index => {
- activeIndex.value = index;
- if (index === 0) {
- identityInfo.authType = 'OAUTH';
- } else {
- identityInfo.authType = 'SAML';
- }
- };
- </script>
- <style scoped>
- .steps {
- padding: 0;
- }
- .steps-content {
- margin-top: 16px;
- border-radius: 6px;
- min-height: 100px;
- }
- .steps-action {
- margin-top: 24px;
- }
- .labelStyle {
- color: rgba(0, 0, 0, 0.4);
- padding-bottom: 6px;
- padding-right: 20px;
- padding-top: 6px;
- vertical-align: baseline;
- width: 100px;
- }
- .selectUl {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- font-size: 12px;
- width: 100%;
- list-style: none;
- padding: 0;
- }
- .selectUl li {
- align-items: center;
- border: 1px solid #dcdcdc;
- box-sizing: border-box;
- cursor: pointer;
- display: flex;
- flex-direction: row;
- height: 80px;
- margin-bottom: 20px;
- margin-right: 16px;
- overflow: hidden;
- padding: 20px;
- width: 295px;
- }
- .selectUl li:hover {
- border-color: #006eff;
- }
- .selectUl li.active {
- border: 1px solid #006eff;
- }
- .box {
- display: table;
- font-size: 12px;
- line-height: 1.5;
- }
- .picture {
- height: 32px;
- margin-right: 16px;
- width: 32px;
- }
- .info {
- flex: 1;
- overflow: hidden;
- text-align: left;
- }
- .text {
- margin-top: 5px;
- display: inline-block;
- max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- vertical-align: middle;
- white-space: nowrap;
- color: rgba(0, 0, 0, 0.4) !important;
- }
- .ant-card-body {
- padding: 20px !important;
- }
- :deep(.ant-form-item-control-input-content) {
- display: flex;
- align-items: center;
- }
- .ant-upload-select-picture-card i {
- font-size: 32px;
- color: #999;
- }
- .ant-upload-select-picture-card .ant-upload-text {
- margin-top: 8px;
- color: #666;
- }
- :deep(.ant-form-item-label > label) {
- font-size: 12px !important;
- color: rgba(0, 0, 0, 0.4);
- }
- .ant-form-item {
- margin-bottom: 4px;
- }
- .proDog-setting > h3 {
- font-size: 14px !important;
- font-weight: 700;
- }
- </style>
|