Browse Source

4.0.22 增加身份认证源

liuyanpeng 2 năm trước cách đây
mục cha
commit
82f58cbcf0

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "client-base-v4",
   "description": "Leanwo Prodog Client",
-  "version": "4.0.19",
+  "version": "4.0.22",
   "author": "yangzhijie1488 <yangzhijie1488@163.com>",
   "scripts": {
     "dev": "cross-env webpack serve --config ./webpack.dev.js",

BIN
public/image/logo.png


BIN
public/image/oAuth.png


BIN
public/image/saml.png


+ 101 - 10
src/client/Login.vue

@@ -60,15 +60,31 @@
                       type="text"
                       class="form-control"
                       autocomplete="off"
-                      :placeholder="$t('lang.login.pleaseInputVerificationCode')"
+                      :placeholder="
+                        $t('lang.login.pleaseInputVerificationCode')
+                      "
                       style="width: 240px"
                     />
                   </div>
                   <div>
-                    <button v-if="disabledBtn == false" class="btn btn-default" :disabled="disabledBtn" style="width: 10rem;" @click="sendUserVerificationCode">
+                    <button
+                      v-if="disabledBtn == false"
+                      class="btn btn-default"
+                      :disabled="disabledBtn"
+                      style="width: 10rem"
+                      @click="sendUserVerificationCode"
+                    >
                       {{ $t("lang.login.getVerificationCode") }}
                     </button>
-                    <button v-else class="btn btn-default" :disabled="disabledBtn" style="width: 10rem;" @click="sendUserVerificationCode">{{ btntxt }}</button>
+                    <button
+                      v-else
+                      class="btn btn-default"
+                      :disabled="disabledBtn"
+                      style="width: 10rem"
+                      @click="sendUserVerificationCode"
+                    >
+                      {{ btntxt }}
+                    </button>
                   </div>
                 </div>
               </div>
@@ -164,8 +180,22 @@
                     @click="toDownloadPage()"
                   >{{ $t("lang.login.appDownload") }}</a>
                 </p>
-                <div class="clearfix" />
+                <!-- <div class="clearfix" /> -->
                 <br />
+                <div class="login-third-box">
+                  <div class="login-third">
+                    <div class="login-third-items">
+                      <span
+                        v-for="item in authSetting"
+                        :key="item.id"
+                        v-tooltip.top="`${item.authType}-${item.name}`"
+                        class="login-third-item"
+                      >
+                        <img :src="getImageSrc(item.className, item.logo)" />
+                      </span>
+                    </div>
+                  </div>
+                </div>
                 <div v-if="showLeanwoText">
                   <p>{{ $t("lang.login.copyright") }}</p>
                 </div>
@@ -185,6 +215,9 @@ import CustomerEnvironment from '../CustomerEnvironment.js';
 import TokenClientResource from '../api/base/TokenClientResource.js';
 import LoginService from './LoginService.js';
 import { Notify, Uuid } from 'pc-component-v3';
+import { queryLoginAuth } from '.././identity/configData.js';
+import { message } from 'ant-design-vue';
+import { getImageSrc } from '../common/image-src';
 
 export default {
   components: {},
@@ -207,6 +240,8 @@ export default {
       disabledBtn: false,
       verificationCode: '', // 验证码
       time: 60,
+      authSetting: [],
+      getImageSrc,
     };
   },
 
@@ -231,7 +266,7 @@ export default {
       this.showSystemConfigPage = true;
     }
     this.initView();
-
+    this.queryAllAuthSetting();
     this.unqiueAccountManagementDto();
 
     this.getCurrentDate();
@@ -245,12 +280,12 @@ export default {
       var _self = this;
 
       const restoreDate = LoginService.restoreFromLocalStorage();
-      if(restoreDate.rememberPassword === true){
+      if (restoreDate.rememberPassword === true) {
         _self.userName = restoreDate.userName;
         _self.password = restoreDate.password;
         _self.rememberPassword = restoreDate.rememberPassword;
         _self.languageSelected = restoreDate.languageSelected;
-      }else{
+      } else {
         _self.userName = null;
         _self.password = null;
         _self.rememberPassword = false;
@@ -258,6 +293,24 @@ export default {
       }
     },
 
+    // 查询单点登录认证源信息
+    queryAllAuthSetting: function () {
+      queryLoginAuth().then(
+        success => {
+          if (success.errorCode === 0) {
+            if (success.datas !== null && success.datas !== undefined){
+              this.authSetting = success.datas;
+            }
+          } else {
+            message.warning(success.errorMessage);
+          }
+        },
+        error => {
+          Common.processException(error);
+        },
+      );
+    },
+
     /**
      * 根据域名查询账套的信息
      */
@@ -268,9 +321,9 @@ export default {
         type: 'get',
         async: true,
         success: function (response) {
-          if(response.errorCode === 0){
+          if (response.errorCode === 0) {
             _self.accountItem = response.data;
-          }else{
+          } else {
             Notify.error('账套获取失败', response.errorMessage);
           }
         },
@@ -387,7 +440,7 @@ export default {
             LoginService.setLoginInfo(loginInfo.data, _self.$router);
             LoginService.saveLocalStorage(_self);
             _self.setTokenClient();
-            localStorage.setItem('allowSound',false);
+            localStorage.setItem('allowSound', false);
           } else {
             Notify.error(
               _self.$t('lang.login.loginFailure'),
@@ -702,4 +755,42 @@ export default {
 .float-left {
   float: left;
 }
+.separator > p {
+  margin-bottom: 0 !important;
+}
+.login-third-box {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  border-bottom-left-radius: 2px;
+  border-bottom-right-radius: 2px;
+  box-sizing: border-box;
+  padding: 0;
+  font-size: 12px;
+  color: #555666;
+}
+.login-third {
+  margin: 6px 0;
+  box-sizing: border-box;
+  text-align: center;
+  margin-left: 12px;
+  font-size: 0;
+  position: relative;
+  margin-left: 0;
+}
+
+.login-third-items {
+  font-size: 0;
+  position: relative;
+  margin-left: 0;
+}
+.login-third-item img {
+  display: inline-block;
+  width: 32px;
+  height: 32px;
+  border-radius: 50% 50%;
+  background: #ccc;
+  cursor: pointer;
+  margin-left: 12px;
+}
 </style>

+ 1 - 1
src/common/image-src.js

@@ -6,7 +6,7 @@ export function getImageSrc(className, imageName) {
   var host = window.location.host;
   // console.log('host:' + host);
   var localhostPath = protocol + '//' + host;
-  var accountId = localStorage.getItem('#accountId');
+  var accountId = localStorage.getItem('#accountId') || 0;
   if (imageName == null) {
     return null;
   }

+ 554 - 0
src/identity/CreateIdentity.vue

@@ -0,0 +1,554 @@
+<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
+            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
+            label="身份提供商 id"
+            name="entityID"
+            :rules="[{ required: true, message: '请输入身份提供商 id!' }]"
+          >
+            <a-input
+              v-model:value="identitySetting.entityID"
+              placeholder="必填,请输入身份提供商 id"
+            />
+          </a-form-item>
+          <a-form-item
+            label="SSO 地址"
+            name="ssoUrl"
+            :rules="[{ required: true, message: '请输入 SSO 地址' }]"
+          >
+            <a-input
+              v-model:value="identitySetting.ssoUrl"
+              placeholder="必填,请输入 SSO 地址"
+            />
+          </a-form-item>
+          <a-form-item
+            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">
+            <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="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>
+    </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"
+        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: '',
+});
+
+const logoName = ref('');
+const logoClassName = ref('');
+// 设置form样式
+const labelCol = ref({
+  style: {
+    width: '120px',
+  },
+});
+const wrapperCol = ref({
+  span: 8,
+});
+
+// 获取更新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: 14px !important;
+}
+</style>

+ 181 - 0
src/identity/IdentityManager.vue

@@ -0,0 +1,181 @@
+<template>
+  <div id="test-header">
+    <Navbar title="身份源管理" />
+    <a-button type="primary" @click="createOrUpdate('')"> 新建认证源 </a-button>
+  </div>
+  <a-card :bordered="false" class="allInfo-card">
+    <CommonTable
+      class="commonTable"
+      :have-page="false"
+      :is-loading="isLoading"
+      :scroll-value="{ y: '70vh' }"
+      :columns="identityColumns"
+      :data-source="identityDatas"
+    >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'active'">
+          <span v-if="record.active === true" style="color: #0abf5b">
+            已启用
+          </span>
+          <span v-else-if="record.active === false" style="color: #e54545">
+            已停用
+          </span>
+        </template>
+        <template v-else-if="column.key === 'operation'">
+          <span>
+            <a
+              v-if="record.active === false"
+              style="margin-right: 6px"
+              @click="enableConfirm(record)"
+            >启用</a>
+            <a v-else style="margin-right: 6px" @click="disableConfirm(record)">停用</a>
+            <a-dropdown :trigger="['click']">
+              <a class="ant-dropdown-link" @click.prevent>
+                更多
+                <DownOutlined />
+              </a>
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item key="0">
+                    <a @click="createOrUpdate(record.id)">编辑</a>
+                  </a-menu-item>
+                  <a-menu-item key="1">
+                    <a @click="deleteIdentity(record.id)">删除</a>
+                  </a-menu-item>
+                </a-menu>
+              </template>
+            </a-dropdown>
+          </span>
+        </template>
+      </template>
+    </CommonTable>
+  </a-card>
+</template>
+
+<script setup>
+import { useRouter } from 'vue-router';
+import Common from '../common/Common.js';
+import { message, Modal } from 'ant-design-vue';
+import { ref, onMounted, createVNode } from 'vue';
+import CommonTable from '../print/CommonTable.vue';
+import { DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
+import {
+  tableColumns,
+  queryAllAuth,
+  deleteAuth,
+  changeActive,
+} from './configData.js';
+
+const router = useRouter();
+const isLoading = ref(false); //表格加载loading
+const identityDatas = ref([]); // 表格认证源数据
+const identityColumns = ref(tableColumns); //表格列
+
+onMounted(() => {
+  queryAllAuthSetting();
+});
+
+// 查询所有认证源
+const queryAllAuthSetting = () => {
+  queryAllAuth().then(
+    success => {
+      if (success.errorCode === 0) {
+        identityDatas.value = success.datas;
+      } else {
+        message.warning(success.errorMessage);
+      }
+    },
+    error => {
+      Common.processException(error);
+    },
+  );
+};
+
+// 启用
+const enableConfirm = (record, status) => {
+  Modal.confirm({
+    title: `确认启用${record.name}`,
+    icon: createVNode(ExclamationCircleOutlined),
+    content: '启用后认证源将可以正常使用',
+    onOk() {
+      changeStatus(record.id, true);
+    },
+  });
+};
+// 停用
+const disableConfirm = (record, status) => {
+  Modal.confirm({
+    title: `确认停用${record.name}`,
+    icon: createVNode(ExclamationCircleOutlined),
+    content: '停用后认证源将无法正常使用',
+    onOk() {
+      changeStatus(record.id, false);
+    },
+  });
+};
+
+// 更改状态
+const changeStatus = (id, status) => {
+  const params = new FormData();
+  params.append('id', id);
+  params.append('active', status);
+  changeActive(params).then(
+    success => {
+      if (success.errorCode === 0) {
+        message.success('更新状态成功!');
+        queryAllAuthSetting();
+      } else {
+        message.error(success.errorMessage);
+      }
+    },
+    error => {
+      Common.processException(error);
+    },
+  );
+};
+
+// 删除认证源
+const deleteIdentity = id => {
+  const params = new FormData();
+  params.append('id', id);
+  deleteAuth(params).then(
+    success => {
+      if (success.errorCode === 0) {
+        message.success('删除成功!');
+        queryAllAuthSetting();
+      } else {
+        message.error(success.errorMessage);
+      }
+    },
+    error => {
+      Common.processException(error);
+    },
+  );
+};
+
+// 去往新建认证源页面
+const createOrUpdate = id => {
+  router.push({
+    path: '/desktop/createIdentity',
+    query: {
+      identityId: id,
+    },
+  });
+};
+</script>
+
+<style scoped>
+.allInfo-card {
+  margin-top: 10px;
+  box-shadow: 0 2px 4px 0 rgba(54, 58, 80, 0.32);
+}
+.allInfo-card > :deep(.ant-card-body) {
+  padding: 0 !important;
+}
+.commonTable {
+  margin: 0;
+}
+.ant-table-pagination.ant-pagination {
+  margin: 10px 0;
+}
+</style>

+ 206 - 0
src/identity/configData.js

@@ -0,0 +1,206 @@
+import Common from '../common/Common';
+
+export const tableColumns = [
+  {
+    title: '认证源名称',
+    key: 'name',
+    dataIndex: 'name',
+    width: 260,
+  },
+  {
+    title: '认证源类型',
+    key: 'authType',
+    dataIndex: 'authType',
+    width: 260,
+  },
+  {
+    title: '状态',
+    key: 'active',
+    dataIndex: 'active',
+    width: 100,
+  },
+  {
+    title: '描述',
+    key: 'description',
+    dataIndex: 'description',
+    width: 300,
+  },
+  {
+    title: '操作',
+    key: 'operation',
+    width: 200,
+  },
+].map(item => ({ ...item, resizable: true, maxWidth: 400, minWidth: 75 }));
+
+// 登录时获取认证信息
+export const queryLoginAuth= () => {
+  var requestUrl = 'AuthSettingResource/queryBeforeAllAuthSetting';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'get',
+      contentType: 'application/json',
+
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+// 查询认证信息
+export const queryAllAuth= () => {
+  var requestUrl = 'AuthSettingResource/queryAllAuthSetting';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'get',
+      contentType: 'application/json',
+
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+// 根据ID查询信息
+export const queryById = datas => {
+  var requestUrl = 'AuthSettingResource/queryById';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'post',
+      contentType: false,
+      processData: false,
+      data: datas,
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+// 新建或更新认证源
+export const saveUpdateAuth = datas => {
+  var requestUrl = 'AuthSettingResource/saveUpdateAuthSetting';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'post',
+      contentType: false,
+      processData: false,
+      data: datas,
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+// 删除认证源
+export const deleteAuth = datas => {
+  var requestUrl = 'AuthSettingResource/deleteAuthSetting';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'post',
+      contentType: false,
+      processData: false,
+      data: datas,
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+// 更新认证源状态
+export const changeActive = datas => {
+  var requestUrl = 'AuthSettingResource/changeAuthSetting';
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(requestUrl),
+      type: 'post',
+      contentType: false,
+      processData: false,
+      data: datas,
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+
+// file 转 base64
+export const  getBase64 = file => {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.readAsDataURL(file);
+    reader.onload = () => resolve(reader.result);
+    reader.onerror = error => reject(error);
+  });
+};
+
+// 图片转 base64
+export const imageToBase64 = img => {
+  var canvas = document.createElement('canvas');
+  canvas.width = img.width;
+  canvas.height = img.height;
+  var ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0, img.width, img.height);
+  var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
+  var dataURL = canvas.toDataURL('image/jpeg' + ext);
+  return dataURL;
+};
+  
+
+// base64 转file
+export const base64toFile = (base64, imageName) => {
+  const arr = base64.split(',');
+  const mime = arr[0].match(/.*?:(.*?);/)[1];
+  const suffix = mime.split('/')[1];
+  const bstr = window.atob(arr[1]);
+  let n = bstr.length;
+  const u8arr = new Uint8Array(n);
+  while (n--) {
+    u8arr[n] = bstr.charCodeAt(n);
+  }
+  const file = new File([u8arr], `${imageName}.${suffix}`, {
+    type: 'image/jpeg',
+  });
+  return file;
+};

+ 4 - 0
src/index.js

@@ -72,6 +72,8 @@ import PrintTemp from '../src/customer/printTemp/index.vue';
 import PrintCard from '../src/print/PrintCard.vue';
 import DataArchive from '../src/archive/DataArchive.vue';
 import ArchivalRecord from '../src/archive/ArchivalRecord.vue';
+import IdentityManager  from '../src/identity/IdentityManager.vue';
+import CreateIdentity  from '../src/identity/CreateIdentity.vue';
 
 
 
@@ -136,4 +138,6 @@ export {
   DataArchive,
   ArchivalRecord,
   ExcelImport,
+  IdentityManager,
+  CreateIdentity,
 };

+ 17 - 5
src/print/CommonTable.vue

@@ -9,9 +9,9 @@
         :loading="isLoading"
         :data-source="dataSource"
         :columns="columns"
-        :scroll="{ y: 350 }"
-        :pagination="pagination"
         :row-key="(record) => record.id"
+        :scroll="scrollValue"
+        :pagination="havePage ? pagination : false"
         :row-selection="
           isSelect
             ? {
@@ -24,6 +24,7 @@
         :row-class-name="
           (_record, index) => (index % 2 === 1 ? 'table-striped' : null)
         "
+        @resize-column="handleResizeColumn"
       >
         <template
           v-for="(item, index) in renderArr"
@@ -69,6 +70,14 @@ const props = defineProps({
     type: Number,
     default: 0,
   },
+  havePage: {
+    type: Boolean,
+    default: true,
+  },
+  scrollValue: {
+    type: Object,
+    default: () => ({ y: 410 }),
+  },
 });
 
 //  选择数据
@@ -145,7 +154,10 @@ const selectAllEvent = (selected, selectedRows, changeRows) => {
   }
   emit('selectColumn', state.selectedRows);
 };
-
+// 伸缩列
+const handleResizeColumn = (w, col) => {
+  col.width = w;
+};
 // 清空选择
 const clear = () => {
   state.selectedRowKeys = [];
@@ -155,9 +167,9 @@ const clear = () => {
 // 回到第一页
 const backFirstPage = () => {
   pagination.current = 1;
-  emit('pageParams', pagination.current,pagination.pageSize);
+  emit('pageParams', pagination.current, pagination.pageSize);
 };
-defineExpose({ clear,backFirstPage });
+defineExpose({ clear, backFirstPage });
 
 // 监听total变化
 watch(

+ 5 - 0
src/routes/main_routes.js

@@ -62,6 +62,9 @@ const PrintCard = () => import('../print/PrintCard.vue');
 const DataArchive = () => import('../archive/DataArchive.vue');
 const ArchivalRecord = () => import('../archive/ArchivalRecord.vue');
 const ExcelImport = () => import('../customer/ExcelImport.vue');
+const IdentityManager = () => import('../identity/IdentityManager.vue');
+const CreateIdentity = () => import('../identity/CreateIdentity.vue');
+
 import { ProcessReport } from 'pc-component-v3';
 
 export default [
@@ -370,6 +373,8 @@ export default [
       { path: '/desktop/dataArchive', component: DataArchive },//数据归档
       { path: '/desktop/archivalRecord', component: ArchivalRecord },//数据归档记录下载
       { path: '/desktop/excelImport', component: ExcelImport },
+      { path: '/desktop/identityManager', component: IdentityManager }, // 身份源管理
+      { path: '/desktop/createIdentity', name:'createIdentity',component: CreateIdentity }, // 身份源管理
     ],
   },
 ];