mirror of
				https://github.com/veops/cmdb.git
				synced 2025-11-04 13:46:17 +08:00 
			
		
		
		
	前端更新 (#189)
* fix:add package * fix:notice_info为null的情况 * fix:2 bugs * feat:1.common增加通知配置 2.cmdb预定义值webhook&其他模型 * fix:json 不支持预定义值 * fix:json 不支持预定义值
This commit is contained in:
		@@ -54,6 +54,48 @@
 | 
			
		||||
      <div class="content unicode" style="display: block;">
 | 
			
		||||
          <ul class="icon_lists dib-box">
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">wechatApp</div>
 | 
			
		||||
                <div class="code-name">&#xe88e;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">robot</div>
 | 
			
		||||
                <div class="code-name">&#xe88b;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">feishuApp</div>
 | 
			
		||||
                <div class="code-name">&#xe88c;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">dingdingApp</div>
 | 
			
		||||
                <div class="code-name">&#xe88d;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">email</div>
 | 
			
		||||
                <div class="code-name">&#xe88a;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">setting-feishu</div>
 | 
			
		||||
                <div class="code-name">&#xe887;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">setting-feishu-selected</div>
 | 
			
		||||
                <div class="code-name">&#xe888;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">cmdb-histogram</div>
 | 
			
		||||
@@ -2100,6 +2142,12 @@
 | 
			
		||||
                <div class="code-name">&#xe738;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">ops-setting-notice-email-selected</div>
 | 
			
		||||
                <div class="code-name">&#xe889;</div>
 | 
			
		||||
              </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
              <span class="icon iconfont"></span>
 | 
			
		||||
                <div class="name">ops-setting-notice</div>
 | 
			
		||||
@@ -3954,9 +4002,9 @@
 | 
			
		||||
<pre><code class="language-css"
 | 
			
		||||
>@font-face {
 | 
			
		||||
  font-family: 'iconfont';
 | 
			
		||||
  src: url('iconfont.woff2?t=1694508259411') format('woff2'),
 | 
			
		||||
       url('iconfont.woff?t=1694508259411') format('woff'),
 | 
			
		||||
       url('iconfont.ttf?t=1694508259411') format('truetype');
 | 
			
		||||
  src: url('iconfont.woff2?t=1696815443987') format('woff2'),
 | 
			
		||||
       url('iconfont.woff?t=1696815443987') format('woff'),
 | 
			
		||||
       url('iconfont.ttf?t=1696815443987') format('truetype');
 | 
			
		||||
}
 | 
			
		||||
</code></pre>
 | 
			
		||||
          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
 | 
			
		||||
@@ -3982,6 +4030,69 @@
 | 
			
		||||
      <div class="content font-class">
 | 
			
		||||
        <ul class="icon_lists dib-box">
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont wechatApp"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              wechatApp
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.wechatApp
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont robot"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              robot
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.robot
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont feishuApp"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              feishuApp
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.feishuApp
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont dingdingApp"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              dingdingApp
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.dingdingApp
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont email"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              email
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.email
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont ops-setting-notice-feishu"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              setting-feishu
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.ops-setting-notice-feishu
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont ops-setting-notice-feishu-selected"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              setting-feishu-selected
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.ops-setting-notice-feishu-selected
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont cmdb-bar"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
@@ -7051,6 +7162,15 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont ops-setting-notice-email-selected-copy"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
              ops-setting-notice-email-selected
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="code-name">.ops-setting-notice-email-selected-copy
 | 
			
		||||
            </div>
 | 
			
		||||
          </li>
 | 
			
		||||
          
 | 
			
		||||
          <li class="dib">
 | 
			
		||||
            <span class="icon iconfont ops-setting-notice"></span>
 | 
			
		||||
            <div class="name">
 | 
			
		||||
@@ -9832,6 +9952,62 @@
 | 
			
		||||
      <div class="content symbol">
 | 
			
		||||
          <ul class="icon_lists dib-box">
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#wechatApp"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">wechatApp</div>
 | 
			
		||||
                <div class="code-name">#wechatApp</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#robot"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">robot</div>
 | 
			
		||||
                <div class="code-name">#robot</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#feishuApp"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">feishuApp</div>
 | 
			
		||||
                <div class="code-name">#feishuApp</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#dingdingApp"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">dingdingApp</div>
 | 
			
		||||
                <div class="code-name">#dingdingApp</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#email"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">email</div>
 | 
			
		||||
                <div class="code-name">#email</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#ops-setting-notice-feishu"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">setting-feishu</div>
 | 
			
		||||
                <div class="code-name">#ops-setting-notice-feishu</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#ops-setting-notice-feishu-selected"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">setting-feishu-selected</div>
 | 
			
		||||
                <div class="code-name">#ops-setting-notice-feishu-selected</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#cmdb-bar"></use>
 | 
			
		||||
@@ -12560,6 +12736,14 @@
 | 
			
		||||
                <div class="code-name">#ops-dot</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#ops-setting-notice-email-selected-copy"></use>
 | 
			
		||||
                </svg>
 | 
			
		||||
                <div class="name">ops-setting-notice-email-selected</div>
 | 
			
		||||
                <div class="code-name">#ops-setting-notice-email-selected-copy</div>
 | 
			
		||||
            </li>
 | 
			
		||||
          
 | 
			
		||||
            <li class="dib">
 | 
			
		||||
                <svg class="icon svg-icon" aria-hidden="true">
 | 
			
		||||
                  <use xlink:href="#ops-setting-notice"></use>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
@font-face {
 | 
			
		||||
  font-family: "iconfont"; /* Project id 3857903 */
 | 
			
		||||
  src: url('iconfont.woff2?t=1694508259411') format('woff2'),
 | 
			
		||||
       url('iconfont.woff?t=1694508259411') format('woff'),
 | 
			
		||||
       url('iconfont.ttf?t=1694508259411') format('truetype');
 | 
			
		||||
  src: url('iconfont.woff2?t=1696815443987') format('woff2'),
 | 
			
		||||
       url('iconfont.woff?t=1696815443987') format('woff'),
 | 
			
		||||
       url('iconfont.ttf?t=1696815443987') format('truetype');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.iconfont {
 | 
			
		||||
@@ -13,6 +13,34 @@
 | 
			
		||||
  -moz-osx-font-smoothing: grayscale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wechatApp:before {
 | 
			
		||||
  content: "\e88e";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.robot:before {
 | 
			
		||||
  content: "\e88b";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.feishuApp:before {
 | 
			
		||||
  content: "\e88c";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dingdingApp:before {
 | 
			
		||||
  content: "\e88d";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.email:before {
 | 
			
		||||
  content: "\e88a";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ops-setting-notice-feishu:before {
 | 
			
		||||
  content: "\e887";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ops-setting-notice-feishu-selected:before {
 | 
			
		||||
  content: "\e888";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.cmdb-bar:before {
 | 
			
		||||
  content: "\e886";
 | 
			
		||||
}
 | 
			
		||||
@@ -1377,6 +1405,10 @@
 | 
			
		||||
  content: "\e738";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ops-setting-notice-email-selected-copy:before {
 | 
			
		||||
  content: "\e889";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ops-setting-notice:before {
 | 
			
		||||
  content: "\e72f";
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -5,6 +5,55 @@
 | 
			
		||||
  "css_prefix_text": "",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "glyphs": [
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37590786",
 | 
			
		||||
      "name": "wechatApp",
 | 
			
		||||
      "font_class": "wechatApp",
 | 
			
		||||
      "unicode": "e88e",
 | 
			
		||||
      "unicode_decimal": 59534
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37590798",
 | 
			
		||||
      "name": "robot",
 | 
			
		||||
      "font_class": "robot",
 | 
			
		||||
      "unicode": "e88b",
 | 
			
		||||
      "unicode_decimal": 59531
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37590794",
 | 
			
		||||
      "name": "feishuApp",
 | 
			
		||||
      "font_class": "feishuApp",
 | 
			
		||||
      "unicode": "e88c",
 | 
			
		||||
      "unicode_decimal": 59532
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37590791",
 | 
			
		||||
      "name": "dingdingApp",
 | 
			
		||||
      "font_class": "dingdingApp",
 | 
			
		||||
      "unicode": "e88d",
 | 
			
		||||
      "unicode_decimal": 59533
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37590776",
 | 
			
		||||
      "name": "email",
 | 
			
		||||
      "font_class": "email",
 | 
			
		||||
      "unicode": "e88a",
 | 
			
		||||
      "unicode_decimal": 59530
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37537876",
 | 
			
		||||
      "name": "setting-feishu",
 | 
			
		||||
      "font_class": "ops-setting-notice-feishu",
 | 
			
		||||
      "unicode": "e887",
 | 
			
		||||
      "unicode_decimal": 59527
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37537859",
 | 
			
		||||
      "name": "setting-feishu-selected",
 | 
			
		||||
      "font_class": "ops-setting-notice-feishu-selected",
 | 
			
		||||
      "unicode": "e888",
 | 
			
		||||
      "unicode_decimal": 59528
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37334642",
 | 
			
		||||
      "name": "cmdb-histogram",
 | 
			
		||||
@@ -2392,6 +2441,13 @@
 | 
			
		||||
      "unicode": "e738",
 | 
			
		||||
      "unicode_decimal": 59192
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "37575490",
 | 
			
		||||
      "name": "ops-setting-notice-email-selected",
 | 
			
		||||
      "font_class": "ops-setting-notice-email-selected-copy",
 | 
			
		||||
      "unicode": "e889",
 | 
			
		||||
      "unicode_decimal": 59529
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "icon_id": "34108346",
 | 
			
		||||
      "name": "ops-setting-notice",
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,127 +1,134 @@
 | 
			
		||||
import { axios } from '@/utils/request'
 | 
			
		||||
 | 
			
		||||
export function getEmployeeList(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: params,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
// export function getEmployeeList(params, orderBy) {
 | 
			
		||||
//   return axios({
 | 
			
		||||
//     url: '/common-setting/v1/employee' + '/' + orderBy,
 | 
			
		||||
//     method: 'get',
 | 
			
		||||
//     params: params,
 | 
			
		||||
//   })
 | 
			
		||||
// }
 | 
			
		||||
export function postEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function getEmployeeCount(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/count',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: params,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function deleteEmployee(_id) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${_id}`,
 | 
			
		||||
    method: 'delete',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function putEmployee(_id, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${_id}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function batchEditEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/batch',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function importEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/import',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeByUid(uid) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/${uid}`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function updateEmployeeByUid(uid, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function updatePasswordByUid(uid, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/change_password/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function bindWxByUid(uid) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/bind_work_wechat/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getAllPosition() {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/position`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeByEmployeeId(employee_id) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${employee_id}`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 下载员工列表
 | 
			
		||||
export function downloadAllEmployee(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/export_all`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params,
 | 
			
		||||
    responseType: 'blob'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeListByFilter(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/filter',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getNoticeByEmployeeIds(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/get_notice_by_ids',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
import { axios } from '@/utils/request'
 | 
			
		||||
 | 
			
		||||
export function getEmployeeList(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: params,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
// export function getEmployeeList(params, orderBy) {
 | 
			
		||||
//   return axios({
 | 
			
		||||
//     url: '/common-setting/v1/employee' + '/' + orderBy,
 | 
			
		||||
//     method: 'get',
 | 
			
		||||
//     params: params,
 | 
			
		||||
//   })
 | 
			
		||||
// }
 | 
			
		||||
export function postEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function getEmployeeCount(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/count',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: params,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function deleteEmployee(_id) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${_id}`,
 | 
			
		||||
    method: 'delete',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function putEmployee(_id, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${_id}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function batchEditEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/batch',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data: data,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
export function importEmployee(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/import',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeByUid(uid) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/${uid}`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function updateEmployeeByUid(uid, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function updatePasswordByUid(uid, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/change_password/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function bindPlatformByUid(platform, uid) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/bind_notice/${platform}/${uid}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function unbindPlatformByUid(platform, uid) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/by_uid/bind_notice/${platform}/${uid}`,
 | 
			
		||||
    method: 'delete',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getAllPosition() {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/position`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeByEmployeeId(employee_id) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/${employee_id}`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 下载员工列表
 | 
			
		||||
export function downloadAllEmployee(params) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/employee/export_all`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params,
 | 
			
		||||
    responseType: 'blob'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getEmployeeListByFilter(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/filter',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getNoticeByEmployeeIds(data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/employee/get_notice_by_ids',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								cmdb-ui/src/api/noticeSetting.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								cmdb-ui/src/api/noticeSetting.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import { axios } from '@/utils/request'
 | 
			
		||||
 | 
			
		||||
export function sendTestEmail(receive_address, data) {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/notice_config/send_test_email?receive_address=${receive_address}`,
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getNoticeConfigByPlatform = (platform) => {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/notice_config',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: { ...platform },
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const postNoticeConfigByPlatform = (data) => {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: '/common-setting/v1/notice_config',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const putNoticeConfigByPlatform = (id, info) => {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/notice_config/${id}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data: info
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getNoticeConfigAppBot = () => {
 | 
			
		||||
  return axios({
 | 
			
		||||
    url: `/common-setting/v1/notice_config/app_bot`,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
@@ -1,285 +1,293 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
 | 
			
		||||
      <div :style="{ width: '50px', height: '24px', position: 'relative' }">
 | 
			
		||||
        <treeselect
 | 
			
		||||
          v-if="index"
 | 
			
		||||
          class="custom-treeselect"
 | 
			
		||||
          :style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
 | 
			
		||||
          v-model="item.type"
 | 
			
		||||
          :multiple="false"
 | 
			
		||||
          :clearable="false"
 | 
			
		||||
          searchable
 | 
			
		||||
          :options="ruleTypeList"
 | 
			
		||||
          :normalizer="
 | 
			
		||||
            (node) => {
 | 
			
		||||
              return {
 | 
			
		||||
                id: node.value,
 | 
			
		||||
                label: node.label,
 | 
			
		||||
                children: node.children,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          "
 | 
			
		||||
        >
 | 
			
		||||
        </treeselect>
 | 
			
		||||
      </div>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '130px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.property"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        :options="canSearchPreferenceAttrList"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node.name,
 | 
			
		||||
              label: node.alias || node.name,
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
          :title="node.label"
 | 
			
		||||
          slot="option-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
        >
 | 
			
		||||
          <ValueTypeMapIcon :attr="node.raw" />
 | 
			
		||||
          {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
          slot="value-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
        >
 | 
			
		||||
          <ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '100px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.exp"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        :options="[...getExpListByProperty(item.property), ...advancedExpList]"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node.value,
 | 
			
		||||
              label: node.label,
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        @select="(value) => handleChangeExp(value, item, index)"
 | 
			
		||||
      >
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '175px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.value"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
 | 
			
		||||
        :options="getChoiceValueByProperty(item.property)"
 | 
			
		||||
        placeholder="请选择"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node[0],
 | 
			
		||||
              label: node[0],
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
          :title="node.label"
 | 
			
		||||
          slot="option-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
        >
 | 
			
		||||
          {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <a-input-group
 | 
			
		||||
        size="small"
 | 
			
		||||
        compact
 | 
			
		||||
        v-else-if="item.exp === 'range' || item.exp === '~range'"
 | 
			
		||||
        :style="{ width: '175px' }"
 | 
			
		||||
      >
 | 
			
		||||
        <a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
 | 
			
		||||
        ~
 | 
			
		||||
        <a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
 | 
			
		||||
      </a-input-group>
 | 
			
		||||
      <a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
 | 
			
		||||
        <treeselect
 | 
			
		||||
          class="custom-treeselect"
 | 
			
		||||
          :style="{ width: '60px', '--custom-height': '24px' }"
 | 
			
		||||
          v-model="item.compareType"
 | 
			
		||||
          :multiple="false"
 | 
			
		||||
          :clearable="false"
 | 
			
		||||
          searchable
 | 
			
		||||
          :options="compareTypeList"
 | 
			
		||||
          :normalizer="
 | 
			
		||||
            (node) => {
 | 
			
		||||
              return {
 | 
			
		||||
                id: node.value,
 | 
			
		||||
                label: node.label,
 | 
			
		||||
                children: node.children,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          "
 | 
			
		||||
        >
 | 
			
		||||
        </treeselect>
 | 
			
		||||
        <a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
 | 
			
		||||
      </a-input-group>
 | 
			
		||||
      <a-input
 | 
			
		||||
        v-else-if="item.exp !== 'value' && item.exp !== '~value'"
 | 
			
		||||
        size="small"
 | 
			
		||||
        v-model="item.value"
 | 
			
		||||
        :placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
 | 
			
		||||
        class="ops-input"
 | 
			
		||||
      ></a-input>
 | 
			
		||||
      <div v-else :style="{ width: '175px' }"></div>
 | 
			
		||||
      <a-tooltip title="复制">
 | 
			
		||||
        <a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
 | 
			
		||||
      </a-tooltip>
 | 
			
		||||
      <a-tooltip title="删除">
 | 
			
		||||
        <a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
 | 
			
		||||
      </a-tooltip>
 | 
			
		||||
    </a-space>
 | 
			
		||||
    <div class="table-filter-add">
 | 
			
		||||
      <a @click="handleAddRule">+ 新增</a>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import _ from 'lodash'
 | 
			
		||||
import { v4 as uuidv4 } from 'uuid'
 | 
			
		||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
 | 
			
		||||
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Expression',
 | 
			
		||||
  components: { ValueTypeMapIcon },
 | 
			
		||||
  model: {
 | 
			
		||||
    prop: 'value',
 | 
			
		||||
    event: 'change',
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
    canSearchPreferenceAttrList: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      required: true,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      ruleTypeList,
 | 
			
		||||
      expList,
 | 
			
		||||
      advancedExpList,
 | 
			
		||||
      compareTypeList,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ruleList: {
 | 
			
		||||
      get() {
 | 
			
		||||
        return this.value
 | 
			
		||||
      },
 | 
			
		||||
      set(val) {
 | 
			
		||||
        this.$emit('change', val)
 | 
			
		||||
        return val
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getExpListByProperty(property) {
 | 
			
		||||
      if (property) {
 | 
			
		||||
        const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
        if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
 | 
			
		||||
          return [
 | 
			
		||||
            { value: 'is', label: '等于' },
 | 
			
		||||
            { value: '~is', label: '不等于' },
 | 
			
		||||
            { value: '~value', label: '为空' }, // 为空的定义有点绕
 | 
			
		||||
            { value: 'value', label: '不为空' },
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
        return this.expList
 | 
			
		||||
      }
 | 
			
		||||
      return this.expList
 | 
			
		||||
    },
 | 
			
		||||
    isChoiceByProperty(property) {
 | 
			
		||||
      const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
      if (_find) {
 | 
			
		||||
        return _find.is_choice
 | 
			
		||||
      }
 | 
			
		||||
      return false
 | 
			
		||||
    },
 | 
			
		||||
    handleAddRule() {
 | 
			
		||||
      this.ruleList.push({
 | 
			
		||||
        id: uuidv4(),
 | 
			
		||||
        type: 'and',
 | 
			
		||||
        property: this.canSearchPreferenceAttrList[0]?.name,
 | 
			
		||||
        exp: 'is',
 | 
			
		||||
        value: null,
 | 
			
		||||
      })
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    handleCopyRule(item) {
 | 
			
		||||
      this.ruleList.push({ ...item, id: uuidv4() })
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    handleDeleteRule(item) {
 | 
			
		||||
      const idx = this.ruleList.findIndex((r) => r.id === item.id)
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        this.ruleList.splice(idx, 1)
 | 
			
		||||
      }
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    getChoiceValueByProperty(property) {
 | 
			
		||||
      const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
      if (_find) {
 | 
			
		||||
        return _find.choice_value
 | 
			
		||||
      }
 | 
			
		||||
      return []
 | 
			
		||||
    },
 | 
			
		||||
    handleChangeExp({ value }, item, index) {
 | 
			
		||||
      const _ruleList = _.cloneDeep(this.ruleList)
 | 
			
		||||
      if (value === 'range') {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          min: '',
 | 
			
		||||
          max: '',
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      } else if (value === 'compare') {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          compareType: '1',
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.ruleList = _ruleList
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style></style>
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
 | 
			
		||||
      <div :style="{ width: '50px', height: '24px', position: 'relative' }">
 | 
			
		||||
        <treeselect
 | 
			
		||||
          v-if="index"
 | 
			
		||||
          class="custom-treeselect"
 | 
			
		||||
          :style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
 | 
			
		||||
          v-model="item.type"
 | 
			
		||||
          :multiple="false"
 | 
			
		||||
          :clearable="false"
 | 
			
		||||
          searchable
 | 
			
		||||
          :options="ruleTypeList"
 | 
			
		||||
          :normalizer="
 | 
			
		||||
            (node) => {
 | 
			
		||||
              return {
 | 
			
		||||
                id: node.value,
 | 
			
		||||
                label: node.label,
 | 
			
		||||
                children: node.children,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          "
 | 
			
		||||
        >
 | 
			
		||||
        </treeselect>
 | 
			
		||||
      </div>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '130px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.property"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        :options="canSearchPreferenceAttrList"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node.name,
 | 
			
		||||
              label: node.alias || node.name,
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        appendToBody
 | 
			
		||||
        :zIndex="1050"
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
          :title="node.label"
 | 
			
		||||
          slot="option-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
        >
 | 
			
		||||
          <ValueTypeMapIcon :attr="node.raw" />
 | 
			
		||||
          {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
          slot="value-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
        >
 | 
			
		||||
          <ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '100px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.exp"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        :options="[...getExpListByProperty(item.property), ...advancedExpList]"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node.value,
 | 
			
		||||
              label: node.label,
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        @select="(value) => handleChangeExp(value, item, index)"
 | 
			
		||||
        appendToBody
 | 
			
		||||
        :zIndex="1050"
 | 
			
		||||
      >
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <treeselect
 | 
			
		||||
        class="custom-treeselect"
 | 
			
		||||
        :style="{ width: '175px', '--custom-height': '24px' }"
 | 
			
		||||
        v-model="item.value"
 | 
			
		||||
        :multiple="false"
 | 
			
		||||
        :clearable="false"
 | 
			
		||||
        searchable
 | 
			
		||||
        v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
 | 
			
		||||
        :options="getChoiceValueByProperty(item.property)"
 | 
			
		||||
        placeholder="请选择"
 | 
			
		||||
        :normalizer="
 | 
			
		||||
          (node) => {
 | 
			
		||||
            return {
 | 
			
		||||
              id: node[0],
 | 
			
		||||
              label: node[0],
 | 
			
		||||
              children: node.children,
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        appendToBody
 | 
			
		||||
        :zIndex="1050"
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
          :title="node.label"
 | 
			
		||||
          slot="option-label"
 | 
			
		||||
          slot-scope="{ node }"
 | 
			
		||||
          :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
        >
 | 
			
		||||
          {{ node.label }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </treeselect>
 | 
			
		||||
      <a-input-group
 | 
			
		||||
        size="small"
 | 
			
		||||
        compact
 | 
			
		||||
        v-else-if="item.exp === 'range' || item.exp === '~range'"
 | 
			
		||||
        :style="{ width: '175px' }"
 | 
			
		||||
      >
 | 
			
		||||
        <a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
 | 
			
		||||
        ~
 | 
			
		||||
        <a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
 | 
			
		||||
      </a-input-group>
 | 
			
		||||
      <a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
 | 
			
		||||
        <treeselect
 | 
			
		||||
          class="custom-treeselect"
 | 
			
		||||
          :style="{ width: '60px', '--custom-height': '24px' }"
 | 
			
		||||
          v-model="item.compareType"
 | 
			
		||||
          :multiple="false"
 | 
			
		||||
          :clearable="false"
 | 
			
		||||
          searchable
 | 
			
		||||
          :options="compareTypeList"
 | 
			
		||||
          :normalizer="
 | 
			
		||||
            (node) => {
 | 
			
		||||
              return {
 | 
			
		||||
                id: node.value,
 | 
			
		||||
                label: node.label,
 | 
			
		||||
                children: node.children,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          "
 | 
			
		||||
          appendToBody
 | 
			
		||||
          :zIndex="1050"
 | 
			
		||||
        >
 | 
			
		||||
        </treeselect>
 | 
			
		||||
        <a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
 | 
			
		||||
      </a-input-group>
 | 
			
		||||
      <a-input
 | 
			
		||||
        v-else-if="item.exp !== 'value' && item.exp !== '~value'"
 | 
			
		||||
        size="small"
 | 
			
		||||
        v-model="item.value"
 | 
			
		||||
        :placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
 | 
			
		||||
        class="ops-input"
 | 
			
		||||
      ></a-input>
 | 
			
		||||
      <div v-else :style="{ width: '175px' }"></div>
 | 
			
		||||
      <a-tooltip title="复制">
 | 
			
		||||
        <a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
 | 
			
		||||
      </a-tooltip>
 | 
			
		||||
      <a-tooltip title="删除">
 | 
			
		||||
        <a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
 | 
			
		||||
      </a-tooltip>
 | 
			
		||||
    </a-space>
 | 
			
		||||
    <div class="table-filter-add">
 | 
			
		||||
      <a @click="handleAddRule">+ 新增</a>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import _ from 'lodash'
 | 
			
		||||
import { v4 as uuidv4 } from 'uuid'
 | 
			
		||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
 | 
			
		||||
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Expression',
 | 
			
		||||
  components: { ValueTypeMapIcon },
 | 
			
		||||
  model: {
 | 
			
		||||
    prop: 'value',
 | 
			
		||||
    event: 'change',
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
    canSearchPreferenceAttrList: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      required: true,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      ruleTypeList,
 | 
			
		||||
      expList,
 | 
			
		||||
      advancedExpList,
 | 
			
		||||
      compareTypeList,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ruleList: {
 | 
			
		||||
      get() {
 | 
			
		||||
        return this.value
 | 
			
		||||
      },
 | 
			
		||||
      set(val) {
 | 
			
		||||
        this.$emit('change', val)
 | 
			
		||||
        return val
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getExpListByProperty(property) {
 | 
			
		||||
      if (property) {
 | 
			
		||||
        const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
        if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
 | 
			
		||||
          return [
 | 
			
		||||
            { value: 'is', label: '等于' },
 | 
			
		||||
            { value: '~is', label: '不等于' },
 | 
			
		||||
            { value: '~value', label: '为空' }, // 为空的定义有点绕
 | 
			
		||||
            { value: 'value', label: '不为空' },
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
        return this.expList
 | 
			
		||||
      }
 | 
			
		||||
      return this.expList
 | 
			
		||||
    },
 | 
			
		||||
    isChoiceByProperty(property) {
 | 
			
		||||
      const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
      if (_find) {
 | 
			
		||||
        return _find.is_choice
 | 
			
		||||
      }
 | 
			
		||||
      return false
 | 
			
		||||
    },
 | 
			
		||||
    handleAddRule() {
 | 
			
		||||
      this.ruleList.push({
 | 
			
		||||
        id: uuidv4(),
 | 
			
		||||
        type: 'and',
 | 
			
		||||
        property: this.canSearchPreferenceAttrList[0]?.name,
 | 
			
		||||
        exp: 'is',
 | 
			
		||||
        value: null,
 | 
			
		||||
      })
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    handleCopyRule(item) {
 | 
			
		||||
      this.ruleList.push({ ...item, id: uuidv4() })
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    handleDeleteRule(item) {
 | 
			
		||||
      const idx = this.ruleList.findIndex((r) => r.id === item.id)
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        this.ruleList.splice(idx, 1)
 | 
			
		||||
      }
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
    getChoiceValueByProperty(property) {
 | 
			
		||||
      const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
 | 
			
		||||
      if (_find) {
 | 
			
		||||
        return _find.choice_value
 | 
			
		||||
      }
 | 
			
		||||
      return []
 | 
			
		||||
    },
 | 
			
		||||
    handleChangeExp({ value }, item, index) {
 | 
			
		||||
      const _ruleList = _.cloneDeep(this.ruleList)
 | 
			
		||||
      if (value === 'range') {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          min: '',
 | 
			
		||||
          max: '',
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      } else if (value === 'compare') {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          compareType: '1',
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        _ruleList[index] = {
 | 
			
		||||
          ..._ruleList[index],
 | 
			
		||||
          exp: value,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.ruleList = _ruleList
 | 
			
		||||
      this.$emit('change', this.ruleList)
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -83,10 +83,10 @@ export default {
 | 
			
		||||
    getContent() {
 | 
			
		||||
      const html = _.cloneDeep(this.editor.getHtml())
 | 
			
		||||
      const _html = html.replace(
 | 
			
		||||
        /<span data-w-e-type="attachment" data-w-e-is-void data-w-e-is-inline.*?<\/span>/gm,
 | 
			
		||||
        /<span data-w-e-type="attachment" (data-w-e-is-void|data-w-e-is-void="") (data-w-e-is-inline|data-w-e-is-inline="").*?<\/span>/gm,
 | 
			
		||||
        (value) => {
 | 
			
		||||
          const _match = value.match(/(?<=data-attachmentValue=").*?(?=")/)
 | 
			
		||||
          return `{{${_match}}}`
 | 
			
		||||
          const _match = value.match(/(?<=data-attachment(V|v)alue=").*?(?=")/)
 | 
			
		||||
          return `{{${_match[0]}}}`
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
      return { body_html: html, body: _html }
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ export default {
 | 
			
		||||
    ]
 | 
			
		||||
    return {
 | 
			
		||||
      segmentedContentTypes,
 | 
			
		||||
    //   contentType: 'none',
 | 
			
		||||
      //   contentType: 'none',
 | 
			
		||||
      jsonData: {},
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
@@ -74,6 +74,9 @@ export default {
 | 
			
		||||
  }
 | 
			
		||||
  div.jsoneditor {
 | 
			
		||||
    border-color: #f3f4f6;
 | 
			
		||||
    .jsoneditor-outer {
 | 
			
		||||
      border-color: #f3f4f6;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,195 +1,362 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <a-tabs id="preValueArea" v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }">
 | 
			
		||||
    <a-tab-pane key="define" :disabled="disabled">
 | 
			
		||||
      <span style="font-size:12px;" slot="tab">定义</span>
 | 
			
		||||
      <PreValueTag type="add" :item="[]" @add="addNewValue" :disabled="disabled">
 | 
			
		||||
        <template #default>
 | 
			
		||||
          <a-button
 | 
			
		||||
            :style="{ marginBottom: '10px', fontSize: '12px', padding: '1px 7px' }"
 | 
			
		||||
            type="primary"
 | 
			
		||||
            ghost
 | 
			
		||||
            :disabled="disabled"
 | 
			
		||||
            size="small"
 | 
			
		||||
          >
 | 
			
		||||
            <a-icon type="plus" />添加</a-button
 | 
			
		||||
          >
 | 
			
		||||
        </template>
 | 
			
		||||
      </PreValueTag>
 | 
			
		||||
      <draggable :list="valueList" handle=".handle" :disabled="disabled">
 | 
			
		||||
        <PreValueTag
 | 
			
		||||
          :disabled="disabled"
 | 
			
		||||
          v-for="(item, index) in valueList"
 | 
			
		||||
          :key="`${item[0]}_${index}`"
 | 
			
		||||
          :item="item"
 | 
			
		||||
          @deleteValue="deleteValue"
 | 
			
		||||
          @editValue="editValue"
 | 
			
		||||
        />
 | 
			
		||||
      </draggable>
 | 
			
		||||
    </a-tab-pane>
 | 
			
		||||
    <a-tab-pane key="webhook" :disabled="disabled">
 | 
			
		||||
      <span style="font-size:12px;" slot="tab">Webhook</span>
 | 
			
		||||
      <a-form-model :model="form">
 | 
			
		||||
        <a-row :gutter="24">
 | 
			
		||||
          <a-col :span="24">
 | 
			
		||||
            <a-form-model-item label="地址" prop="url" :labelCol="{ span: 3 }" :wrapperCol="{ span: 16 }">
 | 
			
		||||
              <a-input v-model="form.url" :disabled="disabled">
 | 
			
		||||
                <a-select
 | 
			
		||||
                  :showArrow="false"
 | 
			
		||||
                  slot="addonBefore"
 | 
			
		||||
                  style="width:60px;"
 | 
			
		||||
                  v-model="form.method"
 | 
			
		||||
                  :disabled="disabled"
 | 
			
		||||
                >
 | 
			
		||||
                  <a-select-option value="get">
 | 
			
		||||
                    GET
 | 
			
		||||
                  </a-select-option>
 | 
			
		||||
                  <a-select-option value="post">
 | 
			
		||||
                    POST
 | 
			
		||||
                  </a-select-option>
 | 
			
		||||
                  <a-select-option value="put">
 | 
			
		||||
                    PUT
 | 
			
		||||
                  </a-select-option>
 | 
			
		||||
                </a-select>
 | 
			
		||||
              </a-input>
 | 
			
		||||
            </a-form-model-item>
 | 
			
		||||
          </a-col>
 | 
			
		||||
        </a-row>
 | 
			
		||||
        <a-col :span="24">
 | 
			
		||||
          <a-form-model-item prop="ret_key" :labelCol="{ span: 3 }" :wrapperCol="{ span: 18 }">
 | 
			
		||||
            <template slot="label">
 | 
			
		||||
              <span
 | 
			
		||||
                style="position:relative;white-space:pre;"
 | 
			
		||||
              >{{ `过滤` }}
 | 
			
		||||
                <a-tooltip
 | 
			
		||||
                  title="返回的结果按字段来过滤,层级嵌套用##分隔,比如k1##k2,web请求返回{k1: [{k2: 1}, {k2: 2}]}, 解析结果为[1, 2]"
 | 
			
		||||
                >
 | 
			
		||||
                  <a-icon
 | 
			
		||||
                    style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
 | 
			
		||||
                    type="question-circle"
 | 
			
		||||
                    theme="filled"
 | 
			
		||||
                  />
 | 
			
		||||
                </a-tooltip>
 | 
			
		||||
              </span>
 | 
			
		||||
            </template>
 | 
			
		||||
            <a-input style="width:150px;" v-model="form.ret_key" placeholder="k1##k2" :disabled="disabled" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-form-model>
 | 
			
		||||
    </a-tab-pane>
 | 
			
		||||
  </a-tabs>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import _ from 'lodash'
 | 
			
		||||
import draggable from 'vuedraggable'
 | 
			
		||||
import PreValueTag from './preValueTag.vue'
 | 
			
		||||
import { defautValueColor } from '../../utils/const'
 | 
			
		||||
import ColorPicker from '../../components/colorPicker/index.vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'PreValueArea',
 | 
			
		||||
  components: { draggable, PreValueTag, ColorPicker },
 | 
			
		||||
  props: {
 | 
			
		||||
    disabled: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: true,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      defautValueColor,
 | 
			
		||||
      activeKey: 'define', // define webhook
 | 
			
		||||
      valueList: [],
 | 
			
		||||
      form: {
 | 
			
		||||
        url: '',
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        ret_key: '',
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    disabled: {
 | 
			
		||||
      immediate: false,
 | 
			
		||||
      handler(newValue) {
 | 
			
		||||
        const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
 | 
			
		||||
        if (newValue) {
 | 
			
		||||
          // 如果是disabled 把tab 的ink-bar也置灰
 | 
			
		||||
          dom.style.backgroundColor = '#00000040'
 | 
			
		||||
        } else {
 | 
			
		||||
          dom.style.backgroundColor = '#2f54eb'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    addNewValue(newValue, newStyle, newIcon) {
 | 
			
		||||
      if (newValue) {
 | 
			
		||||
        const idx = this.valueList.findIndex((v) => v[0] === newValue)
 | 
			
		||||
        if (idx > -1) {
 | 
			
		||||
          this.$message.warning('当前值已存在!')
 | 
			
		||||
        } else {
 | 
			
		||||
          this.valueList.push([newValue, { style: newStyle, icon: { ...newIcon } }])
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    deleteValue(item) {
 | 
			
		||||
      const _valueList = _.cloneDeep(this.valueList)
 | 
			
		||||
      const idx = _valueList.findIndex((v) => v[0] === item[0])
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        _valueList.splice(idx, 1)
 | 
			
		||||
        this.valueList = _valueList
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    editValue(item, newValue, newStyle, newIcon) {
 | 
			
		||||
      const _valueList = _.cloneDeep(this.valueList)
 | 
			
		||||
      const idx = _valueList.findIndex((v) => v[0] === item[0])
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        _valueList[idx] = [newValue, { style: newStyle, icon: { ...newIcon } }]
 | 
			
		||||
        this.valueList = _valueList
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    getData() {
 | 
			
		||||
      if (this.activeKey === 'define') {
 | 
			
		||||
        return {
 | 
			
		||||
          choice_value: this.valueList,
 | 
			
		||||
          choice_web_hook: null,
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return { choice_value: [], choice_web_hook: this.form }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    setData({ choice_value, choice_web_hook }) {
 | 
			
		||||
      if (choice_web_hook) {
 | 
			
		||||
        this.form = choice_web_hook
 | 
			
		||||
        this.activeKey = 'webhook'
 | 
			
		||||
      } else {
 | 
			
		||||
        this.valueList = choice_value
 | 
			
		||||
        this.activeKey = 'define'
 | 
			
		||||
      }
 | 
			
		||||
      const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
 | 
			
		||||
      if (this.disabled) {
 | 
			
		||||
        // 如果是disabled 把tab 的ink-bar也置灰
 | 
			
		||||
        dom.style.backgroundColor = '#00000040'
 | 
			
		||||
      } else {
 | 
			
		||||
        dom.style.backgroundColor = '#2f54eb'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.pre-value-edit-color {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  .pre-value-edit-color-item {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    width: 25px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<template>
 | 
			
		||||
  <a-tabs id="preValueArea" v-model="activeKey" size="small" :tabBarStyle="{ borderBottom: 'none' }">
 | 
			
		||||
    <a-tab-pane key="define" :disabled="disabled">
 | 
			
		||||
      <span style="font-size:12px;" slot="tab">定义</span>
 | 
			
		||||
      <PreValueTag type="add" :item="[]" @add="addNewValue" :disabled="disabled">
 | 
			
		||||
        <template #default>
 | 
			
		||||
          <a-button
 | 
			
		||||
            :style="{ marginBottom: '10px', fontSize: '12px', padding: '1px 7px' }"
 | 
			
		||||
            type="primary"
 | 
			
		||||
            ghost
 | 
			
		||||
            :disabled="disabled"
 | 
			
		||||
            size="small"
 | 
			
		||||
          >
 | 
			
		||||
            <a-icon type="plus" />添加</a-button
 | 
			
		||||
          >
 | 
			
		||||
        </template>
 | 
			
		||||
      </PreValueTag>
 | 
			
		||||
      <draggable :list="valueList" handle=".handle" :disabled="disabled">
 | 
			
		||||
        <PreValueTag
 | 
			
		||||
          :disabled="disabled"
 | 
			
		||||
          v-for="(item, index) in valueList"
 | 
			
		||||
          :key="`${item[0]}_${index}`"
 | 
			
		||||
          :item="item"
 | 
			
		||||
          @deleteValue="deleteValue"
 | 
			
		||||
          @editValue="editValue"
 | 
			
		||||
        />
 | 
			
		||||
      </draggable>
 | 
			
		||||
    </a-tab-pane>
 | 
			
		||||
    <a-tab-pane key="webhook" :disabled="disabled">
 | 
			
		||||
      <span style="font-size:12px;" slot="tab">Webhook</span>
 | 
			
		||||
      <Webhook ref="webhook" style="margin-top:10px" />
 | 
			
		||||
      <a-form-model :model="form">
 | 
			
		||||
        <a-col :span="24">
 | 
			
		||||
          <a-form-model-item prop="ret_key" :labelCol="{ span: 3 }" :wrapperCol="{ span: 18 }">
 | 
			
		||||
            <template slot="label">
 | 
			
		||||
              <span
 | 
			
		||||
                style="position:relative;white-space:pre;"
 | 
			
		||||
              >{{ `过滤` }}
 | 
			
		||||
                <a-tooltip
 | 
			
		||||
                  title="返回的结果按字段来过滤,层级嵌套用##分隔,比如k1##k2,web请求返回{k1: [{k2: 1}, {k2: 2}]}, 解析结果为[1, 2]"
 | 
			
		||||
                >
 | 
			
		||||
                  <a-icon
 | 
			
		||||
                    style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
 | 
			
		||||
                    type="question-circle"
 | 
			
		||||
                    theme="filled"
 | 
			
		||||
                  />
 | 
			
		||||
                </a-tooltip>
 | 
			
		||||
              </span>
 | 
			
		||||
            </template>
 | 
			
		||||
            <a-input style="width:150px;" v-model="form.ret_key" placeholder="k1##k2" :disabled="disabled" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-form-model>
 | 
			
		||||
    </a-tab-pane>
 | 
			
		||||
    <a-tab-pane key="choice_other" :disabled="disabled">
 | 
			
		||||
      <span style="font-size:12px;" slot="tab">其他模型属性</span>
 | 
			
		||||
      <a-row :gutter="[24, 24]">
 | 
			
		||||
        <a-col :span="12">
 | 
			
		||||
          <a-form-item
 | 
			
		||||
            :style="{ lineHeight: '24px', marginBottom: '5px' }"
 | 
			
		||||
            label="模型"
 | 
			
		||||
            :label-col="{ span: 4 }"
 | 
			
		||||
            :wrapper-col="{ span: 20 }"
 | 
			
		||||
          >
 | 
			
		||||
            <treeselect
 | 
			
		||||
              :disable-branch-nodes="true"
 | 
			
		||||
              :class="{
 | 
			
		||||
                'custom-treeselect': true,
 | 
			
		||||
                'custom-treeselect-bgcAndBorder': true,
 | 
			
		||||
              }"
 | 
			
		||||
              :style="{
 | 
			
		||||
                '--custom-height': '32px',
 | 
			
		||||
                lineHeight: '32px',
 | 
			
		||||
                '--custom-bg-color': '#fff',
 | 
			
		||||
                '--custom-border': '1px solid #d9d9d9',
 | 
			
		||||
                '--custom-multiple-lineHeight': '14px',
 | 
			
		||||
              }"
 | 
			
		||||
              v-model="choice_other.type_ids"
 | 
			
		||||
              :multiple="true"
 | 
			
		||||
              :clearable="true"
 | 
			
		||||
              searchable
 | 
			
		||||
              :options="ciTypeGroup"
 | 
			
		||||
              value-consists-of="LEAF_PRIORITY"
 | 
			
		||||
              placeholder="请选择CMDB模型"
 | 
			
		||||
              :normalizer="
 | 
			
		||||
                (node) => {
 | 
			
		||||
                  return {
 | 
			
		||||
                    id: node.id || -1,
 | 
			
		||||
                    label: node.alias || node.name || '其他',
 | 
			
		||||
                    title: node.alias || node.name || '其他',
 | 
			
		||||
                    children: node.ci_types,
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              "
 | 
			
		||||
              appendToBody
 | 
			
		||||
              :zIndex="1050"
 | 
			
		||||
              @select="
 | 
			
		||||
                () => {
 | 
			
		||||
                  choice_other.attr_id = undefined
 | 
			
		||||
                }
 | 
			
		||||
              "
 | 
			
		||||
            >
 | 
			
		||||
              <div
 | 
			
		||||
                :title="node.label"
 | 
			
		||||
                slot="option-label"
 | 
			
		||||
                slot-scope="{ node }"
 | 
			
		||||
                :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
              >
 | 
			
		||||
                {{ node.label }}
 | 
			
		||||
              </div>
 | 
			
		||||
            </treeselect>
 | 
			
		||||
          </a-form-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
        <a-col :span="12" v-if="choice_other.type_ids && choice_other.type_ids.length">
 | 
			
		||||
          <a-form-item
 | 
			
		||||
            :style="{ marginBottom: '5px' }"
 | 
			
		||||
            label="属性"
 | 
			
		||||
            :label-col="{ span: 4 }"
 | 
			
		||||
            :wrapper-col="{ span: 20 }"
 | 
			
		||||
          >
 | 
			
		||||
            <treeselect
 | 
			
		||||
              :disable-branch-nodes="true"
 | 
			
		||||
              class="ops-setting-treeselect"
 | 
			
		||||
              v-model="choice_other.attr_id"
 | 
			
		||||
              :multiple="false"
 | 
			
		||||
              :clearable="true"
 | 
			
		||||
              searchable
 | 
			
		||||
              :options="typeAttrs"
 | 
			
		||||
              value-consists-of="LEAF_PRIORITY"
 | 
			
		||||
              placeholder="请选择模型属性"
 | 
			
		||||
              :normalizer="
 | 
			
		||||
                (node) => {
 | 
			
		||||
                  return {
 | 
			
		||||
                    id: node.id || -1,
 | 
			
		||||
                    label: node.alias || node.name || '其他',
 | 
			
		||||
                    title: node.alias || node.name || '其他',
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              "
 | 
			
		||||
              appendToBody
 | 
			
		||||
              :zIndex="1050"
 | 
			
		||||
            >
 | 
			
		||||
              <div
 | 
			
		||||
                :title="node.label"
 | 
			
		||||
                slot="option-label"
 | 
			
		||||
                slot-scope="{ node }"
 | 
			
		||||
                :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
              >
 | 
			
		||||
                {{ node.label }}
 | 
			
		||||
              </div>
 | 
			
		||||
            </treeselect>
 | 
			
		||||
          </a-form-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
        <a-col :span="24" v-if="choice_other.type_ids && choice_other.type_ids.length">
 | 
			
		||||
          <a-form-item
 | 
			
		||||
            :style="{ marginBottom: '5px' }"
 | 
			
		||||
            class="pre-value-filter"
 | 
			
		||||
            label="筛选"
 | 
			
		||||
            :label-col="{ span: 2 }"
 | 
			
		||||
            :wrapper-col="{ span: 22 }"
 | 
			
		||||
          >
 | 
			
		||||
            <FilterComp
 | 
			
		||||
              ref="filterComp"
 | 
			
		||||
              :isDropdown="false"
 | 
			
		||||
              :canSearchPreferenceAttrList="typeAttrs"
 | 
			
		||||
              @setExpFromFilter="setExpFromFilter"
 | 
			
		||||
              :expression="filterExp ? `q=${filterExp}` : ''"
 | 
			
		||||
            />
 | 
			
		||||
          </a-form-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-tab-pane>
 | 
			
		||||
  </a-tabs>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import _ from 'lodash'
 | 
			
		||||
import draggable from 'vuedraggable'
 | 
			
		||||
import PreValueTag from './preValueTag.vue'
 | 
			
		||||
import { defautValueColor } from '../../utils/const'
 | 
			
		||||
import ColorPicker from '../../components/colorPicker/index.vue'
 | 
			
		||||
import Webhook from '../../components/webhook'
 | 
			
		||||
import { getCITypeGroups } from '../../api/ciTypeGroup'
 | 
			
		||||
import { getCITypeCommonAttributesByTypeIds } from '../../api/CITypeAttr'
 | 
			
		||||
import FilterComp from '@/components/CMDBFilterComp'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'PreValueArea',
 | 
			
		||||
  components: { draggable, PreValueTag, ColorPicker, Webhook, FilterComp },
 | 
			
		||||
  props: {
 | 
			
		||||
    disabled: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: true,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      defautValueColor,
 | 
			
		||||
      activeKey: 'define', // define webhook
 | 
			
		||||
      valueList: [],
 | 
			
		||||
      form: {
 | 
			
		||||
        ret_key: '',
 | 
			
		||||
      },
 | 
			
		||||
      choice_other: {
 | 
			
		||||
        type_ids: undefined,
 | 
			
		||||
        attr_id: undefined,
 | 
			
		||||
      },
 | 
			
		||||
      ciTypeGroup: [],
 | 
			
		||||
      typeAttrs: [],
 | 
			
		||||
      filterExp: '',
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    disabled: {
 | 
			
		||||
      immediate: false,
 | 
			
		||||
      handler(newValue) {
 | 
			
		||||
        const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
 | 
			
		||||
        if (newValue) {
 | 
			
		||||
          // 如果是disabled 把tab 的ink-bar也置灰
 | 
			
		||||
          dom.style.backgroundColor = '#00000040'
 | 
			
		||||
        } else {
 | 
			
		||||
          dom.style.backgroundColor = '#2f54eb'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    'choice_other.type_ids': {
 | 
			
		||||
      handler(newValue) {
 | 
			
		||||
        if (newValue && newValue.length) {
 | 
			
		||||
          getCITypeCommonAttributesByTypeIds({ type_ids: newValue.join(',') }).then((res) => {
 | 
			
		||||
            this.typeAttrs = res.attributes
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  created() {
 | 
			
		||||
    getCITypeGroups({ need_other: true }).then((res) => {
 | 
			
		||||
      this.ciTypeGroup = res
 | 
			
		||||
        .filter((item) => item.ci_types && item.ci_types.length)
 | 
			
		||||
        .map((item) => {
 | 
			
		||||
          item.id = `parent_${item.id || -1}`
 | 
			
		||||
          return { ..._.cloneDeep(item) }
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    addNewValue(newValue, newStyle, newIcon) {
 | 
			
		||||
      if (newValue) {
 | 
			
		||||
        const idx = this.valueList.findIndex((v) => v[0] === newValue)
 | 
			
		||||
        if (idx > -1) {
 | 
			
		||||
          this.$message.warning('当前值已存在!')
 | 
			
		||||
        } else {
 | 
			
		||||
          this.valueList.push([newValue, { style: newStyle, icon: { ...newIcon } }])
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    deleteValue(item) {
 | 
			
		||||
      const _valueList = _.cloneDeep(this.valueList)
 | 
			
		||||
      const idx = _valueList.findIndex((v) => v[0] === item[0])
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        _valueList.splice(idx, 1)
 | 
			
		||||
        this.valueList = _valueList
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    editValue(item, newValue, newStyle, newIcon) {
 | 
			
		||||
      const _valueList = _.cloneDeep(this.valueList)
 | 
			
		||||
      const idx = _valueList.findIndex((v) => v[0] === item[0])
 | 
			
		||||
      if (idx > -1) {
 | 
			
		||||
        _valueList[idx] = [newValue, { style: newStyle, icon: { ...newIcon } }]
 | 
			
		||||
        this.valueList = _valueList
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    getData() {
 | 
			
		||||
      if (this.activeKey === 'define') {
 | 
			
		||||
        return {
 | 
			
		||||
          choice_value: this.valueList,
 | 
			
		||||
          choice_web_hook: null,
 | 
			
		||||
          choice_other: null,
 | 
			
		||||
        }
 | 
			
		||||
      } else if (this.activeKey === 'webhook') {
 | 
			
		||||
        const choice_web_hook = this.$refs.webhook.getParams()
 | 
			
		||||
        choice_web_hook.ret_key = this.form.ret_key
 | 
			
		||||
        return { choice_value: [], choice_web_hook, choice_other: null }
 | 
			
		||||
      } else {
 | 
			
		||||
        let choice_other = {}
 | 
			
		||||
        if (this.choice_other.type_ids && this.choice_other.type_ids.length) {
 | 
			
		||||
          this.$refs.filterComp.handleSubmit()
 | 
			
		||||
          choice_other = { ...this.choice_other, filter: this.filterExp }
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
          choice_value: [],
 | 
			
		||||
          choice_web_hook: null,
 | 
			
		||||
          choice_other,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    setData({ choice_value, choice_web_hook, choice_other }) {
 | 
			
		||||
      if (choice_web_hook) {
 | 
			
		||||
        this.activeKey = 'webhook'
 | 
			
		||||
        this.$nextTick(() => {
 | 
			
		||||
          this.$refs.webhook.setParams(choice_web_hook)
 | 
			
		||||
          this.form.ret_key = choice_web_hook.ret_key ?? ''
 | 
			
		||||
        })
 | 
			
		||||
      } else if (choice_other) {
 | 
			
		||||
        this.activeKey = 'choice_other'
 | 
			
		||||
        const { type_ids, attr_id, filter } = choice_other
 | 
			
		||||
        this.choice_other = { type_ids, attr_id }
 | 
			
		||||
        this.filterExp = filter
 | 
			
		||||
        if (type_ids && type_ids.length) {
 | 
			
		||||
          this.$nextTick(() => {
 | 
			
		||||
            this.$refs.filterComp.visibleChange(true, false)
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.valueList = choice_value
 | 
			
		||||
        this.activeKey = 'define'
 | 
			
		||||
      }
 | 
			
		||||
      const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
 | 
			
		||||
      if (this.disabled) {
 | 
			
		||||
        // 如果是disabled 把tab 的ink-bar也置灰
 | 
			
		||||
        dom.style.backgroundColor = '#00000040'
 | 
			
		||||
      } else {
 | 
			
		||||
        dom.style.backgroundColor = '#2f54eb'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    setExpFromFilter(filterExp) {
 | 
			
		||||
      if (filterExp) {
 | 
			
		||||
        this.filterExp = `${filterExp}`
 | 
			
		||||
      } else {
 | 
			
		||||
        this.filterExp = ''
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.pre-value-edit-color {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  .pre-value-edit-color-item {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    width: 25px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<style lang="less">
 | 
			
		||||
.pre-value-filter {
 | 
			
		||||
  .ant-form-item-control {
 | 
			
		||||
    line-height: 24px;
 | 
			
		||||
  }
 | 
			
		||||
  .table-filter-add {
 | 
			
		||||
    line-height: 40px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -156,12 +156,74 @@
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="通知方式" prop="method">
 | 
			
		||||
        <a-checkbox-group v-model="notifies.method">
 | 
			
		||||
          <a-checkbox value="wechatApp">
 | 
			
		||||
            微信
 | 
			
		||||
          </a-checkbox>
 | 
			
		||||
          <a-checkbox value="email">
 | 
			
		||||
            邮件
 | 
			
		||||
          </a-checkbox>
 | 
			
		||||
          <a-row :style="{ marginTop: '4px' }" :gutter="[0, 12]">
 | 
			
		||||
            <a-col :span="6">
 | 
			
		||||
              <a-checkbox value="email"> <ops-icon type="email" style="margin-right:5px" />邮件 </a-checkbox>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="6">
 | 
			
		||||
              <a-checkbox value="wechatApp">
 | 
			
		||||
                <ops-icon type="wechatApp" style="margin-right:5px" />企业微信
 | 
			
		||||
              </a-checkbox>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="6">
 | 
			
		||||
              <a-checkbox value="dingdingApp">
 | 
			
		||||
                <ops-icon type="dingdingApp" style="margin-right:5px" />钉钉
 | 
			
		||||
              </a-checkbox>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="6">
 | 
			
		||||
              <a-checkbox value="feishuApp"> <ops-icon type="feishuApp" style="margin-right:5px" />飞书 </a-checkbox>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="4" :style="{ lineHeight: '32px' }">
 | 
			
		||||
              <ops-icon type="robot" style="margin-right:5px" />机器人:
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="18">
 | 
			
		||||
              <treeselect
 | 
			
		||||
                :disable-branch-nodes="true"
 | 
			
		||||
                :class="{
 | 
			
		||||
                  'custom-treeselect': true,
 | 
			
		||||
                  'custom-treeselect-bgcAndBorder': true,
 | 
			
		||||
                }"
 | 
			
		||||
                :style="{
 | 
			
		||||
                  '--custom-height': '32px',
 | 
			
		||||
                  lineHeight: '32px',
 | 
			
		||||
                  '--custom-bg-color': '#fff',
 | 
			
		||||
                  '--custom-border': '1px solid #d9d9d9',
 | 
			
		||||
                  '--custom-multiple-lineHeight': '14px',
 | 
			
		||||
                }"
 | 
			
		||||
                v-model="selectedBot"
 | 
			
		||||
                :multiple="true"
 | 
			
		||||
                :clearable="true"
 | 
			
		||||
                searchable
 | 
			
		||||
                :options="appBot"
 | 
			
		||||
                value-consists-of="LEAF_PRIORITY"
 | 
			
		||||
                placeholder="请选择机器人"
 | 
			
		||||
                :normalizer="
 | 
			
		||||
                  (node) => {
 | 
			
		||||
                    return {
 | 
			
		||||
                      id: node.name,
 | 
			
		||||
                      label: node.label || node.name,
 | 
			
		||||
                      children: node.bot,
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                "
 | 
			
		||||
                appendToBody
 | 
			
		||||
                :zIndex="1050"
 | 
			
		||||
                noChildrenText="暂无数据"
 | 
			
		||||
              >
 | 
			
		||||
                <div
 | 
			
		||||
                  :title="node.label"
 | 
			
		||||
                  slot="option-label"
 | 
			
		||||
                  slot-scope="{ node }"
 | 
			
		||||
                  :style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
 | 
			
		||||
                >
 | 
			
		||||
                  <ops-icon :type="node.id" v-if="node.children" />{{ node.label }}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div slot="value-label" slot-scope="{ node }">
 | 
			
		||||
                  <ops-icon :type="node.parentNode.id" />{{ node.label }}
 | 
			
		||||
                </div>
 | 
			
		||||
              </treeselect>
 | 
			
		||||
            </a-col>
 | 
			
		||||
          </a-row>
 | 
			
		||||
        </a-checkbox-group>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
@@ -200,6 +262,7 @@ import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vu
 | 
			
		||||
import Webhook from '../../components/webhook'
 | 
			
		||||
import NoticeContent from '../../components/noticeContent'
 | 
			
		||||
import { getNoticeByEmployeeIds } from '@/api/employee'
 | 
			
		||||
import { getNoticeConfigAppBot } from '@/api/noticeSetting'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'TriggerForm',
 | 
			
		||||
@@ -260,6 +323,8 @@ export default {
 | 
			
		||||
      isShow: false,
 | 
			
		||||
      dag_id: null,
 | 
			
		||||
      showCustomEmail: false,
 | 
			
		||||
      appBot: [],
 | 
			
		||||
      selectedBot: undefined,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
@@ -286,9 +351,15 @@ export default {
 | 
			
		||||
        this.dags = res.map((dag) => ({ id: dag[1], label: dag[0] }))
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async getNoticeConfigAppBot() {
 | 
			
		||||
      await getNoticeConfigAppBot().then((res) => {
 | 
			
		||||
        this.appBot = res
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    createFromTriggerTable(attrList) {
 | 
			
		||||
      this.visible = true
 | 
			
		||||
      // this.getDags()
 | 
			
		||||
      this.getDags()
 | 
			
		||||
      this.getNoticeConfigAppBot()
 | 
			
		||||
      this.attrList = attrList
 | 
			
		||||
      this.triggerId = null
 | 
			
		||||
      this.title = '新增触发器'
 | 
			
		||||
@@ -307,7 +378,8 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    async open(property, attrList) {
 | 
			
		||||
      this.visible = true
 | 
			
		||||
      // await this.getDags()
 | 
			
		||||
      await this.getDags()
 | 
			
		||||
      this.getNoticeConfigAppBot()
 | 
			
		||||
      this.attrList = attrList
 | 
			
		||||
      if (property.has_trigger) {
 | 
			
		||||
        this.triggerId = property.trigger.id
 | 
			
		||||
@@ -348,7 +420,7 @@ export default {
 | 
			
		||||
          const employee_ids = property?.trigger?.option?.employee_ids ?? undefined
 | 
			
		||||
          const custom_email =
 | 
			
		||||
            tos
 | 
			
		||||
              .filter((t) => !t.employee_id)
 | 
			
		||||
              .filter((t) => !t.employee_id && t.email)
 | 
			
		||||
              .map((t) => t.email)
 | 
			
		||||
              .join(';') ?? ''
 | 
			
		||||
 | 
			
		||||
@@ -360,7 +432,16 @@ export default {
 | 
			
		||||
              this.$refs.noticeContent.setContent(body_html)
 | 
			
		||||
            }, 100)
 | 
			
		||||
          }
 | 
			
		||||
          this.notifies = { employee_ids, custom_email, subject, method }
 | 
			
		||||
          const _method = method.filter((item) => ['email', 'wechatApp', 'dingdingApp', 'feishuApp'].includes(item))
 | 
			
		||||
          const _flatAppBot = []
 | 
			
		||||
          this.appBot.forEach((item) => {
 | 
			
		||||
            _flatAppBot.push(...item.bot.map((b) => b.name))
 | 
			
		||||
          })
 | 
			
		||||
          const selectedBot = method.filter(
 | 
			
		||||
            (item) => !['email', 'wechatApp', 'dingdingApp', 'feishuApp'].includes(item) && _flatAppBot.includes(item)
 | 
			
		||||
          )
 | 
			
		||||
          this.selectedBot = selectedBot
 | 
			
		||||
          this.notifies = { employee_ids, custom_email, subject, method: _method }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.title = `新增触发器 ${property.alias || property.name}`
 | 
			
		||||
@@ -378,6 +459,7 @@ export default {
 | 
			
		||||
      this.category = 1
 | 
			
		||||
      this.triggerAction = '1'
 | 
			
		||||
      this.filterExp = ''
 | 
			
		||||
      this.selectedBot = undefined
 | 
			
		||||
      this.visible = false
 | 
			
		||||
    },
 | 
			
		||||
    filterChange(value) {
 | 
			
		||||
@@ -415,11 +497,30 @@ export default {
 | 
			
		||||
                  tos.push({ email })
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
              if (this.selectedBot && this.selectedBot.length) {
 | 
			
		||||
                this.selectedBot.forEach((bot) => {
 | 
			
		||||
                  tos.push({ [`${bot}`]: bot })
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
              if (this.category === 2) {
 | 
			
		||||
                const { before_days, notify_at } = this.dateForm
 | 
			
		||||
                params.option.notifies = { tos, subject, body, body_html, method, before_days, notify_at }
 | 
			
		||||
                params.option.notifies = {
 | 
			
		||||
                  tos,
 | 
			
		||||
                  subject,
 | 
			
		||||
                  body,
 | 
			
		||||
                  body_html,
 | 
			
		||||
                  method: [...method, ...(this.selectedBot ?? [])],
 | 
			
		||||
                  before_days,
 | 
			
		||||
                  notify_at,
 | 
			
		||||
                }
 | 
			
		||||
              } else {
 | 
			
		||||
                params.option.notifies = { tos, subject, body, body_html, method }
 | 
			
		||||
                params.option.notifies = {
 | 
			
		||||
                  tos,
 | 
			
		||||
                  subject,
 | 
			
		||||
                  body,
 | 
			
		||||
                  body_html,
 | 
			
		||||
                  method: [...method, ...(this.selectedBot ?? [])],
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              break
 | 
			
		||||
            case '2':
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,12 @@
 | 
			
		||||
          <span v-else-if="row.notify">通知</span>
 | 
			
		||||
        </template>
 | 
			
		||||
      </vxe-column>
 | 
			
		||||
      <vxe-column title="状态">
 | 
			
		||||
        <template #default="{ row }">
 | 
			
		||||
          <a-tag color="green" v-if="row.is_ok">已完成</a-tag>
 | 
			
		||||
          <a-tag color="red" v-else>未完成</a-tag>
 | 
			
		||||
        </template>
 | 
			
		||||
      </vxe-column>
 | 
			
		||||
      <vxe-column title="触发时间">
 | 
			
		||||
        <template #default="{row}">
 | 
			
		||||
          {{ row.updated_at || row.created_at }}
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,34 @@ export const generatorDynamicRouter = async () => {
 | 
			
		||||
          meta: { title: '公司架构', appName: 'backend', icon: 'ops-setting-companyStructure', selectedIcon: 'ops-setting-companyStructure-selected', permission: ['acl_admin', 'backend_admin'] },
 | 
			
		||||
          component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/companyStructure/index')
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: '/setting/notice',
 | 
			
		||||
          name: 'notice',
 | 
			
		||||
          component: RouteView,
 | 
			
		||||
          meta: { title: '通知设置', appName: 'backend', icon: 'ops-setting-notice', selectedIcon: 'ops-setting-notice-selected', permission: ['通知设置', 'backend_admin'] },
 | 
			
		||||
          redirect: '/setting/notice/email',
 | 
			
		||||
          children: [{
 | 
			
		||||
            path: '/setting/notice/email',
 | 
			
		||||
            name: 'notice_email',
 | 
			
		||||
            meta: { title: '邮件设置', icon: 'ops-setting-notice-email', selectedIcon: 'ops-setting-notice-email-selected' },
 | 
			
		||||
            component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/email/index')
 | 
			
		||||
          }, {
 | 
			
		||||
            path: '/setting/notice/wx',
 | 
			
		||||
            name: 'notice_wx',
 | 
			
		||||
            meta: { title: '企业微信', icon: 'ops-setting-notice-wx', selectedIcon: 'ops-setting-notice-wx-selected' },
 | 
			
		||||
            component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/wx')
 | 
			
		||||
          }, {
 | 
			
		||||
            path: '/setting/notice/dingding',
 | 
			
		||||
            name: 'notice_dingding',
 | 
			
		||||
            meta: { title: '钉钉', icon: 'ops-setting-notice-dingding', selectedIcon: 'ops-setting-notice-dingding-selected' },
 | 
			
		||||
            component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/dingding')
 | 
			
		||||
          }, {
 | 
			
		||||
            path: '/setting/notice/feishu',
 | 
			
		||||
            name: 'notice_feishu',
 | 
			
		||||
            meta: { title: '飞书', icon: 'ops-setting-notice-feishu', selectedIcon: 'ops-setting-notice-feishu-selected' },
 | 
			
		||||
            component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/feishu')
 | 
			
		||||
          }]
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },])
 | 
			
		||||
  return routes
 | 
			
		||||
 
 | 
			
		||||
@@ -1,372 +1,379 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
 | 
			
		||||
    <a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
 | 
			
		||||
      <SpanTitle>公司描述</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="名称" prop="name">
 | 
			
		||||
        <a-input v-model="infoData.name" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="描述">
 | 
			
		||||
        <a-input v-model="infoData.description" type="textarea" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>公司地址</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="国家/地区">
 | 
			
		||||
        <a-input v-model="infoData.country" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="城市">
 | 
			
		||||
        <a-input v-model="infoData.city" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="地址">
 | 
			
		||||
        <a-input v-model="infoData.address" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="邮编">
 | 
			
		||||
        <a-input v-model="infoData.postCode" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>联系方式</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="网站">
 | 
			
		||||
        <a-input v-model="infoData.website" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="电话号码" prop="phone">
 | 
			
		||||
        <a-input v-model="infoData.phone" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="传真号码" prop="faxCode">
 | 
			
		||||
        <a-input v-model="infoData.faxCode" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="电子邮箱" prop="email">
 | 
			
		||||
        <a-input v-model="infoData.email" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>公司标识</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="部署域名" prop="domainName">
 | 
			
		||||
        <a-input v-model="infoData.domainName" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="公司logo">
 | 
			
		||||
        <a-space>
 | 
			
		||||
          <a-upload
 | 
			
		||||
            :disabled="!isEditable"
 | 
			
		||||
            name="avatar"
 | 
			
		||||
            list-type="picture-card"
 | 
			
		||||
            class="avatar-uploader"
 | 
			
		||||
            :show-upload-list="false"
 | 
			
		||||
            :customRequest="customRequest"
 | 
			
		||||
            :before-upload="beforeUpload"
 | 
			
		||||
            :style="{ width: '400px', height: '80px' }"
 | 
			
		||||
            accept=".png,.jpg,.jpeg"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              class="ops-setting-companyinfo-upload-show"
 | 
			
		||||
              v-if="infoData.logoName"
 | 
			
		||||
              :style="{ width: '400px', height: '80px' }"
 | 
			
		||||
              @click="eidtImageOption.type = 'Logo'"
 | 
			
		||||
            >
 | 
			
		||||
              <img :src="`/api/common-setting/v1/file/${infoData.logoName}`" alt="avatar" />
 | 
			
		||||
              <a-icon
 | 
			
		||||
                v-if="isEditable"
 | 
			
		||||
                type="minus-circle"
 | 
			
		||||
                theme="filled"
 | 
			
		||||
                class="delete-icon"
 | 
			
		||||
                @click.stop="deleteLogo"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else @click="eidtImageOption.type = 'Logo'">
 | 
			
		||||
              <a-icon type="plus" />
 | 
			
		||||
              <div class="ant-upload-text">上传</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-upload>
 | 
			
		||||
 | 
			
		||||
          <a-upload
 | 
			
		||||
            :disabled="!isEditable"
 | 
			
		||||
            name="avatar"
 | 
			
		||||
            list-type="picture-card"
 | 
			
		||||
            class="avatar-uploader"
 | 
			
		||||
            :show-upload-list="false"
 | 
			
		||||
            :customRequest="customRequest"
 | 
			
		||||
            :before-upload="beforeUpload"
 | 
			
		||||
            :style="{ width: '82px', height: '82px' }"
 | 
			
		||||
            accept=".png,.jpg,.jpeg"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              class="ops-setting-companyinfo-upload-show"
 | 
			
		||||
              v-if="infoData.smallLogoName"
 | 
			
		||||
              :style="{ width: '82px', height: '82px' }"
 | 
			
		||||
              @click="eidtImageOption.type = 'SmallLogo'"
 | 
			
		||||
            >
 | 
			
		||||
              <img :src="`/api/common-setting/v1/file/${infoData.smallLogoName}`" alt="avatar" />
 | 
			
		||||
              <a-icon
 | 
			
		||||
                v-if="isEditable"
 | 
			
		||||
                type="minus-circle"
 | 
			
		||||
                theme="filled"
 | 
			
		||||
                class="delete-icon"
 | 
			
		||||
                @click.stop="deleteSmallLogo"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else @click="eidtImageOption.type = 'SmallLogo'">
 | 
			
		||||
              <a-icon type="plus" />
 | 
			
		||||
              <div class="ant-upload-text">上传</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-upload>
 | 
			
		||||
        </a-space>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
 | 
			
		||||
        <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
        <a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
    <edit-image
 | 
			
		||||
      v-if="showEditImage"
 | 
			
		||||
      :show="showEditImage"
 | 
			
		||||
      :image="editImage"
 | 
			
		||||
      :title="eidtImageOption.title"
 | 
			
		||||
      :eidtImageOption="eidtImageOption"
 | 
			
		||||
      @save="submitImage"
 | 
			
		||||
      @close="showEditImage = false"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
 | 
			
		||||
import { postImageFile } from '@/api/file'
 | 
			
		||||
import { mapMutations, mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../components/spanTitle.vue'
 | 
			
		||||
import EditImage from '../components/EditImage.vue'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'CompanyInfo',
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  components: { SpanTitle, EditImage },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { span: 3 },
 | 
			
		||||
      wrapperCol: { span: 10 },
 | 
			
		||||
      infoData: {
 | 
			
		||||
        name: '',
 | 
			
		||||
        description: '',
 | 
			
		||||
        address: '',
 | 
			
		||||
        city: '',
 | 
			
		||||
        postCode: '',
 | 
			
		||||
        country: '',
 | 
			
		||||
        website: '',
 | 
			
		||||
        phone: '',
 | 
			
		||||
        faxCode: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        logoName: '',
 | 
			
		||||
        smallLogoName: '',
 | 
			
		||||
      },
 | 
			
		||||
      rule: {
 | 
			
		||||
        name: [{ required: true, whitespace: true, message: '请输入名称', trigger: 'blur' }],
 | 
			
		||||
        phone: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^([0-9]|-)+$', 'g'),
 | 
			
		||||
            message: '请输入正确的电话号码',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        faxCode: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^([0-9]|-)+$', 'g'),
 | 
			
		||||
            message: '请输入正确的传真号码',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        email: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$', 'g'),
 | 
			
		||||
            message: '请输入正确的邮箱地址',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      getId: -1,
 | 
			
		||||
      showEditImage: false,
 | 
			
		||||
      editImage: null,
 | 
			
		||||
      eidtImageOption: {
 | 
			
		||||
        type: 'Logo',
 | 
			
		||||
        fixedNumber: [15, 4],
 | 
			
		||||
        title: '编辑企业logo',
 | 
			
		||||
        previewWidth: '200px',
 | 
			
		||||
        previewHeight: '40px',
 | 
			
		||||
        autoCropWidth: 200,
 | 
			
		||||
        autoCropHeight: 40,
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  async mounted() {
 | 
			
		||||
    const res = await getCompanyInfo()
 | 
			
		||||
    if (!res.id) {
 | 
			
		||||
      this.getId = -1
 | 
			
		||||
    } else {
 | 
			
		||||
      this.infoData = res.info
 | 
			
		||||
      this.getId = res.id
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '公司信息', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    ...mapMutations(['SET_FILENAME', 'SET_SMALL_FILENAME']),
 | 
			
		||||
    deleteLogo() {
 | 
			
		||||
      this.infoData.logoName = ''
 | 
			
		||||
    },
 | 
			
		||||
    deleteSmallLogo() {
 | 
			
		||||
      this.infoData.smallLogoName = ''
 | 
			
		||||
    },
 | 
			
		||||
    async onSubmit() {
 | 
			
		||||
      this.$refs.infoData.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          if (this.getId === -1) {
 | 
			
		||||
            await postCompanyInfo(this.infoData)
 | 
			
		||||
          } else {
 | 
			
		||||
            await putCompanyInfo(this.getId, this.infoData)
 | 
			
		||||
          }
 | 
			
		||||
          this.SET_FILENAME(this.infoData.logoName)
 | 
			
		||||
          this.SET_SMALL_FILENAME(this.infoData.smallFileName)
 | 
			
		||||
          this.$message.success('保存成功')
 | 
			
		||||
        } else {
 | 
			
		||||
          this.$message.warning('检查您的输入是否正确!')
 | 
			
		||||
          return false
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.infoData = {
 | 
			
		||||
        name: '',
 | 
			
		||||
        description: '',
 | 
			
		||||
        address: '',
 | 
			
		||||
        city: '',
 | 
			
		||||
        postCode: '',
 | 
			
		||||
        country: '',
 | 
			
		||||
        website: '',
 | 
			
		||||
        phone: '',
 | 
			
		||||
        faxCode: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        logoName: '',
 | 
			
		||||
        smallLogoName: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    customRequest(file) {
 | 
			
		||||
      const reader = new FileReader()
 | 
			
		||||
      var self = this
 | 
			
		||||
      if (this.eidtImageOption.type === 'Logo') {
 | 
			
		||||
        this.eidtImageOption = {
 | 
			
		||||
          type: 'Logo',
 | 
			
		||||
          fixedNumber: [20, 4],
 | 
			
		||||
          title: '编辑企业logo',
 | 
			
		||||
          previewWidth: '200px',
 | 
			
		||||
          previewHeight: '40px',
 | 
			
		||||
          autoCropWidth: 200,
 | 
			
		||||
          autoCropHeight: 40,
 | 
			
		||||
        }
 | 
			
		||||
      } else if (this.eidtImageOption.type === 'SmallLogo') {
 | 
			
		||||
        this.eidtImageOption = {
 | 
			
		||||
          type: 'SmallLogo',
 | 
			
		||||
          fixedNumber: [4, 4],
 | 
			
		||||
          title: '编辑企业logo缩略图',
 | 
			
		||||
          previewWidth: '80px',
 | 
			
		||||
          previewHeight: '80px',
 | 
			
		||||
          autoCropWidth: 250,
 | 
			
		||||
          autoCropHeight: 250,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      reader.onload = function(e) {
 | 
			
		||||
        let result
 | 
			
		||||
        if (typeof e.target.result === 'object') {
 | 
			
		||||
          // 把Array Buffer转化为blob 如果是base64不需要
 | 
			
		||||
          result = window.URL.createObjectURL(new Blob([e.target.result]))
 | 
			
		||||
        } else {
 | 
			
		||||
          result = e.target.result
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.editImage = result
 | 
			
		||||
        self.showEditImage = true
 | 
			
		||||
      }
 | 
			
		||||
      reader.readAsDataURL(file.file)
 | 
			
		||||
    },
 | 
			
		||||
    submitImage(file) {
 | 
			
		||||
      postImageFile(file).then((res) => {
 | 
			
		||||
        if (res.file_name) {
 | 
			
		||||
          if (this.eidtImageOption.type === 'Logo') {
 | 
			
		||||
            this.infoData.logoName = res.file_name
 | 
			
		||||
          } else if (this.eidtImageOption.type === 'SmallLogo') {
 | 
			
		||||
            this.infoData.smallLogoName = res.file_name
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beforeUpload(file) {
 | 
			
		||||
      const isLt2M = file.size / 1024 / 1024 < 2
 | 
			
		||||
      if (!isLt2M) {
 | 
			
		||||
        this.$message.error('图片大小不可超过2MB!')
 | 
			
		||||
      }
 | 
			
		||||
      return isLt2M
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less">
 | 
			
		||||
.ops-setting-companyinfo {
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border-radius: 15px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  margin-bottom: -24px;
 | 
			
		||||
  .ops-setting-companyinfo-upload-show {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 290px;
 | 
			
		||||
    height: 100px;
 | 
			
		||||
    max-height: 100px;
 | 
			
		||||
    img {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .delete-icon {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .ant-upload:hover .delete-icon {
 | 
			
		||||
    display: block;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 5px;
 | 
			
		||||
    right: 5px;
 | 
			
		||||
    color: rgb(247, 85, 85);
 | 
			
		||||
  }
 | 
			
		||||
  .ant-form-item {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
  .ant-form-item label {
 | 
			
		||||
    padding-right: 10px;
 | 
			
		||||
  }
 | 
			
		||||
  .avatar-uploader > .ant-upload {
 | 
			
		||||
    // max-width: 100px;
 | 
			
		||||
    max-height: 100px;
 | 
			
		||||
  }
 | 
			
		||||
  // .ant-upload.ant-upload-select-picture-card {
 | 
			
		||||
  //   width: 100%;
 | 
			
		||||
  //   > .ant-upload {
 | 
			
		||||
  //     padding: 0px;
 | 
			
		||||
  .ant-upload-picture-card-wrapper {
 | 
			
		||||
    height: 100px;
 | 
			
		||||
    .ant-upload.ant-upload-select-picture-card {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      > .ant-upload {
 | 
			
		||||
        padding: 0px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
 | 
			
		||||
    <a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
 | 
			
		||||
      <SpanTitle>公司描述</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="名称" prop="name">
 | 
			
		||||
        <a-input v-model="infoData.name" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="描述">
 | 
			
		||||
        <a-input v-model="infoData.description" type="textarea" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>公司地址</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="国家/地区">
 | 
			
		||||
        <a-input v-model="infoData.country" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="城市">
 | 
			
		||||
        <a-input v-model="infoData.city" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="地址">
 | 
			
		||||
        <a-input v-model="infoData.address" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="邮编">
 | 
			
		||||
        <a-input v-model="infoData.postCode" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>联系方式</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="网站">
 | 
			
		||||
        <a-input v-model="infoData.website" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="电话号码" prop="phone">
 | 
			
		||||
        <a-input v-model="infoData.phone" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="传真号码" prop="faxCode">
 | 
			
		||||
        <a-input v-model="infoData.faxCode" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="电子邮箱" prop="email">
 | 
			
		||||
        <a-input v-model="infoData.email" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>公司标识</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="Messenger地址" prop="messenger">
 | 
			
		||||
        <a-input v-model="infoData.messenger" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="部署域名" prop="domainName">
 | 
			
		||||
        <a-input v-model="infoData.domainName" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="公司logo">
 | 
			
		||||
        <a-space>
 | 
			
		||||
          <a-upload
 | 
			
		||||
            :disabled="!isEditable"
 | 
			
		||||
            name="avatar"
 | 
			
		||||
            list-type="picture-card"
 | 
			
		||||
            class="avatar-uploader"
 | 
			
		||||
            :show-upload-list="false"
 | 
			
		||||
            :customRequest="customRequest"
 | 
			
		||||
            :before-upload="beforeUpload"
 | 
			
		||||
            :style="{ width: '400px', height: '80px' }"
 | 
			
		||||
            accept=".png,.jpg,.jpeg"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              class="ops-setting-companyinfo-upload-show"
 | 
			
		||||
              v-if="infoData.logoName"
 | 
			
		||||
              :style="{ width: '400px', height: '80px' }"
 | 
			
		||||
              @click="eidtImageOption.type = 'Logo'"
 | 
			
		||||
            >
 | 
			
		||||
              <img :src="`/api/common-setting/v1/file/${infoData.logoName}`" alt="avatar" />
 | 
			
		||||
              <a-icon
 | 
			
		||||
                v-if="isEditable"
 | 
			
		||||
                type="minus-circle"
 | 
			
		||||
                theme="filled"
 | 
			
		||||
                class="delete-icon"
 | 
			
		||||
                @click.stop="deleteLogo"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else @click="eidtImageOption.type = 'Logo'">
 | 
			
		||||
              <a-icon type="plus" />
 | 
			
		||||
              <div class="ant-upload-text">上传</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-upload>
 | 
			
		||||
 | 
			
		||||
          <a-upload
 | 
			
		||||
            :disabled="!isEditable"
 | 
			
		||||
            name="avatar"
 | 
			
		||||
            list-type="picture-card"
 | 
			
		||||
            class="avatar-uploader"
 | 
			
		||||
            :show-upload-list="false"
 | 
			
		||||
            :customRequest="customRequest"
 | 
			
		||||
            :before-upload="beforeUpload"
 | 
			
		||||
            :style="{ width: '82px', height: '82px' }"
 | 
			
		||||
            accept=".png,.jpg,.jpeg"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              class="ops-setting-companyinfo-upload-show"
 | 
			
		||||
              v-if="infoData.smallLogoName"
 | 
			
		||||
              :style="{ width: '82px', height: '82px' }"
 | 
			
		||||
              @click="eidtImageOption.type = 'SmallLogo'"
 | 
			
		||||
            >
 | 
			
		||||
              <img :src="`/api/common-setting/v1/file/${infoData.smallLogoName}`" alt="avatar" />
 | 
			
		||||
              <a-icon
 | 
			
		||||
                v-if="isEditable"
 | 
			
		||||
                type="minus-circle"
 | 
			
		||||
                theme="filled"
 | 
			
		||||
                class="delete-icon"
 | 
			
		||||
                @click.stop="deleteSmallLogo"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else @click="eidtImageOption.type = 'SmallLogo'">
 | 
			
		||||
              <a-icon type="plus" />
 | 
			
		||||
              <div class="ant-upload-text">上传</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-upload>
 | 
			
		||||
        </a-space>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
 | 
			
		||||
        <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
        <a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
    <edit-image
 | 
			
		||||
      v-if="showEditImage"
 | 
			
		||||
      :show="showEditImage"
 | 
			
		||||
      :image="editImage"
 | 
			
		||||
      :title="eidtImageOption.title"
 | 
			
		||||
      :eidtImageOption="eidtImageOption"
 | 
			
		||||
      @save="submitImage"
 | 
			
		||||
      @close="showEditImage = false"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo } from '@/api/company'
 | 
			
		||||
import { postImageFile } from '@/api/file'
 | 
			
		||||
import { mapMutations, mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../components/spanTitle.vue'
 | 
			
		||||
import EditImage from '../components/EditImage.vue'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'CompanyInfo',
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  components: { SpanTitle, EditImage },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { span: 3 },
 | 
			
		||||
      wrapperCol: { span: 10 },
 | 
			
		||||
      infoData: {
 | 
			
		||||
        name: '',
 | 
			
		||||
        description: '',
 | 
			
		||||
        address: '',
 | 
			
		||||
        city: '',
 | 
			
		||||
        postCode: '',
 | 
			
		||||
        country: '',
 | 
			
		||||
        website: '',
 | 
			
		||||
        phone: '',
 | 
			
		||||
        faxCode: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        logoName: '',
 | 
			
		||||
        smallLogoName: '',
 | 
			
		||||
        messenger: '',
 | 
			
		||||
        domainName: '',
 | 
			
		||||
      },
 | 
			
		||||
      rule: {
 | 
			
		||||
        name: [{ required: true, whitespace: true, message: '请输入名称', trigger: 'blur' }],
 | 
			
		||||
        phone: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^([0-9]|-)+$', 'g'),
 | 
			
		||||
            message: '请输入正确的电话号码',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        faxCode: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^([0-9]|-)+$', 'g'),
 | 
			
		||||
            message: '请输入正确的传真号码',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        email: [
 | 
			
		||||
          {
 | 
			
		||||
            required: false,
 | 
			
		||||
            whitespace: true,
 | 
			
		||||
            pattern: new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$', 'g'),
 | 
			
		||||
            message: '请输入正确的邮箱地址',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      getId: -1,
 | 
			
		||||
      showEditImage: false,
 | 
			
		||||
      editImage: null,
 | 
			
		||||
      eidtImageOption: {
 | 
			
		||||
        type: 'Logo',
 | 
			
		||||
        fixedNumber: [15, 4],
 | 
			
		||||
        title: '编辑企业logo',
 | 
			
		||||
        previewWidth: '200px',
 | 
			
		||||
        previewHeight: '40px',
 | 
			
		||||
        autoCropWidth: 200,
 | 
			
		||||
        autoCropHeight: 40,
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  async mounted() {
 | 
			
		||||
    const res = await getCompanyInfo()
 | 
			
		||||
    if (!res.id) {
 | 
			
		||||
      this.getId = -1
 | 
			
		||||
    } else {
 | 
			
		||||
      this.infoData = res.info
 | 
			
		||||
      this.getId = res.id
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '公司信息', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    ...mapMutations(['SET_FILENAME', 'SET_SMALL_FILENAME']),
 | 
			
		||||
    deleteLogo() {
 | 
			
		||||
      this.infoData.logoName = ''
 | 
			
		||||
    },
 | 
			
		||||
    deleteSmallLogo() {
 | 
			
		||||
      this.infoData.smallLogoName = ''
 | 
			
		||||
    },
 | 
			
		||||
    async onSubmit() {
 | 
			
		||||
      this.$refs.infoData.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          if (this.getId === -1) {
 | 
			
		||||
            await postCompanyInfo(this.infoData)
 | 
			
		||||
          } else {
 | 
			
		||||
            await putCompanyInfo(this.getId, this.infoData)
 | 
			
		||||
          }
 | 
			
		||||
          this.SET_FILENAME(this.infoData.logoName)
 | 
			
		||||
          this.SET_SMALL_FILENAME(this.infoData.smallFileName)
 | 
			
		||||
          this.$message.success('保存成功')
 | 
			
		||||
        } else {
 | 
			
		||||
          this.$message.warning('检查您的输入是否正确!')
 | 
			
		||||
          return false
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.infoData = {
 | 
			
		||||
        name: '',
 | 
			
		||||
        description: '',
 | 
			
		||||
        address: '',
 | 
			
		||||
        city: '',
 | 
			
		||||
        postCode: '',
 | 
			
		||||
        country: '',
 | 
			
		||||
        website: '',
 | 
			
		||||
        phone: '',
 | 
			
		||||
        faxCode: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        logoName: '',
 | 
			
		||||
        smallLogoName: '',
 | 
			
		||||
        messenger: '',
 | 
			
		||||
        domainName: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    customRequest(file) {
 | 
			
		||||
      const reader = new FileReader()
 | 
			
		||||
      var self = this
 | 
			
		||||
      if (this.eidtImageOption.type === 'Logo') {
 | 
			
		||||
        this.eidtImageOption = {
 | 
			
		||||
          type: 'Logo',
 | 
			
		||||
          fixedNumber: [20, 4],
 | 
			
		||||
          title: '编辑企业logo',
 | 
			
		||||
          previewWidth: '200px',
 | 
			
		||||
          previewHeight: '40px',
 | 
			
		||||
          autoCropWidth: 200,
 | 
			
		||||
          autoCropHeight: 40,
 | 
			
		||||
        }
 | 
			
		||||
      } else if (this.eidtImageOption.type === 'SmallLogo') {
 | 
			
		||||
        this.eidtImageOption = {
 | 
			
		||||
          type: 'SmallLogo',
 | 
			
		||||
          fixedNumber: [4, 4],
 | 
			
		||||
          title: '编辑企业logo缩略图',
 | 
			
		||||
          previewWidth: '80px',
 | 
			
		||||
          previewHeight: '80px',
 | 
			
		||||
          autoCropWidth: 250,
 | 
			
		||||
          autoCropHeight: 250,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      reader.onload = function(e) {
 | 
			
		||||
        let result
 | 
			
		||||
        if (typeof e.target.result === 'object') {
 | 
			
		||||
          // 把Array Buffer转化为blob 如果是base64不需要
 | 
			
		||||
          result = window.URL.createObjectURL(new Blob([e.target.result]))
 | 
			
		||||
        } else {
 | 
			
		||||
          result = e.target.result
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.editImage = result
 | 
			
		||||
        self.showEditImage = true
 | 
			
		||||
      }
 | 
			
		||||
      reader.readAsDataURL(file.file)
 | 
			
		||||
    },
 | 
			
		||||
    submitImage(file) {
 | 
			
		||||
      postImageFile(file).then((res) => {
 | 
			
		||||
        if (res.file_name) {
 | 
			
		||||
          if (this.eidtImageOption.type === 'Logo') {
 | 
			
		||||
            this.infoData.logoName = res.file_name
 | 
			
		||||
          } else if (this.eidtImageOption.type === 'SmallLogo') {
 | 
			
		||||
            this.infoData.smallLogoName = res.file_name
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beforeUpload(file) {
 | 
			
		||||
      const isLt2M = file.size / 1024 / 1024 < 2
 | 
			
		||||
      if (!isLt2M) {
 | 
			
		||||
        this.$message.error('图片大小不可超过2MB!')
 | 
			
		||||
      }
 | 
			
		||||
      return isLt2M
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less">
 | 
			
		||||
.ops-setting-companyinfo {
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border-radius: 15px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  margin-bottom: -24px;
 | 
			
		||||
  .ops-setting-companyinfo-upload-show {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 290px;
 | 
			
		||||
    height: 100px;
 | 
			
		||||
    max-height: 100px;
 | 
			
		||||
    img {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .delete-icon {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .ant-upload:hover .delete-icon {
 | 
			
		||||
    display: block;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 5px;
 | 
			
		||||
    right: 5px;
 | 
			
		||||
    color: rgb(247, 85, 85);
 | 
			
		||||
  }
 | 
			
		||||
  .ant-form-item {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
  .ant-form-item label {
 | 
			
		||||
    padding-right: 10px;
 | 
			
		||||
  }
 | 
			
		||||
  .avatar-uploader > .ant-upload {
 | 
			
		||||
    // max-width: 100px;
 | 
			
		||||
    max-height: 100px;
 | 
			
		||||
  }
 | 
			
		||||
  // .ant-upload.ant-upload-select-picture-card {
 | 
			
		||||
  //   width: 100%;
 | 
			
		||||
  //   > .ant-upload {
 | 
			
		||||
  //     padding: 0px;
 | 
			
		||||
  .ant-upload-picture-card-wrapper {
 | 
			
		||||
    height: 100px;
 | 
			
		||||
    .ant-upload.ant-upload-select-picture-card {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      > .ant-upload {
 | 
			
		||||
        padding: 0px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										122
									
								
								cmdb-ui/src/views/setting/notice/bot.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								cmdb-ui/src/views/setting/notice/bot.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <vxe-table
 | 
			
		||||
      ref="xTable"
 | 
			
		||||
      :data="tableData"
 | 
			
		||||
      size="mini"
 | 
			
		||||
      stripe
 | 
			
		||||
      class="ops-stripe-table"
 | 
			
		||||
      show-overflow
 | 
			
		||||
      :edit-config="{ showIcon: false, trigger: 'manual', mode: 'row' }"
 | 
			
		||||
    >
 | 
			
		||||
      <vxe-column v-for="col in columns" :key="col.field" :field="col.field" :title="col.title" :edit-render="{}">
 | 
			
		||||
        <template #header> <span v-if="col.required" :style="{ color: 'red' }">* </span>{{ col.title }} </template>
 | 
			
		||||
        <template #edit="{ row }">
 | 
			
		||||
          <vxe-input v-model="row[col.field]" type="text"></vxe-input>
 | 
			
		||||
        </template>
 | 
			
		||||
      </vxe-column>
 | 
			
		||||
      <vxe-column title="操作" width="80" v-if="!disabled">
 | 
			
		||||
        <template #default="{ row }">
 | 
			
		||||
          <template v-if="$refs.xTable.isActiveByRow(row)">
 | 
			
		||||
            <a @click="saveRowEvent(row)"><a-icon type="save"/></a>
 | 
			
		||||
          </template>
 | 
			
		||||
          <a-space v-else>
 | 
			
		||||
            <a @click="editRowEvent(row)"><ops-icon type="icon-xianxing-edit"/></a>
 | 
			
		||||
            <a style="color:red" @click="deleteRowEvent(row)"><ops-icon type="icon-xianxing-delete"/></a>
 | 
			
		||||
          </a-space>
 | 
			
		||||
        </template>
 | 
			
		||||
      </vxe-column>
 | 
			
		||||
    </vxe-table>
 | 
			
		||||
    <div :style="{ color: '#f5222d' }" v-if="errorFlag">请完整填写机器人配置</div>
 | 
			
		||||
    <a-button
 | 
			
		||||
      v-if="!disabled"
 | 
			
		||||
      icon="plus-circle"
 | 
			
		||||
      class="ops-button-primary"
 | 
			
		||||
      type="primary"
 | 
			
		||||
      @click="insertEvent"
 | 
			
		||||
    >添加</a-button
 | 
			
		||||
    >
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Bot',
 | 
			
		||||
  props: {
 | 
			
		||||
    columns: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default: () => [
 | 
			
		||||
        {
 | 
			
		||||
          field: 'name',
 | 
			
		||||
          title: '名称',
 | 
			
		||||
          required: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          field: 'url',
 | 
			
		||||
          title: 'Webhook地址',
 | 
			
		||||
          required: true,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    disabled: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      tableData: [],
 | 
			
		||||
      errorFlag: false,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    async insertEvent() {
 | 
			
		||||
      const $table = this.$refs.xTable
 | 
			
		||||
      const record = {
 | 
			
		||||
        name: '',
 | 
			
		||||
        url: '',
 | 
			
		||||
      }
 | 
			
		||||
      const { row: newRow } = await $table.insertAt(record, -1)
 | 
			
		||||
      await $table.setActiveRow(newRow)
 | 
			
		||||
    },
 | 
			
		||||
    saveRowEvent(row) {
 | 
			
		||||
      const $table = this.$refs.xTable
 | 
			
		||||
      $table.clearActived()
 | 
			
		||||
    },
 | 
			
		||||
    editRowEvent(row) {
 | 
			
		||||
      const $table = this.$refs.xTable
 | 
			
		||||
      $table.setActiveRow(row)
 | 
			
		||||
    },
 | 
			
		||||
    deleteRowEvent(row) {
 | 
			
		||||
      const $table = this.$refs.xTable
 | 
			
		||||
      $table.remove(row)
 | 
			
		||||
    },
 | 
			
		||||
    getData(callback) {
 | 
			
		||||
      const $table = this.$refs.xTable
 | 
			
		||||
      const { fullData: _tableData } = $table.getTableData()
 | 
			
		||||
      const requiredObj = {}
 | 
			
		||||
      this.columns.forEach((col) => {
 | 
			
		||||
        if (col.required) {
 | 
			
		||||
          requiredObj[col.field] = true
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      let flag = true
 | 
			
		||||
      _tableData.forEach((td) => {
 | 
			
		||||
        Object.keys(requiredObj).forEach((key) => {
 | 
			
		||||
          if (requiredObj[key]) {
 | 
			
		||||
            flag = !!(flag && td[`${key}`])
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
      this.errorFlag = !flag
 | 
			
		||||
      callback(flag, _tableData)
 | 
			
		||||
    },
 | 
			
		||||
    setData(value) {
 | 
			
		||||
      this.tableData = value
 | 
			
		||||
      this.errorFlag = false
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style></style>
 | 
			
		||||
							
								
								
									
										151
									
								
								cmdb-ui/src/views/setting/notice/dingding.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								cmdb-ui/src/views/setting/notice/dingding.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-dingding-wrapper" :style="{ height: `${windowHeight - 64}px` }">
 | 
			
		||||
    <a-form-model ref="dingdingForm" :model="dingdingData" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
      <SpanTitle>基础设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="应用Key">
 | 
			
		||||
        <a-input v-model="dingdingData.appKey" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="应用密码">
 | 
			
		||||
        <a-input v-model="dingdingData.appSecret" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="机器人码">
 | 
			
		||||
        <a-input v-model="dingdingData.robotCode" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="机器人">
 | 
			
		||||
        <Bot
 | 
			
		||||
          ref="bot"
 | 
			
		||||
          :disabled="!isEditable"
 | 
			
		||||
          :columns="[
 | 
			
		||||
            {
 | 
			
		||||
              field: 'name',
 | 
			
		||||
              title: '名称',
 | 
			
		||||
              required: true,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              field: 'url',
 | 
			
		||||
              title: 'Webhook地址',
 | 
			
		||||
              required: true,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              field: 'token',
 | 
			
		||||
              title: 'token',
 | 
			
		||||
              required: false,
 | 
			
		||||
            },
 | 
			
		||||
          ]"
 | 
			
		||||
        />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <!-- <a-form-model-item label="测试邮件设置">
 | 
			
		||||
        <a-button type="primary" ghost>测试回收箱</a-button>
 | 
			
		||||
        <br />
 | 
			
		||||
        <span
 | 
			
		||||
          class="notice-dingding-wrapper-tips"
 | 
			
		||||
        ><ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" /> 邮件接收失败</span
 | 
			
		||||
        >
 | 
			
		||||
        <br />
 | 
			
		||||
        <span>邮箱服务器未配置,请配置一个邮箱服务器 | <a>故障诊断</a></span>
 | 
			
		||||
      </a-form-model-item> -->
 | 
			
		||||
      <a-row v-if="isEditable">
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
            <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
            <a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../components/spanTitle.vue'
 | 
			
		||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
import Bot from './bot.vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'NoticeDingding',
 | 
			
		||||
  components: { SpanTitle, Bot },
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { lg: 3, md: 5, sm: 8 },
 | 
			
		||||
      wrapperCol: { lg: 15, md: 19, sm: 16 },
 | 
			
		||||
      id: null,
 | 
			
		||||
      dingdingData: {
 | 
			
		||||
        appKey: '',
 | 
			
		||||
        appSecret: '',
 | 
			
		||||
        robotCode: '',
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '通知设置', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getData()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getData() {
 | 
			
		||||
      getNoticeConfigByPlatform({ platform: 'dingdingApp' }).then((res) => {
 | 
			
		||||
        this.id = res?.id ?? null
 | 
			
		||||
        if (this.id) {
 | 
			
		||||
          this.dingdingData = res.info
 | 
			
		||||
          this.$refs.bot.setData(res?.info?.bot)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    onSubmit() {
 | 
			
		||||
      this.$refs.dingdingForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          this.$refs.bot.getData(async (flag, bot) => {
 | 
			
		||||
            if (flag) {
 | 
			
		||||
              if (this.id) {
 | 
			
		||||
                await putNoticeConfigByPlatform(this.id, { info: { ...this.dingdingData, bot, label: '钉钉' } })
 | 
			
		||||
              } else {
 | 
			
		||||
                await postNoticeConfigByPlatform({
 | 
			
		||||
                  platform: 'dingdingApp',
 | 
			
		||||
                  info: { ...this.dingdingData, bot, label: '钉钉' },
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
              this.$message.success('保存成功')
 | 
			
		||||
              this.getData()
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.dingdingData = {
 | 
			
		||||
        appKey: '',
 | 
			
		||||
        appSecret: '',
 | 
			
		||||
        robotCode: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.notice-dingding-wrapper {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  margin-bottom: -24px;
 | 
			
		||||
  border-radius: 15px;
 | 
			
		||||
  .notice-dingding-wrapper-tips {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    background-color: #ffdfdf;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 0 12px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    color: #000000;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										17
									
								
								cmdb-ui/src/views/setting/notice/email/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cmdb-ui/src/views/setting/notice/email/index.less
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
.notice-email-wrapper {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  padding-top: 24px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  .notice-email-error-tips {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    background-color: #ffdfdf;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 0 12px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    color: #000000;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
  }
 | 
			
		||||
  .ant-form-item {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								cmdb-ui/src/views/setting/notice/email/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								cmdb-ui/src/views/setting/notice/email/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div :style="{ marginBottom: '-24px' }">
 | 
			
		||||
    <a-tabs :activeKey="activeKey" @change="changeTab" class="ops-tab" type="card">
 | 
			
		||||
      <!-- <a-tab-pane key="1" tab="接收服务器">
 | 
			
		||||
        <Receive />
 | 
			
		||||
      </a-tab-pane> -->
 | 
			
		||||
      <a-tab-pane key="2" tab="发送服务器">
 | 
			
		||||
        <Send />
 | 
			
		||||
      </a-tab-pane>
 | 
			
		||||
    </a-tabs>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import Receive from './receive.vue'
 | 
			
		||||
import Send from './send.vue'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'NoticeEmail',
 | 
			
		||||
  components: { Receive, Send },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      activeKey: '2',
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    changeTab(activeKey) {
 | 
			
		||||
      this.activeKey = activeKey
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style></style>
 | 
			
		||||
							
								
								
									
										196
									
								
								cmdb-ui/src/views/setting/notice/email/receive.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								cmdb-ui/src/views/setting/notice/email/receive.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-email-wrapper" :style="{ height: `${windowHeight - 104}px` }">
 | 
			
		||||
    <a-form-model :model="settingData" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
      <SpanTitle>基础设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="连接协议">
 | 
			
		||||
        <a-radio-group v-model="settingData.connectProtocol" :default-value="1" @change="changeConnectProtocol">
 | 
			
		||||
          <a-radio :value="1" :default-checked="true"> POP/IMAP/POPS/IMAPS </a-radio>
 | 
			
		||||
          <a-radio :value="2"> EWS(Exchange Web服务) </a-radio>
 | 
			
		||||
        </a-radio-group>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="认证类型">
 | 
			
		||||
        <a-select v-model="settingData.authentication">
 | 
			
		||||
          <a-select-option value="Base"> 基本 </a-select-option>
 | 
			
		||||
          <a-select-option value="OAuth"> OAuth </a-select-option>
 | 
			
		||||
        </a-select>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="服务器名/IP地址" prop="IP">
 | 
			
		||||
        <a-input v-model="settingData.IP" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="用户名">
 | 
			
		||||
        <a-input v-model="settingData.username" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="密码">
 | 
			
		||||
        <a-input v-model="settingData.password" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="邮件地址">
 | 
			
		||||
        <a-input v-model="settingData.email" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <template v-if="settingData.connectProtocol === 1">
 | 
			
		||||
        <a-form-model-item label="邮件类型">
 | 
			
		||||
          <a-select v-model="settingData.emailType">
 | 
			
		||||
            <a-select-option value="POP"> POP </a-select-option>
 | 
			
		||||
            <a-select-option value="IMAP"> IMAP </a-select-option>
 | 
			
		||||
            <a-select-option value="POPS"> POPS </a-select-option>
 | 
			
		||||
            <a-select-option value="IMAPS"> IMAPS </a-select-option>
 | 
			
		||||
          </a-select>
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
        <a-form-model-item label="端口">
 | 
			
		||||
          <a-input v-model="settingData.port" />
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
      </template>
 | 
			
		||||
      <a-form-model-item label="测试邮件设置">
 | 
			
		||||
        <a-button type="primary" ghost>测试回收箱</a-button>
 | 
			
		||||
        <br />
 | 
			
		||||
        <span class="notice-email-error-tips">
 | 
			
		||||
          <ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" />
 | 
			
		||||
          邮件接收失败
 | 
			
		||||
        </span>
 | 
			
		||||
        <br />
 | 
			
		||||
        <span
 | 
			
		||||
        >邮箱服务器未配置,请配置一个邮箱服务器 <a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
 | 
			
		||||
          <a>故障诊断</a></span
 | 
			
		||||
        >
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>邮件设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="获取邮件间隔" :wrapperCol="{ span: 4 }">
 | 
			
		||||
        <a-input class="ant-input-after" v-model="settingData.getEmailTimeout" />
 | 
			
		||||
        <span :style="{ position: 'absolute', marginLeft: '8px' }">分</span>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-row>
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-checkbox :default-checked="false" disabled>启动代理服务器</a-checkbox>
 | 
			
		||||
          <a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px' }" />
 | 
			
		||||
          <a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
 | 
			
		||||
          <a @click="configProxySetting">配置代理设置</a>
 | 
			
		||||
          <br />
 | 
			
		||||
          <a-checkbox :default-checked="false">启动邮件测试</a-checkbox>
 | 
			
		||||
          <br /><br />
 | 
			
		||||
          <a-checkbox :default-checked="false" @change="changeCreateReqByEmail">禁用通过邮件创建请求</a-checkbox>
 | 
			
		||||
          <br />
 | 
			
		||||
          <template v-if="settingData.banReqByEmail">
 | 
			
		||||
            <strong>指定允许的邮件/域名,逗号分隔多个值</strong>
 | 
			
		||||
            <a-input type="textarea" :style="{ borderRadius: '8px', borderColor: '#2F54EB' }" />
 | 
			
		||||
            <p :style="{ fontSize: '12px' }">例如:user@domain.com,*@domain.com</p>
 | 
			
		||||
            <p :style="{ fontSize: '12px' }">限制不能适用于已在会话中的请求,它将聚集到它的上级工单中</p>
 | 
			
		||||
          </template>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
      <SpanTitle>消息设置</SpanTitle>
 | 
			
		||||
      <a-row>
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-checkbox :default-checked="false">将消息移动到错误的文件夹</a-checkbox>
 | 
			
		||||
          <a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px' }" />
 | 
			
		||||
          <a-divider type="vertical" :style="{ backgroundColor: '#2F54EB' }" />
 | 
			
		||||
          <a href="#">了解更多</a>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
      <br /><br />
 | 
			
		||||
      <a-row>
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
            <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
            <a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
    <a-modal dialogClass="ops-modal" width="500px" v-model="visible" title="配置代理设置">
 | 
			
		||||
      <a-form-model v-model="proxySetting" :label-col="{ span: 4 }" :wrapper-col="{ span: 19 }">
 | 
			
		||||
        <a-form-model-item label="主机">
 | 
			
		||||
          <a-input v-model="proxySetting.host" />
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
        <a-form-model-item label="端口">
 | 
			
		||||
          <a-input v-model="proxySetting.port" />
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
        <a-form-model-item label="用户名">
 | 
			
		||||
          <a-input v-model="proxySetting.username" />
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
        <a-form-model-item label="密码">
 | 
			
		||||
          <a-input v-model="proxySetting.password" />
 | 
			
		||||
        </a-form-model-item>
 | 
			
		||||
      </a-form-model>
 | 
			
		||||
    </a-modal>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../../components/spanTitle.vue'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Receive',
 | 
			
		||||
  components: { SpanTitle },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { span: 3 },
 | 
			
		||||
      wrapperCol: { span: 10 },
 | 
			
		||||
      settingData: {
 | 
			
		||||
        connectProtocol: 1,
 | 
			
		||||
        authentication: 'Base',
 | 
			
		||||
        IP: '',
 | 
			
		||||
        username: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        emailType: '',
 | 
			
		||||
        port: '',
 | 
			
		||||
 | 
			
		||||
        getEmailTimeout: '',
 | 
			
		||||
        activeProxy: false,
 | 
			
		||||
        activeEmailDebug: false,
 | 
			
		||||
        banReqByEmail: false,
 | 
			
		||||
 | 
			
		||||
        transfromMessage: false,
 | 
			
		||||
      },
 | 
			
		||||
      visible: false,
 | 
			
		||||
      proxySetting: {
 | 
			
		||||
        host: '',
 | 
			
		||||
        post: '',
 | 
			
		||||
        username: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    changeConnectProtocol(e) {
 | 
			
		||||
      console.log(e.target.value)
 | 
			
		||||
    },
 | 
			
		||||
    changeCreateReqByEmail(e) {
 | 
			
		||||
      this.settingData.banReqByEmail = e.target.checked
 | 
			
		||||
    },
 | 
			
		||||
    configProxySetting() {
 | 
			
		||||
      this.visible = true
 | 
			
		||||
    },
 | 
			
		||||
    onSubmit() {
 | 
			
		||||
      console.log(this.settingData)
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.settingData = {
 | 
			
		||||
        connectProtocol: 1,
 | 
			
		||||
        authentication: '',
 | 
			
		||||
        IP: '',
 | 
			
		||||
        username: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
        email: '',
 | 
			
		||||
        emailType: '',
 | 
			
		||||
        port: '',
 | 
			
		||||
 | 
			
		||||
        getEmailTimeout: '',
 | 
			
		||||
        activeProxy: false,
 | 
			
		||||
        activeEmailDebug: false,
 | 
			
		||||
        banReqByEmail: false,
 | 
			
		||||
 | 
			
		||||
        transfromMessage: false,
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
@import './index.less';
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										169
									
								
								cmdb-ui/src/views/setting/notice/email/send.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								cmdb-ui/src/views/setting/notice/email/send.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-email-wrapper" :style="{ height: `${windowHeight - 104}px` }">
 | 
			
		||||
    <a-form-model ref="sendForm" :model="settingData" :label-col="labelCol" :rules="rules" :wrapper-col="wrapperCol">
 | 
			
		||||
      <SpanTitle>基础设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="是否加密">
 | 
			
		||||
        <a-radio-group v-model="settingData.tls" :disabled="!isEditable">
 | 
			
		||||
          <a-radio :value="true">
 | 
			
		||||
            是
 | 
			
		||||
          </a-radio>
 | 
			
		||||
          <a-radio :value="false">
 | 
			
		||||
            否
 | 
			
		||||
          </a-radio>
 | 
			
		||||
        </a-radio-group>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="端口" prop="port">
 | 
			
		||||
        <a-input v-model="settingData.port" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="邮件服务器" prop="host">
 | 
			
		||||
        <a-input v-model="settingData.host" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="用户名" prop="account">
 | 
			
		||||
        <a-input v-model="settingData.account" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="密码" prop="password">
 | 
			
		||||
        <a-input-password v-model="settingData.password" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <SpanTitle>邮件测试</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="测试发送邮件地址" prop="receive_address">
 | 
			
		||||
        <a-input v-model="settingData.receive_address" :disabled="!isEditable">
 | 
			
		||||
          <span
 | 
			
		||||
            v-if="isEditable"
 | 
			
		||||
            :style="{ cursor: 'pointer' }"
 | 
			
		||||
            @click="testSendEmail"
 | 
			
		||||
            slot="addonAfter"
 | 
			
		||||
          >测试邮件发送</span
 | 
			
		||||
          >
 | 
			
		||||
        </a-input>
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-row v-if="isEditable">
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
            <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
            <a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../../components/spanTitle.vue'
 | 
			
		||||
import {
 | 
			
		||||
  getNoticeConfigByPlatform,
 | 
			
		||||
  postNoticeConfigByPlatform,
 | 
			
		||||
  putNoticeConfigByPlatform,
 | 
			
		||||
  sendTestEmail,
 | 
			
		||||
} from '@/api/noticeSetting'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Send',
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  components: { SpanTitle },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { lg: 3, md: 5, sm: 8 },
 | 
			
		||||
      wrapperCol: { lg: 10, md: 12, sm: 12 },
 | 
			
		||||
      id: null,
 | 
			
		||||
      settingData: {
 | 
			
		||||
        tls: true,
 | 
			
		||||
        host: '',
 | 
			
		||||
        account: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
        port: '',
 | 
			
		||||
        receive_address: '',
 | 
			
		||||
      },
 | 
			
		||||
      rules: {
 | 
			
		||||
        port: [{ required: true, message: '请输入端口', trigger: 'blur' }],
 | 
			
		||||
        host: [{ required: true, whitespace: true, message: '请输入服务器', trigger: 'blur' }],
 | 
			
		||||
        account: [
 | 
			
		||||
          { required: true, whitespace: true, message: '请输入用户名', trigger: 'blur' },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
 | 
			
		||||
            message: '邮箱格式错误',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        password: [{ required: false, whitespace: true, message: '请输入密码', trigger: 'blur' }],
 | 
			
		||||
        receive_address: [
 | 
			
		||||
          { required: false, whitespace: true, message: '请输入测试发送邮件地址', trigger: 'blur' },
 | 
			
		||||
          {
 | 
			
		||||
            pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
 | 
			
		||||
            message: '邮箱格式错误',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '通知设置', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    'settingData.tls': {
 | 
			
		||||
      handler(newV, oldV) {
 | 
			
		||||
        if (newV === false) {
 | 
			
		||||
          this.settingData.port = 25
 | 
			
		||||
        }
 | 
			
		||||
        if (newV === true) {
 | 
			
		||||
          this.settingData.port = 465
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      immediate: true,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getData()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getData() {
 | 
			
		||||
      getNoticeConfigByPlatform({ platform: 'email' }).then((res) => {
 | 
			
		||||
        this.id = res?.id ?? null
 | 
			
		||||
        if (this.id) {
 | 
			
		||||
          this.settingData = res.info
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async testSendEmail() {
 | 
			
		||||
      await sendTestEmail(this.settingData.receive_address, {
 | 
			
		||||
        info: { ...this.settingData, receive_address: undefined },
 | 
			
		||||
      })
 | 
			
		||||
      this.$message.success('已发送邮件,请查收')
 | 
			
		||||
    },
 | 
			
		||||
    onSubmit() {
 | 
			
		||||
      this.$refs.sendForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          if (this.id) {
 | 
			
		||||
            await putNoticeConfigByPlatform(this.id, { info: { ...this.settingData, label: '邮箱' } })
 | 
			
		||||
          } else {
 | 
			
		||||
            await postNoticeConfigByPlatform({ platform: 'email', info: { ...this.settingData, label: '邮箱' } })
 | 
			
		||||
          }
 | 
			
		||||
          this.$message.success('保存成功')
 | 
			
		||||
          this.getData()
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.settingData = {
 | 
			
		||||
        tls: true,
 | 
			
		||||
        host: '',
 | 
			
		||||
        account: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
        port: 25,
 | 
			
		||||
        receive_address: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
@import './index.less';
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										131
									
								
								cmdb-ui/src/views/setting/notice/feishu.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								cmdb-ui/src/views/setting/notice/feishu.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-feishu-wrapper" :style="{ height: `${windowHeight - 64}px` }">
 | 
			
		||||
    <a-form-model ref="feishuForm" :model="feishuData" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
      <SpanTitle>基础设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="应用ID">
 | 
			
		||||
        <a-input v-model="feishuData.id" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="应用密码">
 | 
			
		||||
        <a-input v-model="feishuData.password" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="机器人">
 | 
			
		||||
        <Bot
 | 
			
		||||
          ref="bot"
 | 
			
		||||
          :disabled="!isEditable"
 | 
			
		||||
          :columns="[
 | 
			
		||||
            {
 | 
			
		||||
              field: 'name',
 | 
			
		||||
              title: '名称',
 | 
			
		||||
              required: true,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              field: 'url',
 | 
			
		||||
              title: 'Webhook地址',
 | 
			
		||||
              required: true,
 | 
			
		||||
            },
 | 
			
		||||
          ]"
 | 
			
		||||
        />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-row v-if="isEditable">
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
            <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
            <a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../components/spanTitle.vue'
 | 
			
		||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
import Bot from './bot.vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'NoticeFeishu',
 | 
			
		||||
  components: { SpanTitle, Bot },
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { lg: 3, md: 5, sm: 8 },
 | 
			
		||||
      wrapperCol: { lg: 15, md: 19, sm: 16 },
 | 
			
		||||
      id: null,
 | 
			
		||||
      feishuData: {
 | 
			
		||||
        id: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '通知设置', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getData()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getData() {
 | 
			
		||||
      getNoticeConfigByPlatform({ platform: 'feishuApp' }).then((res) => {
 | 
			
		||||
        this.id = res?.id ?? null
 | 
			
		||||
        if (this.id) {
 | 
			
		||||
          this.feishuData = res.info
 | 
			
		||||
          this.$refs.bot.setData(res?.info?.bot)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    onSubmit() {
 | 
			
		||||
      this.$refs.feishuForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          this.$refs.bot.getData(async (flag, bot) => {
 | 
			
		||||
            if (flag) {
 | 
			
		||||
              if (this.id) {
 | 
			
		||||
                await putNoticeConfigByPlatform(this.id, { info: { ...this.feishuData, bot, label: '飞书' } })
 | 
			
		||||
              } else {
 | 
			
		||||
                await postNoticeConfigByPlatform({
 | 
			
		||||
                  platform: 'feishuApp',
 | 
			
		||||
                  info: { ...this.feishuData, bot, label: '飞书' },
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
              this.$message.success('保存成功')
 | 
			
		||||
              this.getData()
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.feishuData = {
 | 
			
		||||
        id: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.notice-feishu-wrapper {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  margin-bottom: -24px;
 | 
			
		||||
  border-radius: 15px;
 | 
			
		||||
  .notice-feishu-wrapper-tips {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    background-color: #ffdfdf;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 0 12px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    color: #000000;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										135
									
								
								cmdb-ui/src/views/setting/notice/wx.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								cmdb-ui/src/views/setting/notice/wx.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-wx-wrapper" :style="{ height: `${windowHeight - 64}px` }">
 | 
			
		||||
    <a-form-model ref="wxForm" :model="wxData" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
      <SpanTitle>基础设置</SpanTitle>
 | 
			
		||||
      <a-form-model-item label="企业ID">
 | 
			
		||||
        <a-input v-model="wxData.corpid" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="自建应用ID">
 | 
			
		||||
        <a-input v-model="wxData.agentid" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="自建应用密码">
 | 
			
		||||
        <a-input-password v-model="wxData.corpsecret" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="ITSM AppId">
 | 
			
		||||
        <a-input v-model="wxData.itsm_app_id" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <a-form-model-item label="机器人">
 | 
			
		||||
        <Bot ref="bot" :disabled="!isEditable" />
 | 
			
		||||
      </a-form-model-item>
 | 
			
		||||
      <!-- <a-form-model-item label="测试邮件设置">
 | 
			
		||||
        <a-button type="primary" ghost>测试回收箱</a-button>
 | 
			
		||||
        <br />
 | 
			
		||||
        <span
 | 
			
		||||
          class="notice-wx-wrapper-tips"
 | 
			
		||||
        ><ops-icon type="icon-shidi-quxiao" :style="{ color: '#D81E06' }" /> 邮件接收失败</span
 | 
			
		||||
        >
 | 
			
		||||
        <br />
 | 
			
		||||
        <span>邮箱服务器未配置,请配置一个邮箱服务器 | <a>故障诊断</a></span>
 | 
			
		||||
      </a-form-model-item> -->
 | 
			
		||||
      <a-row v-if="isEditable">
 | 
			
		||||
        <a-col :span="16" :offset="3">
 | 
			
		||||
          <a-form-model-item :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
            <a-button type="primary" @click="onSubmit"> 保存 </a-button>
 | 
			
		||||
            <a-button ghost type="primary" style="margin-left: 28px;" @click="resetForm"> 重置 </a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-form-model>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from 'vuex'
 | 
			
		||||
import SpanTitle from '../components/spanTitle.vue'
 | 
			
		||||
import { getNoticeConfigByPlatform, postNoticeConfigByPlatform, putNoticeConfigByPlatform } from '@/api/noticeSetting'
 | 
			
		||||
import { mixinPermissions } from '@/utils/mixin'
 | 
			
		||||
import Bot from './bot.vue'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'NoticeWx',
 | 
			
		||||
  mixins: [mixinPermissions],
 | 
			
		||||
  components: { SpanTitle, Bot },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      labelCol: { lg: 3, md: 5, sm: 8 },
 | 
			
		||||
      wrapperCol: { lg: 15, md: 19, sm: 16 },
 | 
			
		||||
      id: null,
 | 
			
		||||
      wxData: {
 | 
			
		||||
        corpid: '',
 | 
			
		||||
        agentid: '',
 | 
			
		||||
        corpsecret: '',
 | 
			
		||||
        itsm_app_id: '',
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState({
 | 
			
		||||
      windowHeight: (state) => state.windowHeight,
 | 
			
		||||
    }),
 | 
			
		||||
    isEditable() {
 | 
			
		||||
      return this.hasDetailPermission('backend', '通知设置', ['update'])
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getData()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getData() {
 | 
			
		||||
      getNoticeConfigByPlatform({ platform: 'wechatApp' }).then((res) => {
 | 
			
		||||
        this.id = res?.id ?? null
 | 
			
		||||
        if (this.id) {
 | 
			
		||||
          this.wxData = res.info
 | 
			
		||||
          this.$refs.bot.setData(res?.info?.bot)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    onSubmit() {
 | 
			
		||||
      this.$refs.wxForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          this.$refs.bot.getData(async (flag, bot) => {
 | 
			
		||||
            if (flag) {
 | 
			
		||||
              if (this.id) {
 | 
			
		||||
                await putNoticeConfigByPlatform(this.id, { info: { ...this.wxData, bot, label: '企业微信' } })
 | 
			
		||||
              } else {
 | 
			
		||||
                await postNoticeConfigByPlatform({
 | 
			
		||||
                  platform: 'wechatApp',
 | 
			
		||||
                  info: { ...this.wxData, bot, label: '企业微信' },
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
              this.$message.success('保存成功')
 | 
			
		||||
              this.getData()
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    resetForm() {
 | 
			
		||||
      this.wxData = {
 | 
			
		||||
        corpid: '',
 | 
			
		||||
        agentid: '',
 | 
			
		||||
        corpsecret: '',
 | 
			
		||||
        itsm_app_id: '',
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.notice-wx-wrapper {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  padding-top: 15px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  margin-bottom: -24px;
 | 
			
		||||
  border-radius: 15px;
 | 
			
		||||
  .notice-wx-wrapper-tips {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    background-color: #ffdfdf;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 0 12px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    color: #000000;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,368 +1,405 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="setting-person">
 | 
			
		||||
    <div class="setting-person-left">
 | 
			
		||||
      <div
 | 
			
		||||
        @click="
 | 
			
		||||
          () => {
 | 
			
		||||
            $refs.personForm.clearValidate()
 | 
			
		||||
            $nextTick(() => {
 | 
			
		||||
              current = '1'
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        :class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '1' }"
 | 
			
		||||
      >
 | 
			
		||||
        <ops-icon type="icon-shidi-yonghu" />个人信息
 | 
			
		||||
      </div>
 | 
			
		||||
      <div
 | 
			
		||||
        @click="
 | 
			
		||||
          () => {
 | 
			
		||||
            $refs.personForm.clearValidate()
 | 
			
		||||
            $nextTick(() => {
 | 
			
		||||
              current = '2'
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        :class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '2' }"
 | 
			
		||||
      >
 | 
			
		||||
        <a-icon type="unlock" theme="filled" />账号密码
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="setting-person-right">
 | 
			
		||||
      <a-form-model
 | 
			
		||||
        ref="personForm"
 | 
			
		||||
        :model="form"
 | 
			
		||||
        :rules="current === '1' ? rules1 : rules2"
 | 
			
		||||
        :colon="false"
 | 
			
		||||
        labelAlign="left"
 | 
			
		||||
        :labelCol="{ span: 4 }"
 | 
			
		||||
        :wrapperCol="{ span: 10 }"
 | 
			
		||||
      >
 | 
			
		||||
        <div v-show="current === '1'">
 | 
			
		||||
          <a-form-model-item label="头像" :style="{ display: 'flex', alignItems: 'center' }">
 | 
			
		||||
            <a-space>
 | 
			
		||||
              <a-avatar v-if="form.avatar" :src="`/api/common-setting/v1/file/${form.avatar}`" :size="64"> </a-avatar>
 | 
			
		||||
              <a-avatar v-else style="backgroundColor:#F0F5FF" :size="64">
 | 
			
		||||
                <ops-icon type="icon-shidi-yonghu" :style="{ color: '#2F54EB' }" />
 | 
			
		||||
              </a-avatar>
 | 
			
		||||
              <a-upload
 | 
			
		||||
                name="avatar"
 | 
			
		||||
                :show-upload-list="false"
 | 
			
		||||
                :customRequest="customRequest"
 | 
			
		||||
                :before-upload="beforeUpload"
 | 
			
		||||
                :style="{ width: '310px', height: '100px' }"
 | 
			
		||||
                accept=".svg,.png,.jpg,.jpeg"
 | 
			
		||||
              >
 | 
			
		||||
                <a-button type="primary" ghost size="small">更换头像</a-button>
 | 
			
		||||
              </a-upload>
 | 
			
		||||
            </a-space>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="姓名" prop="nickname">
 | 
			
		||||
            <a-input v-model="form.nickname" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="用户名">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.username }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="邮箱">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.email }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="直属上级">
 | 
			
		||||
            <div class="setting-person-right-disabled">
 | 
			
		||||
              {{ getDirectorName(allFlatEmployees, form.direct_supervisor_id) }}
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="性别">
 | 
			
		||||
            <a-select v-model="form.sex">
 | 
			
		||||
              <a-select-option value="男">男</a-select-option>
 | 
			
		||||
              <a-select-option value="女">女</a-select-option>
 | 
			
		||||
            </a-select>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="手机号" prop="mobile">
 | 
			
		||||
            <a-input v-model="form.mobile" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="部门">
 | 
			
		||||
            <div class="setting-person-right-disabled">
 | 
			
		||||
              {{ getDepartmentName(allFlatDepartments, form.department_id) }}
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="岗位">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.position_name }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="绑定信息">
 | 
			
		||||
            <a-space>
 | 
			
		||||
              <div :class="{ 'setting-person-bind': true, 'setting-person-bind-existed': form.wx_id }">
 | 
			
		||||
                <ops-icon type="ops-setting-notice-wx" />
 | 
			
		||||
              </div>
 | 
			
		||||
              <div @click="handleBindWx" class="setting-person-bind-button">
 | 
			
		||||
                {{ form.notice_info && form.notice_info.wechatApp ? '重新绑定' : '绑定' }}
 | 
			
		||||
              </div>
 | 
			
		||||
            </a-space>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-show="current === '2'">
 | 
			
		||||
          <a-form-model-item label="新密码" prop="password1">
 | 
			
		||||
            <a-input v-model="form.password1" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="确认密码" prop="password2">
 | 
			
		||||
            <a-input v-model="form.password2" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div style="margin-right: 120px">
 | 
			
		||||
          <a-form-model-item label=" ">
 | 
			
		||||
            <a-button type="primary" @click="handleSave" :style="{ width: '100%' }">保存</a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
      </a-form-model>
 | 
			
		||||
    </div>
 | 
			
		||||
    <EditImage
 | 
			
		||||
      v-if="showEditImage"
 | 
			
		||||
      :fixed-number="eidtImageOption.fixedNumber"
 | 
			
		||||
      :show="showEditImage"
 | 
			
		||||
      :image="editImage"
 | 
			
		||||
      :title="eidtImageOption.title"
 | 
			
		||||
      :preview-width="eidtImageOption.previewWidth"
 | 
			
		||||
      :preview-height="eidtImageOption.previewHeight"
 | 
			
		||||
      preview-radius="0"
 | 
			
		||||
      width="550px"
 | 
			
		||||
      save-button-title="确定"
 | 
			
		||||
      @save="submitImage"
 | 
			
		||||
      @close="showEditImage = false"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapActions, mapGetters } from 'vuex'
 | 
			
		||||
import { getAllDepartmentList } from '@/api/company'
 | 
			
		||||
import { postImageFile } from '@/api/file'
 | 
			
		||||
import {
 | 
			
		||||
  getEmployeeList,
 | 
			
		||||
  getEmployeeByUid,
 | 
			
		||||
  updateEmployeeByUid,
 | 
			
		||||
  updatePasswordByUid,
 | 
			
		||||
  bindWxByUid,
 | 
			
		||||
} from '@/api/employee'
 | 
			
		||||
import { getDepartmentName, getDirectorName } from '@/utils/util'
 | 
			
		||||
import EditImage from '../components/EditImage.vue'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Person',
 | 
			
		||||
  components: { EditImage },
 | 
			
		||||
  data() {
 | 
			
		||||
    const validatePassword = (rule, value, callback) => {
 | 
			
		||||
      if (!value) {
 | 
			
		||||
        callback(new Error('请二次确认新密码'))
 | 
			
		||||
      }
 | 
			
		||||
      if (value !== this.form.password1) {
 | 
			
		||||
        callback(new Error('两次输入密码不一致'))
 | 
			
		||||
      }
 | 
			
		||||
      callback()
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      current: '1',
 | 
			
		||||
      form: {},
 | 
			
		||||
      rules1: {
 | 
			
		||||
        nickname: [
 | 
			
		||||
          { required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
 | 
			
		||||
          { max: 20, message: '字符数须小于20' },
 | 
			
		||||
        ],
 | 
			
		||||
        mobile: [
 | 
			
		||||
          {
 | 
			
		||||
            pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
 | 
			
		||||
            message: '请输入正确的手机号',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      rules2: {
 | 
			
		||||
        password1: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
 | 
			
		||||
        password2: [{ required: true, message: '两次输入密码不一致', trigger: 'blur', validator: validatePassword }],
 | 
			
		||||
      },
 | 
			
		||||
      allFlatEmployees: [],
 | 
			
		||||
      allFlatDepartments: [],
 | 
			
		||||
      showEditImage: false,
 | 
			
		||||
      eidtImageOption: {
 | 
			
		||||
        type: 'avatar',
 | 
			
		||||
        fixedNumber: [4, 4],
 | 
			
		||||
        title: '编辑头像',
 | 
			
		||||
        previewWidth: '60px',
 | 
			
		||||
        previewHeight: '60px',
 | 
			
		||||
      },
 | 
			
		||||
      editImage: null,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapGetters(['uid']),
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getAllFlatEmployees()
 | 
			
		||||
    this.getAllFlatDepartment()
 | 
			
		||||
    this.getEmployeeByUid()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    ...mapActions(['GetInfo']),
 | 
			
		||||
    getDepartmentName,
 | 
			
		||||
    getDirectorName,
 | 
			
		||||
    getEmployeeByUid() {
 | 
			
		||||
      getEmployeeByUid(this.uid).then((res) => {
 | 
			
		||||
        this.form = { ...res }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    getAllFlatEmployees() {
 | 
			
		||||
      getEmployeeList({ block_status: 0, page_size: 99999 }).then((res) => {
 | 
			
		||||
        this.allFlatEmployees = res.data_list
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    getAllFlatDepartment() {
 | 
			
		||||
      getAllDepartmentList({ is_tree: 0 }).then((res) => {
 | 
			
		||||
        this.allFlatDepartments = res
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async handleSave() {
 | 
			
		||||
      await this.$refs.personForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          const { nickname, mobile, sex, avatar, password1 } = this.form
 | 
			
		||||
          const params = { nickname, mobile, sex, avatar }
 | 
			
		||||
          if (this.current === '1') {
 | 
			
		||||
            await updateEmployeeByUid(this.uid, params).then((res) => {
 | 
			
		||||
              this.$message.success('保存成功!')
 | 
			
		||||
              this.getEmployeeByUid()
 | 
			
		||||
              this.GetInfo()
 | 
			
		||||
            })
 | 
			
		||||
          } else {
 | 
			
		||||
            await updatePasswordByUid(this.uid, { password: password1 }).then((res) => {
 | 
			
		||||
              this.$message.success('保存成功!')
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    customRequest(file) {
 | 
			
		||||
      const reader = new FileReader()
 | 
			
		||||
      var self = this
 | 
			
		||||
      reader.onload = function(e) {
 | 
			
		||||
        let result
 | 
			
		||||
        if (typeof e.target.result === 'object') {
 | 
			
		||||
          // 把Array Buffer转化为blob 如果是base64不需要
 | 
			
		||||
          result = window.URL.createObjectURL(new Blob([e.target.result]))
 | 
			
		||||
        } else {
 | 
			
		||||
          result = e.target.result
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.editImage = result
 | 
			
		||||
        self.showEditImage = true
 | 
			
		||||
      }
 | 
			
		||||
      reader.readAsDataURL(file.file)
 | 
			
		||||
    },
 | 
			
		||||
    beforeUpload(file) {
 | 
			
		||||
      const isLt2M = file.size / 1024 / 1024 < 2
 | 
			
		||||
      if (!isLt2M) {
 | 
			
		||||
        this.$message.error('图片大小不可超过2MB!')
 | 
			
		||||
      }
 | 
			
		||||
      return isLt2M
 | 
			
		||||
    },
 | 
			
		||||
    submitImage(file) {
 | 
			
		||||
      postImageFile(file).then((res) => {
 | 
			
		||||
        if (res.file_name) {
 | 
			
		||||
          this.form.avatar = res.file_name
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async handleBindWx() {
 | 
			
		||||
      await this.$refs.personForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          const { nickname, mobile, sex, avatar } = this.form
 | 
			
		||||
          const params = { nickname, mobile, sex, avatar }
 | 
			
		||||
          await updateEmployeeByUid(this.uid, params)
 | 
			
		||||
          bindWxByUid(this.uid)
 | 
			
		||||
            .then(() => {
 | 
			
		||||
              this.$message.success('绑定成功!')
 | 
			
		||||
            })
 | 
			
		||||
            .finally(() => {
 | 
			
		||||
              this.getEmployeeByUid()
 | 
			
		||||
              this.GetInfo()
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
@import '~@/style/static.less';
 | 
			
		||||
.setting-person {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  .setting-person-left {
 | 
			
		||||
    width: 200px;
 | 
			
		||||
    height: 400px;
 | 
			
		||||
    margin-right: 24px;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    border-radius: 15px;
 | 
			
		||||
    padding-top: 15px;
 | 
			
		||||
    .setting-person-left-item {
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      padding: 10px 20px;
 | 
			
		||||
      color: #a5a9bc;
 | 
			
		||||
      border-left: 4px solid #fff;
 | 
			
		||||
      margin-bottom: 5px;
 | 
			
		||||
      &:hover {
 | 
			
		||||
        .ops_popover_item_selected();
 | 
			
		||||
        border-color: #custom_colors[color_1];
 | 
			
		||||
      }
 | 
			
		||||
      > i {
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-left-item-selected {
 | 
			
		||||
      .ops_popover_item_selected();
 | 
			
		||||
      border-color: #custom_colors[color_1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .setting-person-right {
 | 
			
		||||
    width: 800px;
 | 
			
		||||
    height: 700px;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    border-radius: 15px;
 | 
			
		||||
    padding: 24px 48px;
 | 
			
		||||
    .setting-person-right-disabled {
 | 
			
		||||
      background-color: #custom_colors[color_2];
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      height: 30px;
 | 
			
		||||
      line-height: 30px;
 | 
			
		||||
      margin-top: 4px;
 | 
			
		||||
      padding: 0 10px;
 | 
			
		||||
      color: #a5a9bc;
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-bind {
 | 
			
		||||
      width: 40px;
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      background: #a5a9bc;
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      color: #fff;
 | 
			
		||||
      font-size: 30px;
 | 
			
		||||
      text-align: center;
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-bind-existed {
 | 
			
		||||
      background: #008cee;
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-bind-button {
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      width: 72px;
 | 
			
		||||
      background: #f0f5ff;
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      padding: 0 8px;
 | 
			
		||||
      text-align: center;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<style lang="less">
 | 
			
		||||
.setting-person-right .ant-form-item {
 | 
			
		||||
  margin-bottom: 12px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="setting-person">
 | 
			
		||||
    <div class="setting-person-left">
 | 
			
		||||
      <div
 | 
			
		||||
        @click="
 | 
			
		||||
          () => {
 | 
			
		||||
            $refs.personForm.clearValidate()
 | 
			
		||||
            $nextTick(() => {
 | 
			
		||||
              current = '1'
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        :class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '1' }"
 | 
			
		||||
      >
 | 
			
		||||
        <ops-icon type="icon-shidi-yonghu" />个人信息
 | 
			
		||||
      </div>
 | 
			
		||||
      <div
 | 
			
		||||
        @click="
 | 
			
		||||
          () => {
 | 
			
		||||
            $refs.personForm.clearValidate()
 | 
			
		||||
            $nextTick(() => {
 | 
			
		||||
              current = '2'
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        "
 | 
			
		||||
        :class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '2' }"
 | 
			
		||||
      >
 | 
			
		||||
        <a-icon type="unlock" theme="filled" />账号密码
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="setting-person-right">
 | 
			
		||||
      <a-form-model
 | 
			
		||||
        ref="personForm"
 | 
			
		||||
        :model="form"
 | 
			
		||||
        :rules="current === '1' ? rules1 : rules2"
 | 
			
		||||
        :colon="false"
 | 
			
		||||
        labelAlign="left"
 | 
			
		||||
        :labelCol="{ span: 4 }"
 | 
			
		||||
        :wrapperCol="{ span: 10 }"
 | 
			
		||||
      >
 | 
			
		||||
        <div v-show="current === '1'">
 | 
			
		||||
          <a-form-model-item label="头像" :style="{ display: 'flex', alignItems: 'center' }">
 | 
			
		||||
            <a-space>
 | 
			
		||||
              <a-avatar v-if="form.avatar" :src="`/api/common-setting/v1/file/${form.avatar}`" :size="64"> </a-avatar>
 | 
			
		||||
              <a-avatar v-else style="backgroundColor:#F0F5FF" :size="64">
 | 
			
		||||
                <ops-icon type="icon-shidi-yonghu" :style="{ color: '#2F54EB' }" />
 | 
			
		||||
              </a-avatar>
 | 
			
		||||
              <a-upload
 | 
			
		||||
                name="avatar"
 | 
			
		||||
                :show-upload-list="false"
 | 
			
		||||
                :customRequest="customRequest"
 | 
			
		||||
                :before-upload="beforeUpload"
 | 
			
		||||
                :style="{ width: '310px', height: '100px' }"
 | 
			
		||||
                accept=".svg,.png,.jpg,.jpeg"
 | 
			
		||||
              >
 | 
			
		||||
                <a-button type="primary" ghost size="small">更换头像</a-button>
 | 
			
		||||
              </a-upload>
 | 
			
		||||
            </a-space>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="姓名" prop="nickname">
 | 
			
		||||
            <a-input v-model="form.nickname" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="用户名">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.username }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="邮箱">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.email }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="直属上级">
 | 
			
		||||
            <div class="setting-person-right-disabled">
 | 
			
		||||
              {{ getDirectorName(allFlatEmployees, form.direct_supervisor_id) }}
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="性别">
 | 
			
		||||
            <a-select v-model="form.sex">
 | 
			
		||||
              <a-select-option value="男">男</a-select-option>
 | 
			
		||||
              <a-select-option value="女">女</a-select-option>
 | 
			
		||||
            </a-select>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="手机号" prop="mobile">
 | 
			
		||||
            <a-input v-model="form.mobile" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="部门">
 | 
			
		||||
            <div class="setting-person-right-disabled">
 | 
			
		||||
              {{ getDepartmentName(allFlatDepartments, form.department_id) }}
 | 
			
		||||
            </div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="岗位">
 | 
			
		||||
            <div class="setting-person-right-disabled">{{ form.position_name }}</div>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="绑定信息">
 | 
			
		||||
            <a-space>
 | 
			
		||||
              <a-tooltip title="企业微信">
 | 
			
		||||
                <div
 | 
			
		||||
                  @click="handleBind('wechatApp', form.notice_info && form.notice_info.wechatApp)"
 | 
			
		||||
                  :class="{
 | 
			
		||||
                    'setting-person-bind': true,
 | 
			
		||||
                    'setting-person-bind-existed': form.notice_info && form.notice_info.wechatApp,
 | 
			
		||||
                  }"
 | 
			
		||||
                >
 | 
			
		||||
                  <ops-icon type="ops-setting-notice-wx" />
 | 
			
		||||
                </div>
 | 
			
		||||
              </a-tooltip>
 | 
			
		||||
              <a-tooltip title="飞书">
 | 
			
		||||
                <div
 | 
			
		||||
                  @click="handleBind('feishuApp', form.notice_info && form.notice_info.feishuApp)"
 | 
			
		||||
                  :class="{
 | 
			
		||||
                    'setting-person-bind': true,
 | 
			
		||||
                    'setting-person-bind-existed': form.notice_info && form.notice_info.feishuApp,
 | 
			
		||||
                  }"
 | 
			
		||||
                >
 | 
			
		||||
                  <ops-icon type="ops-setting-notice-feishu" />
 | 
			
		||||
                </div>
 | 
			
		||||
              </a-tooltip>
 | 
			
		||||
              <a-tooltip title="钉钉">
 | 
			
		||||
                <div
 | 
			
		||||
                  @click="handleBind('dingdingApp', form.notice_info && form.notice_info.dingdingApp)"
 | 
			
		||||
                  :class="{
 | 
			
		||||
                    'setting-person-bind': true,
 | 
			
		||||
                    'setting-person-bind-existed': form.notice_info && form.notice_info.dingdingApp,
 | 
			
		||||
                  }"
 | 
			
		||||
                >
 | 
			
		||||
                  <ops-icon type="ops-setting-notice-dingding" />
 | 
			
		||||
                </div>
 | 
			
		||||
              </a-tooltip>
 | 
			
		||||
            </a-space>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-show="current === '2'">
 | 
			
		||||
          <a-form-model-item label="新密码" prop="password1">
 | 
			
		||||
            <a-input v-model="form.password1" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
          <a-form-model-item label="确认密码" prop="password2">
 | 
			
		||||
            <a-input v-model="form.password2" />
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div style="margin-right: 120px">
 | 
			
		||||
          <a-form-model-item label=" ">
 | 
			
		||||
            <a-button type="primary" @click="handleSave" :style="{ width: '100%' }">保存</a-button>
 | 
			
		||||
          </a-form-model-item>
 | 
			
		||||
        </div>
 | 
			
		||||
      </a-form-model>
 | 
			
		||||
    </div>
 | 
			
		||||
    <EditImage
 | 
			
		||||
      v-if="showEditImage"
 | 
			
		||||
      :show="showEditImage"
 | 
			
		||||
      :image="editImage"
 | 
			
		||||
      :title="eidtImageOption.title"
 | 
			
		||||
      :preview-width="eidtImageOption.previewWidth"
 | 
			
		||||
      :preview-height="eidtImageOption.previewHeight"
 | 
			
		||||
      preview-radius="0"
 | 
			
		||||
      width="550px"
 | 
			
		||||
      save-button-title="确定"
 | 
			
		||||
      @save="submitImage"
 | 
			
		||||
      @close="showEditImage = false"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapActions, mapGetters } from 'vuex'
 | 
			
		||||
import { getAllDepartmentList } from '@/api/company'
 | 
			
		||||
import { postImageFile } from '@/api/file'
 | 
			
		||||
import {
 | 
			
		||||
  getEmployeeList,
 | 
			
		||||
  getEmployeeByUid,
 | 
			
		||||
  updateEmployeeByUid,
 | 
			
		||||
  updatePasswordByUid,
 | 
			
		||||
  bindPlatformByUid,
 | 
			
		||||
  unbindPlatformByUid,
 | 
			
		||||
} from '@/api/employee'
 | 
			
		||||
import { getDepartmentName, getDirectorName } from '@/utils/util'
 | 
			
		||||
import EditImage from '../components/EditImage.vue'
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'Person',
 | 
			
		||||
  components: { EditImage },
 | 
			
		||||
  data() {
 | 
			
		||||
    const validatePassword = (rule, value, callback) => {
 | 
			
		||||
      if (!value) {
 | 
			
		||||
        callback(new Error('请二次确认新密码'))
 | 
			
		||||
      }
 | 
			
		||||
      if (value !== this.form.password1) {
 | 
			
		||||
        callback(new Error('两次输入密码不一致'))
 | 
			
		||||
      }
 | 
			
		||||
      callback()
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      current: '1',
 | 
			
		||||
      form: {},
 | 
			
		||||
      rules1: {
 | 
			
		||||
        nickname: [
 | 
			
		||||
          { required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
 | 
			
		||||
          { max: 20, message: '字符数须小于20' },
 | 
			
		||||
        ],
 | 
			
		||||
        mobile: [
 | 
			
		||||
          {
 | 
			
		||||
            pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
 | 
			
		||||
            message: '请输入正确的手机号',
 | 
			
		||||
            trigger: 'blur',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      rules2: {
 | 
			
		||||
        password1: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
 | 
			
		||||
        password2: [{ required: true, message: '两次输入密码不一致', trigger: 'blur', validator: validatePassword }],
 | 
			
		||||
      },
 | 
			
		||||
      allFlatEmployees: [],
 | 
			
		||||
      allFlatDepartments: [],
 | 
			
		||||
      showEditImage: false,
 | 
			
		||||
      eidtImageOption: {
 | 
			
		||||
        type: 'avatar',
 | 
			
		||||
        fixedNumber: [4, 4],
 | 
			
		||||
        title: '编辑头像',
 | 
			
		||||
        previewWidth: '60px',
 | 
			
		||||
        previewHeight: '60px',
 | 
			
		||||
      },
 | 
			
		||||
      editImage: null,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapGetters(['uid']),
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getAllFlatEmployees()
 | 
			
		||||
    this.getAllFlatDepartment()
 | 
			
		||||
    this.getEmployeeByUid()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    ...mapActions(['GetInfo']),
 | 
			
		||||
    getDepartmentName,
 | 
			
		||||
    getDirectorName,
 | 
			
		||||
    getEmployeeByUid() {
 | 
			
		||||
      getEmployeeByUid(this.uid).then((res) => {
 | 
			
		||||
        this.form = { ...res }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    getAllFlatEmployees() {
 | 
			
		||||
      getEmployeeList({ block_status: 0, page_size: 99999 }).then((res) => {
 | 
			
		||||
        this.allFlatEmployees = res.data_list
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    getAllFlatDepartment() {
 | 
			
		||||
      getAllDepartmentList({ is_tree: 0 }).then((res) => {
 | 
			
		||||
        this.allFlatDepartments = res
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async handleSave() {
 | 
			
		||||
      await this.$refs.personForm.validate(async (valid) => {
 | 
			
		||||
        if (valid) {
 | 
			
		||||
          const { nickname, mobile, sex, avatar, password1 } = this.form
 | 
			
		||||
          const params = { nickname, mobile, sex, avatar }
 | 
			
		||||
          if (this.current === '1') {
 | 
			
		||||
            await updateEmployeeByUid(this.uid, params).then((res) => {
 | 
			
		||||
              this.$message.success('保存成功!')
 | 
			
		||||
              this.getEmployeeByUid()
 | 
			
		||||
              this.GetInfo()
 | 
			
		||||
            })
 | 
			
		||||
          } else {
 | 
			
		||||
            await updatePasswordByUid(this.uid, { password: password1 }).then((res) => {
 | 
			
		||||
              this.$message.success('保存成功!')
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    customRequest(file) {
 | 
			
		||||
      const reader = new FileReader()
 | 
			
		||||
      var self = this
 | 
			
		||||
      reader.onload = function(e) {
 | 
			
		||||
        let result
 | 
			
		||||
        if (typeof e.target.result === 'object') {
 | 
			
		||||
          // 把Array Buffer转化为blob 如果是base64不需要
 | 
			
		||||
          result = window.URL.createObjectURL(new Blob([e.target.result]))
 | 
			
		||||
        } else {
 | 
			
		||||
          result = e.target.result
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.editImage = result
 | 
			
		||||
        self.showEditImage = true
 | 
			
		||||
      }
 | 
			
		||||
      reader.readAsDataURL(file.file)
 | 
			
		||||
    },
 | 
			
		||||
    beforeUpload(file) {
 | 
			
		||||
      const isLt2M = file.size / 1024 / 1024 < 2
 | 
			
		||||
      if (!isLt2M) {
 | 
			
		||||
        this.$message.error('图片大小不可超过2MB!')
 | 
			
		||||
      }
 | 
			
		||||
      return isLt2M
 | 
			
		||||
    },
 | 
			
		||||
    submitImage(file) {
 | 
			
		||||
      postImageFile(file).then((res) => {
 | 
			
		||||
        if (res.file_name) {
 | 
			
		||||
          this.form.avatar = res.file_name
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    async handleBind(platform, isBind) {
 | 
			
		||||
      if (isBind) {
 | 
			
		||||
        const that = this
 | 
			
		||||
        this.$confirm({
 | 
			
		||||
          title: '警告',
 | 
			
		||||
          content: `确认解绑?`,
 | 
			
		||||
          onOk() {
 | 
			
		||||
            unbindPlatformByUid(platform, that.uid)
 | 
			
		||||
              .then(() => {
 | 
			
		||||
                that.$message.success('解绑成功!')
 | 
			
		||||
              })
 | 
			
		||||
              .finally(() => {
 | 
			
		||||
                that.getEmployeeByUid()
 | 
			
		||||
                that.GetInfo()
 | 
			
		||||
              })
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        await this.$refs.personForm.validate(async (valid) => {
 | 
			
		||||
          if (valid) {
 | 
			
		||||
            const { nickname, mobile, sex, avatar } = this.form
 | 
			
		||||
            const params = { nickname, mobile, sex, avatar }
 | 
			
		||||
            await updateEmployeeByUid(this.uid, params)
 | 
			
		||||
            bindPlatformByUid(platform, this.uid)
 | 
			
		||||
              .then(() => {
 | 
			
		||||
                this.$message.success('绑定成功!')
 | 
			
		||||
              })
 | 
			
		||||
              .finally(() => {
 | 
			
		||||
                this.getEmployeeByUid()
 | 
			
		||||
                this.GetInfo()
 | 
			
		||||
              })
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
@import '~@/style/static.less';
 | 
			
		||||
.setting-person {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  .setting-person-left {
 | 
			
		||||
    width: 200px;
 | 
			
		||||
    height: 400px;
 | 
			
		||||
    margin-right: 24px;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    border-radius: 15px;
 | 
			
		||||
    padding-top: 15px;
 | 
			
		||||
    .setting-person-left-item {
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      padding: 10px 20px;
 | 
			
		||||
      color: #a5a9bc;
 | 
			
		||||
      border-left: 4px solid #fff;
 | 
			
		||||
      margin-bottom: 5px;
 | 
			
		||||
      &:hover {
 | 
			
		||||
        .ops_popover_item_selected();
 | 
			
		||||
        border-color: #custom_colors[color_1];
 | 
			
		||||
      }
 | 
			
		||||
      > i {
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-left-item-selected {
 | 
			
		||||
      .ops_popover_item_selected();
 | 
			
		||||
      border-color: #custom_colors[color_1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .setting-person-right {
 | 
			
		||||
    width: 800px;
 | 
			
		||||
    height: 700px;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    border-radius: 15px;
 | 
			
		||||
    padding: 24px 48px;
 | 
			
		||||
    .setting-person-right-disabled {
 | 
			
		||||
      background-color: #custom_colors[color_2];
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      height: 30px;
 | 
			
		||||
      line-height: 30px;
 | 
			
		||||
      margin-top: 4px;
 | 
			
		||||
      padding: 0 10px;
 | 
			
		||||
      color: #a5a9bc;
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-bind {
 | 
			
		||||
      width: 40px;
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      background: #a5a9bc;
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      color: #fff;
 | 
			
		||||
      font-size: 30px;
 | 
			
		||||
      text-align: center;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
    .setting-person-bind-existed {
 | 
			
		||||
      background: #008cee;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<style lang="less">
 | 
			
		||||
.setting-person-right .ant-form-item {
 | 
			
		||||
  margin-bottom: 12px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user