15
LICENSE
|
@ -19,3 +19,18 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>北京敲敲云科技有限公司</name>
|
||||
<email>jeecgos@163.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>http://www.jeecg.com</connection>
|
||||
<developerConnection>https://qiaoqiaoyun.com</developerConnection>
|
||||
<url>http://www.jeecg.com/vip</url>
|
||||
</scm>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
JEECG BOOT 低代码开发平台(Vue3前端)
|
||||
===============
|
||||
当前最新版本: 3.4.4(发布时间:2022-11-21)
|
||||
当前最新版本: 3.5.0(发布时间:2023-03-08)
|
||||
|
||||
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[![](https://img.shields.io/badge/Author-北京敲敲云科技-orange.svg)](http://www.jeecg.com)
|
||||
[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
|
||||
[![](https://img.shields.io/badge/version-3.4.4-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[![](https://img.shields.io/badge/version-3.5.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
|
||||
|
|
106
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "jeecgboot-vue3",
|
||||
"version": "3.4.4",
|
||||
"version": "3.5.0",
|
||||
"author": {
|
||||
"name": "jeecg",
|
||||
"email": "jeecgos@163.com",
|
||||
|
@ -67,6 +67,7 @@
|
|||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "2.0.12",
|
||||
"print-js": "^1.6.0",
|
||||
"pinyin-pro": "^3.11.0",
|
||||
"qs": "^6.10.3",
|
||||
"qrcode": "^1.5.0",
|
||||
"qrcodejs2": "0.0.2",
|
||||
|
@ -107,6 +108,7 @@
|
|||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/pinyin": "^2.10.0",
|
||||
"@types/showdown": "^1.9.4",
|
||||
"@types/sortablejs": "^1.10.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
|
@ -194,7 +196,107 @@
|
|||
"vite": {
|
||||
"optimizeDeps": {
|
||||
"include": [
|
||||
|
||||
"@ant-design/colors",
|
||||
"@ant-design/icons-vue",
|
||||
"@vueuse/core",
|
||||
"@vueuse/shared",
|
||||
"@zxcvbn-ts/core",
|
||||
"ant-design-vue",
|
||||
"axios",
|
||||
"china-area-data",
|
||||
"clipboard",
|
||||
"codemirror",
|
||||
"codemirror/addon/fold/brace-fold.js",
|
||||
"codemirror/addon/fold/comment-fold.js",
|
||||
"codemirror/addon/fold/foldcode.js",
|
||||
"codemirror/addon/fold/foldgutter.js",
|
||||
"codemirror/addon/fold/indent-fold.js",
|
||||
"codemirror/addon/hint/anyword-hint.js",
|
||||
"codemirror/addon/hint/show-hint.js",
|
||||
"codemirror/addon/selection/active-line.js",
|
||||
"codemirror/mode/clike/clike.js",
|
||||
"codemirror/mode/css/css.js",
|
||||
"codemirror/mode/javascript/javascript.js",
|
||||
"codemirror/mode/markdown/markdown.js",
|
||||
"codemirror/mode/python/python.js",
|
||||
"codemirror/mode/r/r.js",
|
||||
"codemirror/mode/shell/shell.js",
|
||||
"codemirror/mode/sql/sql.js",
|
||||
"codemirror/mode/swift/swift.js",
|
||||
"codemirror/mode/vue/vue.js",
|
||||
"codemirror/mode/xml/xml.js",
|
||||
"cron-parser",
|
||||
"cropperjs",
|
||||
"crypto-js/aes",
|
||||
"crypto-js/enc-base64",
|
||||
"crypto-js/enc-utf8",
|
||||
"crypto-js/md5",
|
||||
"crypto-js/mode-ecb",
|
||||
"crypto-js/pad-pkcs7",
|
||||
"dom-align",
|
||||
"echarts",
|
||||
"echarts/charts",
|
||||
"echarts/components",
|
||||
"echarts/core",
|
||||
"echarts/renderers",
|
||||
"emoji-mart-vue-fast/src",
|
||||
"intro.js",
|
||||
"lodash-es",
|
||||
"md5",
|
||||
"nprogress",
|
||||
"path-to-regexp",
|
||||
"pinia",
|
||||
"print-js",
|
||||
"qrcode",
|
||||
"qs",
|
||||
"resize-observer-polyfill",
|
||||
"showdown",
|
||||
"sortablejs",
|
||||
"tinymce/icons/default/icons",
|
||||
"tinymce/plugins/advlist",
|
||||
"tinymce/plugins/anchor",
|
||||
"tinymce/plugins/autolink",
|
||||
"tinymce/plugins/autosave",
|
||||
"tinymce/plugins/code",
|
||||
"tinymce/plugins/codesample",
|
||||
"tinymce/plugins/contextmenu",
|
||||
"tinymce/plugins/directionality",
|
||||
"tinymce/plugins/fullscreen",
|
||||
"tinymce/plugins/hr",
|
||||
"tinymce/plugins/image",
|
||||
"tinymce/plugins/insertdatetime",
|
||||
"tinymce/plugins/link",
|
||||
"tinymce/plugins/lists",
|
||||
"tinymce/plugins/media",
|
||||
"tinymce/plugins/nonbreaking",
|
||||
"tinymce/plugins/noneditable",
|
||||
"tinymce/plugins/pagebreak",
|
||||
"tinymce/plugins/paste",
|
||||
"tinymce/plugins/preview",
|
||||
"tinymce/plugins/print",
|
||||
"tinymce/plugins/save",
|
||||
"tinymce/plugins/searchreplace",
|
||||
"tinymce/plugins/spellchecker",
|
||||
"tinymce/plugins/tabfocus",
|
||||
"tinymce/plugins/table",
|
||||
"tinymce/plugins/template",
|
||||
"tinymce/plugins/textcolor",
|
||||
"tinymce/plugins/textpattern",
|
||||
"tinymce/plugins/visualblocks",
|
||||
"tinymce/plugins/visualchars",
|
||||
"tinymce/plugins/wordcount",
|
||||
"tinymce/themes/silver",
|
||||
"tinymce/tinymce",
|
||||
"vditor",
|
||||
"vue",
|
||||
"vue-i18n",
|
||||
"vue-print-nb-jeecg/src/printarea",
|
||||
"vue-router",
|
||||
"vue-types",
|
||||
"vxe-table",
|
||||
"vxe-table-plugin-antd",
|
||||
"xe-utils",
|
||||
"xss"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
6721
pnpm-lock.yaml
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 430 B |
|
@ -23,6 +23,7 @@ export interface LoginResultModel {
|
|||
userId: string | number;
|
||||
token: string;
|
||||
role: RoleInfo;
|
||||
userInfo?: any
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
|
|||
* @description: getUserInfo
|
||||
*/
|
||||
export function getUserInfo() {
|
||||
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e) => {
|
||||
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, {}).catch((e) => {
|
||||
// update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
|
||||
if (e && (e.message.includes('timeout') || e.message.includes('401'))) {
|
||||
//接口不通时跳转到登录界面
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1671872759315" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="29438" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M136.005284 399.993557h751.9874c35.198978 0 63.999619 28.79861 63.99962 63.999619v495.992986c0 35.198978-28.79861 63.999619-63.99962 63.999619H136.005284c-35.198978 0-63.999619-28.79861-63.999619-63.999619V463.993176c0-35.198978 28.800641-63.999619 63.999619-63.999619z" fill="#ECC45C" p-id="29439"></path><path d="M136.005284 367.993748h751.9874c35.198978 0 63.999619 28.79861 63.99962 63.999619v31.999809c0 35.198978-28.79861 63.999619-63.99962 63.99962H136.005284c-35.198978 0-63.999619-28.79861-63.999619-63.99962v-31.999809c0-35.198978 28.800641-63.999619 63.999619-63.999619z" fill="#F8D578" p-id="29440"></path><path d="M232.002682 415.993462c-39.998746 0-71.998556 14.399305-71.998556 31.99981s31.99981 31.99981 71.998556 31.999809 71.998556-14.399305 71.998556-31.999809c0.002031-17.600505-31.997778-31.99981-71.998556-31.99981z m559.992605 0c-39.998746 0-71.998556 14.399305-71.998556 31.99981s31.99981 31.99981 71.998556 31.999809 71.998556-14.399305 71.998556-31.999809S831.994033 415.993462 791.995287 415.993462zM512 655.990003c66.398488 0 119.99827 53.599783 119.99827 119.99827s-53.599783 119.99827-119.99827 119.99827-119.99827-53.599783-119.99827-119.99827 53.597751-119.99827 119.99827-119.99827z" fill="#D4B053" p-id="29441"></path><path d="M512 687.989812c48.800014 0 87.998461 39.198446 87.998461 87.998461s-39.198446 87.998461-87.998461 87.998461-87.998461-39.198446-87.998461-87.998461a87.742527 87.742527 0 0 1 87.998461-87.998461z" fill="#E4E7E7" p-id="29442"></path><path d="M512 735.987495c8.799237 0 15.999905 7.200668 15.999905 15.999905v47.999714c0 8.799237-7.200668 15.999905-15.999905 15.999905s-15.999905-7.200668-15.999905-15.999905v-47.999714c0-8.799237 7.198637-15.999905 15.999905-15.999905z" fill="#324D5B" p-id="29443"></path><path d="M512 0C326.401511 0 176.004031 150.39748 176.004031 335.993938v95.999429c0 17.600505 24.799141 31.99981 55.998651 31.999809s55.998651-14.399305 55.998651-31.999809v-95.999429c0-123.997739 99.998897-223.996636 223.996636-223.996636s223.996636 99.998897 223.996636 223.996636v95.999429c0 17.600505 24.799141 31.99981 55.998651 31.999809s55.998651-14.399305 55.998651-31.999809v-95.999429C847.993938 150.39748 697.596458 0 512 0z" fill="#E4E7E7" p-id="29444"></path><path d="M512 63.999619c-150.39748 0-271.99635 121.59887-271.99635 271.99635v127.997207c27.200041-2.4009 47.999714-15.199605 47.999714-31.999809v-95.999429c0-123.997739 99.998897-223.996636 223.996636-223.996636s223.996636 99.998897 223.996636 223.996636v95.999429c0 15.999905 20.799673 29.59891 47.999714 31.999809v-127.997207c-0.002031-150.399511-121.59887-271.99635-271.99635-271.99635z" fill="#CDCFCF" p-id="29445"></path></svg>
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 536 B |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,365 @@
|
|||
::-webkit-input-placeholder {
|
||||
/* WebKit browsers */
|
||||
color: #868686;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
/* Mozilla Firefox 19+ */
|
||||
color: #868686;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
/* Internet Explorer 10+ */
|
||||
color: #868686;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
transition: background-color 5000s ease-in-out 0s;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
color: #333;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Myriad Set Pro', 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a,
|
||||
label,
|
||||
button,
|
||||
input,
|
||||
select {
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #e3f0ff;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
button,
|
||||
fieldset,
|
||||
form,
|
||||
input,
|
||||
legend,
|
||||
textarea,
|
||||
th,
|
||||
td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #08acee;
|
||||
}
|
||||
|
||||
button {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
clear: both;
|
||||
content: '.';
|
||||
display: block;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
}
|
||||
|
||||
.divHeight {
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
background: #f5f5f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.r-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.r-line:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
-webkit-transform-origin: 100% 0;
|
||||
transform-origin: 100% 0;
|
||||
}
|
||||
|
||||
.b-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.b-line:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid #dedede;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
-webkit-transform-origin: 0 100%;
|
||||
transform-origin: 0 100%;
|
||||
}
|
||||
|
||||
.aui-arrow {
|
||||
position: relative;
|
||||
padding-right: 0.8rem;
|
||||
}
|
||||
|
||||
.aui-arrow span {
|
||||
font-size: 0.8rem;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
|
||||
.aui-arrow:after {
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-width: 2px 2px 0 0;
|
||||
border-color: #848484;
|
||||
border-style: solid;
|
||||
-webkit-transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
|
||||
transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
|
||||
position: relative;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -4px;
|
||||
right: 2px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.aui-flex {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aui-flex-box {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 必要布局样式css */
|
||||
.aui-flexView {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aui-scrollView {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
position: relative;
|
||||
padding-bottom: 53px;
|
||||
}
|
||||
|
||||
.aui-navBar {
|
||||
height: 44px;
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
z-index: 102;
|
||||
background-color: #5064eb;
|
||||
}
|
||||
|
||||
.aui-navBar-item {
|
||||
height: 44px;
|
||||
min-width: 15%;
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0 0 15%;
|
||||
-ms-flex: 0 0 15%;
|
||||
flex: 0 0 15%;
|
||||
padding: 0 0.9rem;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
font-size: 0.7rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
color: #808080;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aui-navBar-item:first-child {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-webkit-order: 1;
|
||||
-ms-flex-order: 1;
|
||||
order: 1;
|
||||
margin-right: -25%;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.aui-navBar-item:last-child {
|
||||
-webkit-box-ordinal-group: 4;
|
||||
-webkit-order: 3;
|
||||
-ms-flex-order: 3;
|
||||
order: 3;
|
||||
-webkit-box-pack: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
-ms-flex-pack: end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.aui-center {
|
||||
-webkit-box-ordinal-group: 3;
|
||||
-webkit-order: 2;
|
||||
-ms-flex-order: 2;
|
||||
order: 2;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
width: 80%;
|
||||
margin-left: 22%;
|
||||
}
|
||||
|
||||
.aui-center-title {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.95rem;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: block;
|
||||
border: none;
|
||||
float: left;
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-background-img {
|
||||
background-image: url(../icon/jeecg_bg.png);
|
||||
background-size: cover;
|
||||
background-position: top center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
|
@ -0,0 +1,612 @@
|
|||
.aui-content {
|
||||
padding: 40px 60px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.aui-container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 4px 8px 1px rgba(0, 0, 0, 0.2);
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 92%;
|
||||
height: auto;
|
||||
-webkit-transform: translateX(-50%) translateY(-50%);
|
||||
-moz-transform: translateX(-50%) translateY(-50%);
|
||||
-ms-transform: translateX(-50%) translateY(-50%);
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
-webkit-transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
|
||||
.aui-form {
|
||||
width: 100%;
|
||||
background: #eee;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.aui-image {
|
||||
padding: 180px 80px;
|
||||
flex-basis: 60%;
|
||||
-webkit-flex-basis: 60%;
|
||||
background-color: #0198cd;
|
||||
background-image: url(../icon/jeecg_ad.png);
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.aui-image-text {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aui-formBox {
|
||||
flex-basis: 40%;
|
||||
-webkit-flex-basis: 40%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 20px;
|
||||
background: #fff;
|
||||
box-shadow: 2px 9px 49px -17px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.aui-logo {
|
||||
width: 180px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
top: 2%;
|
||||
left: 8%;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.aui-account-line {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.aui-code-line {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-left: 3px solid #fff;
|
||||
height: 42px;
|
||||
padding: 0 15px;
|
||||
line-height: 40px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aui-eye {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 10px;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aui-input-line {
|
||||
background: #f5f5f9;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.aui-input-line input {
|
||||
width: 100%;
|
||||
padding: 12px 10px;
|
||||
border: none;
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
background: unset;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.aui-input-line .icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.icon-line-user {
|
||||
background-image: url(../icon/icon-line-user.png);
|
||||
}
|
||||
|
||||
.icon-line-tel {
|
||||
background-image: url(../icon/icon-line-tel.png);
|
||||
}
|
||||
|
||||
.icon-line-msg {
|
||||
background-image: url(../icon/icon-line-msg.png);
|
||||
}
|
||||
|
||||
.icon-line-pad {
|
||||
background-image: url(../icon/icon-line-pad.png);
|
||||
}
|
||||
|
||||
.aui-forgot .aui-input-line input {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.aui-forgot .aui-input-line {
|
||||
background: none;
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.aui-forgot .aui-input-line:focus {
|
||||
border-color: #1b90ff;
|
||||
}
|
||||
|
||||
.aui-forgot .aui-input-line:hover {
|
||||
border-color: #1b90ff;
|
||||
}
|
||||
|
||||
.aui-forgot .aui-input-line .aui-code-line {
|
||||
border-left: 1px solid #dbdbdb;
|
||||
height: 40px;
|
||||
color: #1b90ff;
|
||||
}
|
||||
|
||||
.aui-step-box {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.aui-step-box::after {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
width: 76%;
|
||||
margin-left: -38%;
|
||||
height: 1px;
|
||||
background: #bcbcbc;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.aui-step-item {
|
||||
width: 33.333%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.aui-step-tags em {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 8px solid #fff;
|
||||
line-height: 1.3;
|
||||
border-radius: 100px;
|
||||
background: #bcbcbc;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
font-style: normal;
|
||||
color: #fff;
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aui-step-tags p {
|
||||
font-size: 14px;
|
||||
color: #bcbcbc;
|
||||
}
|
||||
|
||||
.activeStep .aui-step-tags em {
|
||||
background: #1b90ff;
|
||||
}
|
||||
|
||||
.activeStep .aui-step-tags p {
|
||||
color: #1b90ff;
|
||||
}
|
||||
|
||||
.aui-success {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
margin-top: -40px;
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
.aui-success-icon {
|
||||
width: 40px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.aui-success h3 {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #515151;
|
||||
font-size: 18px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.aui-form-nav {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.aui-form-nav .aui-flex-box {
|
||||
color: #040404;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aui-clear-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.aui-clear-left .activeNav::after {
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
.activeNav {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.activeNav::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
bottom: -10px;
|
||||
left: 50%;
|
||||
margin-left: -15px;
|
||||
width: 30px;
|
||||
height: 4px;
|
||||
background: #1b90ff;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.phone .aui-inputClear {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.phone .aui-inputClear input {
|
||||
//padding-left: 1px;
|
||||
}
|
||||
|
||||
.phone .aui-inputClear .aui-code {
|
||||
text-align: right;
|
||||
width: auto;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.phone .aui-inputClear .aui-code a {
|
||||
color: #1b90ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.phoneChina {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
font-size: 14px;
|
||||
color: #040404;
|
||||
}
|
||||
|
||||
.phoneChina::after {
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
background-image: url(../icon/icon_dow.png);
|
||||
background-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.phoneChina:before {
|
||||
position: absolute;
|
||||
right: -42px;
|
||||
bottom: -15px;
|
||||
content: ' ';
|
||||
background: #fff;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.aui-ewm {
|
||||
width: 280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.aui-formEwm {
|
||||
padding: 50px 40px 55px 40px;
|
||||
}
|
||||
|
||||
.aui-inputClear {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
background: #fff;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.aui-inputClear .icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.aui-inputClear input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.aui-code {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 0;
|
||||
width: 115px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-code {
|
||||
background-image: url(../icon/icon-user.png);
|
||||
}
|
||||
|
||||
.icon-password {
|
||||
background-image: url(../icon/icon-password.png);
|
||||
}
|
||||
|
||||
.icon-code {
|
||||
background-image: url(../icon/icon-code.png);
|
||||
}
|
||||
|
||||
.aui-inputClear:focus {
|
||||
border-bottom: 1px solid #1b90ff;
|
||||
}
|
||||
|
||||
.aui-inputClear:hover {
|
||||
border-bottom: 1px solid #1b90ff;
|
||||
}
|
||||
|
||||
.aui-choice {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
color: #040404;
|
||||
}
|
||||
|
||||
.aui-choice input {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aui-forget a {
|
||||
color: #1b90ff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aui-forget a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.aui-formButton {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.aui-formButton a {
|
||||
height: 42px;
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 8px;
|
||||
border-color: #67b5ff;
|
||||
background: #1b90ff;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
color: #fff;
|
||||
margin: 8px 0;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aui-formButton a:focus {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.aui-formButton a:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.aui-formButton .aui-linek-code {
|
||||
background: #fff;
|
||||
color: #3c3c3c;
|
||||
border: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.aui-formButton .aui-linek-code:hover {
|
||||
color: #1b90ff;
|
||||
border: 1px solid #1b90ff;
|
||||
}
|
||||
|
||||
.aui-third-text {
|
||||
font-size: 12px;
|
||||
color: #3c3c3c;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.aui-third-text span {
|
||||
color: #afafaf;
|
||||
display: block;
|
||||
width: 38%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
z-index: 100;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aui-third-border {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aui-third-border::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
top: 8px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
-webkit-transform-origin: 0 100%;
|
||||
transform-origin: 0 100%;
|
||||
}
|
||||
|
||||
.aui-third-login {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 0 auto;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.aui-third-login a {
|
||||
font-size: 22px;
|
||||
margin: 0 auto;
|
||||
border-radius: 100px;
|
||||
display: inline-block;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.aui-third-login a:hover {
|
||||
color: #1b90ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aui-third-login:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
.aui-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aui-image {
|
||||
order: 2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aui-container {
|
||||
width: 100%;
|
||||
max-width: 550px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.aui-content {
|
||||
justify-content: initial;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 321px) and (max-width: 375px) {
|
||||
.aui-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aui-image {
|
||||
order: 2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aui-container {
|
||||
width: 90%;
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.aui-content {
|
||||
justify-content: initial;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 375px) and (max-width: 425px) {
|
||||
.aui-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aui-image {
|
||||
order: 2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aui-container {
|
||||
width: 90%;
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.aui-content {
|
||||
justify-content: initial;
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 425px) and (max-width: 768px) {
|
||||
.aui-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aui-image {
|
||||
order: 2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aui-container {
|
||||
width: 90%;
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.aui-content {
|
||||
justify-content: initial;
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.aui-step-box::after {
|
||||
width: 70%;
|
||||
margin-left: -35%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.aui-logo {
|
||||
top: 3%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 300px) {
|
||||
.aui-logo {
|
||||
top: 3%;
|
||||
}
|
||||
}
|
|
@ -14,15 +14,18 @@
|
|||
>
|
||||
<a-popconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)">
|
||||
<template #icon v-if="item.popConfirm.icon">
|
||||
<Icon :icon="item.popConfirm.icon" />
|
||||
<Icon v-if="item.iconColor" :icon="item.popConfirm.icon" :color="item.iconColor"/>
|
||||
<Icon v-else :icon="item.popConfirm.icon"/>
|
||||
</template>
|
||||
<div class="dropdown-event-area">
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor"/>
|
||||
<Icon :icon="item.icon" v-else-if="item.icon"/>
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
<template v-else>
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor"/>
|
||||
<Icon :icon="item.icon" v-else-if="item.icon"/>
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
</template>
|
||||
</a-menu-item>
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
import { basicProps } from './props';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import dayjs from 'dayjs';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicForm',
|
||||
|
@ -239,13 +240,19 @@
|
|||
propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2022-11-28 for: QQYUN-3121 【优化】表单视图问题#scott测试 8、此功能未实现
|
||||
const onFormSubmitWhenChange = useDebounceFn(handleSubmit, 300);
|
||||
function setFormModel(key: string, value: any) {
|
||||
formModel[key] = value;
|
||||
const { validateTrigger } = unref(getBindValue);
|
||||
if (!validateTrigger || validateTrigger === 'change') {
|
||||
validateFields([key]).catch((_) => {});
|
||||
}
|
||||
if(props.autoSearch === true){
|
||||
onFormSubmitWhenChange();
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:2022-11-28 for: QQYUN-3121 【优化】表单视图问题#scott测试 8、此功能未实现
|
||||
|
||||
function handleEnterPress(e: KeyboardEvent) {
|
||||
const { autoSubmitOnEnter } = unref(getProps);
|
||||
|
|
|
@ -58,7 +58,9 @@ import JSearchSelect from './jeecg/components/JSearchSelect.vue';
|
|||
import JAddInput from './jeecg/components/JAddInput.vue';
|
||||
import { Time } from '/@/components/Time';
|
||||
import JRangeNumber from './jeecg/components/JRangeNumber.vue';
|
||||
import UserSelect from './jeecg/components/userSelect/index.vue';
|
||||
import JRangeDate from './jeecg/components/JRangeDate.vue'
|
||||
import JRangeTime from './jeecg/components/JRangeTime.vue'
|
||||
|
||||
const componentMap = new Map<ComponentType, Component>();
|
||||
|
||||
|
@ -126,7 +128,9 @@ componentMap.set('JUpload', JUpload);
|
|||
componentMap.set('JSearchSelect', JSearchSelect);
|
||||
componentMap.set('JAddInput', JAddInput);
|
||||
componentMap.set('JRangeNumber', JRangeNumber);
|
||||
componentMap.set('UserSelect', UserSelect);
|
||||
componentMap.set('RangeDate', JRangeDate);
|
||||
componentMap.set('RangeTime', JRangeTime);
|
||||
|
||||
export function add(compName: ComponentType, component: Component) {
|
||||
componentMap.set(compName, component);
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
|
||||
<template v-for="item in getOptions" :key="`${item.value}`">
|
||||
<RadioButton :value="item.value" :disabled="item.disabled">
|
||||
<Icon v-if="item.icon" :icon="item.icon" />
|
||||
{{ item.label ? item.label : '' }}
|
||||
{{ item.label }}
|
||||
</RadioButton>
|
||||
</template>
|
||||
</RadioGroup>
|
||||
|
@ -17,13 +16,8 @@
|
|||
import { isString } from '/@/utils/is';
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
type OptionsItem = {
|
||||
icon?: string;
|
||||
label?: string;
|
||||
value: string | number | boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
|
||||
type RadioItem = string | OptionsItem;
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -31,7 +25,6 @@
|
|||
components: {
|
||||
RadioGroup: Radio.Group,
|
||||
RadioButton: Radio.Button,
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
|
|
|
@ -146,24 +146,26 @@
|
|||
|
||||
function handleChange(e) {
|
||||
const { mode } = unref<Recordable>(getBindValue);
|
||||
let changeValue:any;
|
||||
// 兼容多选模式
|
||||
|
||||
//update-begin---author:wangshuai ---date:20230216 for:[QQYUN-4290]公文发文:选择机关代字报错,是因为值改变触发了change事件三次,导致数据发生改变------------
|
||||
//采用一个值,不然的话state值变换触发多个change
|
||||
if (mode === 'multiple') {
|
||||
state.value = e?.target?.value ?? e;
|
||||
} else {
|
||||
state.value = [e?.target?.value ?? e];
|
||||
}
|
||||
changeValue = e?.target?.value ?? e;
|
||||
// 过滤掉空值
|
||||
if (state.value == null || state.value === '') {
|
||||
state.value = [];
|
||||
if (changeValue == null || changeValue === '') {
|
||||
changeValue = [];
|
||||
}
|
||||
if (Array.isArray(state.value)) {
|
||||
state.value = state.value.filter((item) => item != null && item !== '');
|
||||
if (Array.isArray(changeValue)) {
|
||||
changeValue = changeValue.filter((item) => item != null && item !== '');
|
||||
}
|
||||
//update-begin---author:wangshuai ---date:20221123 for:单选模式要改成字符串------------
|
||||
if(mode !== 'multiple' && state.value && state.value.length>0){
|
||||
state.value = state.value[0];
|
||||
} else {
|
||||
changeValue = e?.target?.value ?? e;
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20221123 for:单选模式要改成字符串--------------
|
||||
state.value = changeValue;
|
||||
//update-end---author:wangshuai ---date:20230216 for:[QQYUN-4290]公文发文:选择机关代字报错,是因为值改变触发了change事件三次,导致数据发生改变------------
|
||||
|
||||
// nextTick(() => formItemContext.onFieldChange());
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { Form } from 'ant-design-vue';
|
||||
|
||||
const placeholder = ['最小值', '最大值']
|
||||
const placeholder = ['开始日期', '结束日期']
|
||||
/**
|
||||
* 用于范围查询
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<a-time-range-picker v-model:value="rangeValue" @change="handleChange" :placeholder="placeholder" :valueFormat="format" :format="format"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { Form } from 'ant-design-vue';
|
||||
|
||||
const placeholder = ['开始时间', '结束时间']
|
||||
/**
|
||||
* 用于时间-time组件的范围查询
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: "JRangeTime",
|
||||
props:{
|
||||
value: propTypes.string.def(''),
|
||||
format: propTypes.string.def('HH:mm:ss'),
|
||||
placeholder: propTypes.string.def(''),
|
||||
},
|
||||
emits:['change', 'update:value'],
|
||||
setup(props, {emit}){
|
||||
const rangeValue = ref([])
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
||||
watch(()=>props.value, (val)=>{
|
||||
if(val){
|
||||
rangeValue.value = val.split(',')
|
||||
}else{
|
||||
rangeValue.value = []
|
||||
}
|
||||
}, {immediate: true});
|
||||
|
||||
|
||||
function handleChange(arr){
|
||||
let str = ''
|
||||
if(arr && arr.length>0){
|
||||
if(arr[1] && arr[0]){
|
||||
str = arr.join(',')
|
||||
}
|
||||
}
|
||||
emit('change', str);
|
||||
emit('update:value', str);
|
||||
formItemContext.onFieldChange();
|
||||
}
|
||||
return {
|
||||
rangeValue,
|
||||
placeholder,
|
||||
handleChange
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -13,6 +13,7 @@
|
|||
v-bind="attrs"
|
||||
@change="onChange"
|
||||
@search="onSearch"
|
||||
:tree-checkable="treeCheckAble"
|
||||
>
|
||||
</a-tree-select>
|
||||
</template>
|
||||
|
@ -50,8 +51,12 @@
|
|||
reload: propTypes.number.def(1),
|
||||
//update-begin-author:taoyan date:2022-11-8 for: issues/4173 Online JTreeSelect控件changeOptions方法未生效
|
||||
url: propTypes.string.def(''),
|
||||
params: propTypes.object.def({})
|
||||
params: propTypes.object.def({}),
|
||||
//update-end-author:taoyan date:2022-11-8 for: issues/4173 Online JTreeSelect控件changeOptions方法未生效
|
||||
//update-begin---author:wangshuai date: 20230202 for: 新增是否有复选框
|
||||
//默认没有选择框
|
||||
treeCheckAble: propTypes.bool.def(false),
|
||||
//update-end---author:wangshuai date: 20230202 for: 新增是否有复选框
|
||||
});
|
||||
const attrs = useAttrs();
|
||||
const emit = defineEmits(['change', 'update:value']);
|
||||
|
|
|
@ -291,7 +291,7 @@
|
|||
fileSize: item.size,
|
||||
};
|
||||
newFileList.push(fileJson);
|
||||
} else {
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
<template>
|
||||
<BasicModal
|
||||
@register="register"
|
||||
:getContainer="getContainer"
|
||||
:canFullscreen="false"
|
||||
:title="title"
|
||||
:width="500"
|
||||
destroyOnClose
|
||||
@ok="handleOk"
|
||||
wrapClassName="j-user-select-modal2" >
|
||||
|
||||
<div style="position: relative; min-height: 350px">
|
||||
<div style="width: 100%">
|
||||
<a-input v-model:value="searchText" allowClear style="width: 100%" placeholder="搜索">
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #c0c0c0" />
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
|
||||
<!-- tabs -->
|
||||
<div class="modal-select-list-container">
|
||||
<div class="scroll">
|
||||
<div class="content" style="right: -10px">
|
||||
|
||||
<label class="item" v-for="item in showDataList" @click="(e)=>onSelect(e, item)">
|
||||
<a-checkbox v-model:checked="item.checked">
|
||||
<span>{{ item.name }}</span>
|
||||
</a-checkbox>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选中用户 -->
|
||||
<div class="selected-users" style="width: 100%; overflow-x: hidden">
|
||||
<SelectedUserItem v-for="item in selectedList" :info="item" @unSelect="unSelect" />
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||
import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { computed, ref, toRaw, watch } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'PositionSelectModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
SelectedUserItem,
|
||||
},
|
||||
props: {
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
getContainer: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
title:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'sys_position',
|
||||
},
|
||||
appId: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
emits: ['selected', 'register'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
const searchText = ref('');
|
||||
const selectedIdList = computed(() => {
|
||||
let arr = selectedList.value;
|
||||
if (!arr || arr.length == 0) {
|
||||
return [];
|
||||
} else {
|
||||
return arr.map((k) => k.id);
|
||||
}
|
||||
});
|
||||
|
||||
watch(()=>props.appId, async (val)=>{
|
||||
if(val){
|
||||
await loadDataList();
|
||||
}
|
||||
}, {immediate: true});
|
||||
|
||||
|
||||
// 弹窗事件
|
||||
const [register] = useModalInner(() => {
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
}
|
||||
for(let item of list){
|
||||
item.checked = false
|
||||
}
|
||||
});
|
||||
|
||||
// 确定事件
|
||||
function handleOk() {
|
||||
let arr = toRaw(selectedIdList.value);
|
||||
emit('selected', arr);
|
||||
}
|
||||
|
||||
const dataList = ref<any[]>([]);
|
||||
const showDataList = computed(()=>{
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return []
|
||||
}
|
||||
let text = searchText.value;
|
||||
if(!text){
|
||||
return list
|
||||
}
|
||||
return list.filter(item=>item.name.indexOf(text)>=0)
|
||||
});
|
||||
|
||||
const selectedList = computed(()=>{
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return []
|
||||
}
|
||||
return list.filter(item=>item.checked)
|
||||
});
|
||||
|
||||
function unSelect(id) {
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return;
|
||||
}
|
||||
let arr = list.filter(item=>item.id == id);
|
||||
arr[0].checked = false;
|
||||
}
|
||||
|
||||
async function loadDataList() {
|
||||
let params = {
|
||||
pageNo: 1,
|
||||
pageSize: 200,
|
||||
column: 'createTime',
|
||||
order: 'desc'
|
||||
};
|
||||
const url = '/sys/position/list'
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
let arr:any[] = [];
|
||||
if(records && records.length>0){
|
||||
for(let item of records){
|
||||
arr.push({
|
||||
id: item.id,
|
||||
name: item.name || item.roleName,
|
||||
selectType: props.type,
|
||||
checked: false
|
||||
})
|
||||
}
|
||||
}
|
||||
dataList.value = arr;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('loadDataList', data);
|
||||
}
|
||||
|
||||
|
||||
function onSelect(e, item) {
|
||||
prevent(e);
|
||||
console.log('onselect');
|
||||
item.checked = !item.checked;
|
||||
}
|
||||
|
||||
function prevent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
return {
|
||||
register,
|
||||
showDataList,
|
||||
searchText,
|
||||
handleOk,
|
||||
selectedList,
|
||||
selectedIdList,
|
||||
unSelect,
|
||||
onSelect
|
||||
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.modal-select-list-container{
|
||||
height: 352px;
|
||||
margin-top: 12px;
|
||||
overflow: auto;
|
||||
.scroll{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.content{
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
.item{
|
||||
padding: 7px 5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
&:hover{
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.j-user-select-modal2 {
|
||||
.depart-select {
|
||||
.ant-select-selector {
|
||||
color: #fff !important;
|
||||
background-color: #409eff !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
.ant-select-selection-item,
|
||||
.ant-select-arrow {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
.my-search {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
z-index: 1;
|
||||
&.all-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #0a8fe9 !important;
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.my-tabs {
|
||||
}
|
||||
|
||||
.selected-users {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,282 @@
|
|||
<template>
|
||||
<BasicModal
|
||||
@register="register"
|
||||
:getContainer="getContainer"
|
||||
:canFullscreen="false"
|
||||
:title="title"
|
||||
:width="500"
|
||||
destroyOnClose
|
||||
@ok="handleOk"
|
||||
wrapClassName="j-user-select-modal2" >
|
||||
|
||||
<div style="position: relative; min-height: 350px">
|
||||
<div style="width: 100%">
|
||||
<a-input v-model:value="searchText" allowClear style="width: 100%" placeholder="搜索">
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #c0c0c0" />
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
|
||||
<!-- tabs -->
|
||||
<div class="modal-select-list-container">
|
||||
<div class="scroll">
|
||||
<div class="content" style="right: -10px">
|
||||
|
||||
<label class="item" v-for="item in showDataList" @click="(e)=>onSelect(e, item)">
|
||||
<a-checkbox v-model:checked="item.checked">
|
||||
<span class="text">{{ item.name }}</span>
|
||||
</a-checkbox>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选中用户 -->
|
||||
<div class="selected-users" style="width: 100%; overflow-x: hidden">
|
||||
<SelectedUserItem v-for="item in selectedList" :info="item" @unSelect="unSelect" />
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||
import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
import { computed, ref, toRaw, watch } from 'vue';
|
||||
export default {
|
||||
name: 'RoleSelectModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
SelectedUserItem,
|
||||
},
|
||||
props: {
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
getContainer: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
title:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'sys_role',
|
||||
},
|
||||
appId: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
emits: ['selected', 'register'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
const searchText = ref('');
|
||||
const selectedIdList = computed(() => {
|
||||
let arr = selectedList.value;
|
||||
if (!arr || arr.length == 0) {
|
||||
return [];
|
||||
} else {
|
||||
return arr.map((k) => k.id);
|
||||
}
|
||||
});
|
||||
|
||||
watch(()=>props.appId, async (val)=>{
|
||||
if(val){
|
||||
await loadDataList();
|
||||
}
|
||||
}, {immediate: true});
|
||||
|
||||
|
||||
// 弹窗事件
|
||||
const [register] = useModalInner(() => {
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
}
|
||||
for(let item of list){
|
||||
item.checked = false
|
||||
}
|
||||
});
|
||||
|
||||
// 确定事件
|
||||
function handleOk() {
|
||||
let arr = toRaw(selectedIdList.value);
|
||||
emit('selected', arr);
|
||||
}
|
||||
|
||||
const dataList = ref<any[]>([]);
|
||||
const showDataList = computed(()=>{
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return []
|
||||
}
|
||||
let text = searchText.value;
|
||||
if(!text){
|
||||
return list
|
||||
}
|
||||
return list.filter(item=>item.name.indexOf(text)>=0)
|
||||
});
|
||||
|
||||
const selectedList = computed(()=>{
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return []
|
||||
}
|
||||
return list.filter(item=>item.checked)
|
||||
});
|
||||
|
||||
function unSelect(id) {
|
||||
let list = dataList.value;
|
||||
if(!list || list.length ==0 ){
|
||||
return;
|
||||
}
|
||||
let arr = list.filter(item=>item.id == id);
|
||||
arr[0].checked = false;
|
||||
}
|
||||
|
||||
async function loadDataList() {
|
||||
let params = {
|
||||
pageNo: 1,
|
||||
pageSize: 200,
|
||||
column: 'createTime',
|
||||
order: 'desc'
|
||||
};
|
||||
const url = '/sys/role/listByTenant';
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
let arr:any[] = [];
|
||||
if(records && records.length>0){
|
||||
for(let item of records){
|
||||
arr.push({
|
||||
id: item.id,
|
||||
name: item.name || item.roleName,
|
||||
selectType: props.type,
|
||||
checked: false
|
||||
})
|
||||
}
|
||||
}
|
||||
dataList.value = arr;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('loadDataList', data);
|
||||
}
|
||||
|
||||
|
||||
function onSelect(e, item) {
|
||||
prevent(e);
|
||||
console.log('onselect');
|
||||
item.checked = !item.checked;
|
||||
}
|
||||
|
||||
function prevent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
return {
|
||||
register,
|
||||
showDataList,
|
||||
searchText,
|
||||
handleOk,
|
||||
selectedList,
|
||||
selectedIdList,
|
||||
unSelect,
|
||||
onSelect
|
||||
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.modal-select-list-container{
|
||||
height: 352px;
|
||||
margin-top: 12px;
|
||||
overflow: auto;
|
||||
.scroll{
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.content{
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
.item{
|
||||
padding: 7px 5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
&:hover{
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.j-user-select-modal2 {
|
||||
.depart-select {
|
||||
.ant-select-selector {
|
||||
color: #fff !important;
|
||||
background-color: #409eff !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
.ant-select-selection-item,
|
||||
.ant-select-arrow {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
.my-search {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
z-index: 1;
|
||||
&.all-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #0a8fe9 !important;
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.my-tabs {
|
||||
}
|
||||
|
||||
.selected-users {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<div class="user-selected-item">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
padding-right: 10px;
|
||||
vertical-align: middle;
|
||||
background-color: #f5f5f5;
|
||||
"
|
||||
>
|
||||
<span style="width: 24px; height: 24px; line-height: 20px; margin-right: 3px; display: inline-block">
|
||||
<a-avatar v-if="info.avatar" :src="getFileAccessHttpUrl(info.avatar)" :size="24"></a-avatar>
|
||||
|
||||
<a-avatar v-else-if="info.selectType == 'sys_role'" :size="24" style="background-color: rgb(255, 173, 0);">
|
||||
<template #icon>
|
||||
<team-outlined style="font-size: 16px"/>
|
||||
</template>
|
||||
</a-avatar>
|
||||
<a-avatar v-else-if="info.selectType == 'sys_position'" :size="24" style="background-color: rgb(245, 34, 45);">
|
||||
<template #icon>
|
||||
<TagsOutlined style="font-size: 16px"/>
|
||||
</template>
|
||||
</a-avatar>
|
||||
|
||||
<a-avatar :size="24" v-else>
|
||||
<template #icon><UserOutlined /></template>
|
||||
</a-avatar>
|
||||
</span>
|
||||
|
||||
<div style="height: 24px; line-height: 24px" class="ellipsis">
|
||||
{{ info.realname || info.name }}
|
||||
</div>
|
||||
|
||||
<div v-if="showClose" class="icon-close">
|
||||
<CloseOutlined @click="removeSelect"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!showClose" class="icon-remove">
|
||||
<MinusCircleFilled @click="removeSelect" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UserOutlined, CloseOutlined, MinusCircleFilled, TagsOutlined, TeamOutlined } from '@ant-design/icons-vue';
|
||||
import {computed} from 'vue'
|
||||
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||
|
||||
export default {
|
||||
name: 'SelectedUserItem',
|
||||
components: {
|
||||
UserOutlined,
|
||||
MinusCircleFilled,
|
||||
CloseOutlined,
|
||||
TagsOutlined,
|
||||
TeamOutlined
|
||||
},
|
||||
props: {
|
||||
info: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
// 是否作为查询条件
|
||||
query:{
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
emits: ['unSelect'],
|
||||
setup(props, { emit }) {
|
||||
function removeSelect(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
emit('unSelect', props.info.id);
|
||||
}
|
||||
|
||||
const showClose = computed(()=>{
|
||||
if(props.query===true){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
showClose,
|
||||
removeSelect,
|
||||
getFileAccessHttpUrl
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.user-selected-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
height: 30px;
|
||||
border-radius: 12px;
|
||||
line-height: 30px;
|
||||
vertical-align: middle;
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon-remove {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -4px;
|
||||
font-size: 18px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-close{
|
||||
height: 22px;
|
||||
line-height: 24px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-left: 7px;
|
||||
&:hover{
|
||||
color: #0a8fe9;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon-remove {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,175 @@
|
|||
<template>
|
||||
<a-list item-layout="horizontal" :data-source="showDataList">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item style="padding: 3px 0">
|
||||
<div class="user-select-user-info" @click="(e) => onClickUser(e, item)">
|
||||
<div>
|
||||
<a-checkbox v-model:checked="checkStatus[item.id]" />
|
||||
</div>
|
||||
<div>
|
||||
<a-avatar v-if="item.avatar" :src="getFileAccessHttpUrl(item.avatar)"></a-avatar>
|
||||
<a-avatar v-else>
|
||||
<template #icon><UserOutlined /></template>
|
||||
</a-avatar>
|
||||
</div>
|
||||
<div :style="nameStyle">
|
||||
{{ item.realname }}
|
||||
</div>
|
||||
<div :style="departStyle">
|
||||
{{ item.orgCodeTxt }}
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { UserOutlined } from '@ant-design/icons-vue';
|
||||
import { computed, toRaw, reactive, watchEffect, ref } from 'vue';
|
||||
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||
|
||||
export default {
|
||||
name: 'UserList',
|
||||
props: {
|
||||
dataList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 是否显示部门文本
|
||||
depart: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
selectedIdList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
excludeUserIdList:{
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
UserOutlined,
|
||||
},
|
||||
emits: ['selected', 'unSelect'],
|
||||
setup(props, { emit }) {
|
||||
function onClickUser(e, user) {
|
||||
e && prevent(e);
|
||||
let status = checkStatus[user.id];
|
||||
if (status === true) {
|
||||
emit('unSelect', user.id);
|
||||
} else {
|
||||
emit('selected', toRaw(user));
|
||||
}
|
||||
}
|
||||
|
||||
function getTwoText(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
} else {
|
||||
return text.substr(0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const departStyle = computed(() => {
|
||||
if (props.depart === true) {
|
||||
// 如果显示部门信息
|
||||
return {
|
||||
flex: 1,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
display: 'none',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const nameStyle = computed(() => {
|
||||
if (props.depart === true) {
|
||||
// 如果显示部门信息
|
||||
return {
|
||||
width: '200px',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
flex: 1,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function onChangeChecked(e) {
|
||||
console.error('onChangeChecked', e);
|
||||
}
|
||||
|
||||
// const showDataList = ref<any[]>([])
|
||||
const checkStatus = reactive<any>({});
|
||||
watchEffect(() => {
|
||||
let arr1 = props.dataList;
|
||||
if (!arr1 || arr1.length === 0) {
|
||||
return;
|
||||
}
|
||||
let idList = props.selectedIdList;
|
||||
for (let item of arr1) {
|
||||
if (idList.indexOf(item.id) >= 0) {
|
||||
checkStatus[item.id] = true;
|
||||
} else {
|
||||
checkStatus[item.id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
function prevent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function records2DataList() {
|
||||
let arr:any[] = [];
|
||||
let excludeList = props.excludeUserIdList;
|
||||
let records = props.dataList;
|
||||
if(records && records.length>0){
|
||||
for(let item of records){
|
||||
if(excludeList.indexOf(item.id)<0){
|
||||
arr.push({...item})
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const showDataList = computed(()=>{
|
||||
let excludeList = props.excludeUserIdList;
|
||||
if(excludeList && excludeList.length>0){
|
||||
return records2DataList();
|
||||
}
|
||||
return props.dataList;
|
||||
});
|
||||
|
||||
return {
|
||||
onClickUser,
|
||||
getTwoText,
|
||||
departStyle,
|
||||
nameStyle,
|
||||
onChangeChecked,
|
||||
checkStatus,
|
||||
showDataList,
|
||||
getFileAccessHttpUrl
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.user-select-user-info {
|
||||
display: flex;
|
||||
> div {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div :style="containerStyle">
|
||||
<a-tree
|
||||
v-if="treeData.length > 0"
|
||||
:load-data="loadChildren"
|
||||
showIcon
|
||||
autoExpandParent
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
@select="onSelect"
|
||||
>
|
||||
</a-tree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12" style="padding-left: 10px">
|
||||
<div :style="containerStyle">
|
||||
<user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import UserList from './UserList.vue';
|
||||
|
||||
export default {
|
||||
name: 'DepartUserList',
|
||||
components: {
|
||||
UserList,
|
||||
},
|
||||
props: {
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
selectedIdList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
excludeUserIdList:{
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
emits: ['loaded', 'selected', 'unSelect'],
|
||||
setup(props, { emit }) {
|
||||
//export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
|
||||
|
||||
async function loadDepartTree(pid?) {
|
||||
const url = '/sys/sysDepart/queryDepartTreeSync';
|
||||
let params = {};
|
||||
if (pid) {
|
||||
params['pid'] = pid;
|
||||
}
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
console.log('loadDepartTree', data);
|
||||
return data;
|
||||
}
|
||||
|
||||
async function initRoot() {
|
||||
console.log('initRoot');
|
||||
const data = await loadDepartTree();
|
||||
if (data.success) {
|
||||
let arr = data.result;
|
||||
treeData.value = arr;
|
||||
emitDepartOptions(arr);
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
function emitDepartOptions(arr) {
|
||||
let options = [];
|
||||
if (arr && arr.length > 0) {
|
||||
options = arr.map((k) => {
|
||||
return {
|
||||
value: k.id,
|
||||
label: k.departName,
|
||||
};
|
||||
});
|
||||
}
|
||||
emit('loaded', options);
|
||||
}
|
||||
|
||||
initRoot();
|
||||
|
||||
const treeData = ref<any[]>([]);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
const selectedDepartId = ref('');
|
||||
function onSelect(ids, e) {
|
||||
let record = e.node.dataRef;
|
||||
selectedKeys.value = [record.key];
|
||||
|
||||
let id = ids[0];
|
||||
selectedDepartId.value = id;
|
||||
loadUserList();
|
||||
}
|
||||
|
||||
function clear() {
|
||||
selectedDepartId.value = '';
|
||||
}
|
||||
async function loadChildren(treeNode) {
|
||||
console.log('loadChildren', treeNode);
|
||||
const data = await loadDepartTree(treeNode.eventKey);
|
||||
if (data.success) {
|
||||
let arr = data.result;
|
||||
treeNode.dataRef.children = [...arr];
|
||||
treeData.value = [...treeData.value];
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
}
|
||||
|
||||
const maxHeight = ref(300);
|
||||
maxHeight.value = window.innerHeight - 300;
|
||||
const containerStyle = computed(() => {
|
||||
return {
|
||||
'overflow-y': 'auto',
|
||||
'max-height': maxHeight.value + 'px',
|
||||
};
|
||||
});
|
||||
|
||||
const userDataList = ref<any[]>([]);
|
||||
async function loadUserList() {
|
||||
const url = '/sys/user/selectUserList';
|
||||
let params = {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
if (props.searchText) {
|
||||
params['keyword'] = props.searchText;
|
||||
}
|
||||
if (selectedDepartId.value) {
|
||||
params['departId'] = selectedDepartId.value;
|
||||
}
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
userDataList.value = records;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('depart-loadUserList', data);
|
||||
}
|
||||
watch(
|
||||
() => props.searchText,
|
||||
() => {
|
||||
loadUserList();
|
||||
}
|
||||
);
|
||||
|
||||
function onSelectUser(info) {
|
||||
emit('selected', info);
|
||||
}
|
||||
function unSelectUser(id) {
|
||||
emit('unSelect', id);
|
||||
}
|
||||
|
||||
return {
|
||||
containerStyle,
|
||||
treeData,
|
||||
selectedKeys,
|
||||
expandedKeys,
|
||||
onSelect,
|
||||
loadChildren,
|
||||
onSelectUser,
|
||||
unSelectUser,
|
||||
userDataList,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,136 @@
|
|||
<template>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div :style="containerStyle">
|
||||
<a-tree v-if="treeData.length > 0" showIcon :treeData="treeData" :selectedKeys="selectedKeys" @select="onSelect"> </a-tree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12" style="padding-left: 10px">
|
||||
<div :style="containerStyle">
|
||||
<user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import UserList from './UserList.vue';
|
||||
|
||||
export default {
|
||||
name: 'RoleUserList',
|
||||
components: {
|
||||
UserList,
|
||||
},
|
||||
props: {
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
selectedIdList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
excludeUserIdList:{
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
emits: ['selected', 'unSelect'],
|
||||
setup(props, { emit }) {
|
||||
const treeData = ref<any[]>([]);
|
||||
async function loadRoleList() {
|
||||
const url = '/sys/role/listByTenant';
|
||||
let params = {
|
||||
order: 'desc',
|
||||
column: 'createTime',
|
||||
pageSize: 200,
|
||||
};
|
||||
let arr = [];
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
arr = records.map((k) => {
|
||||
return {
|
||||
title: k.roleName,
|
||||
id: k.id,
|
||||
key: k.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
console.log('loadRoleList', data);
|
||||
treeData.value = arr;
|
||||
}
|
||||
loadRoleList();
|
||||
|
||||
const selectedKeys = ref<any[]>([]);
|
||||
const selectedRoleId = ref('');
|
||||
function onSelect(ids, e) {
|
||||
let record = e.node.dataRef;
|
||||
selectedKeys.value = [record.key];
|
||||
|
||||
let id = ids[0];
|
||||
selectedRoleId.value = id;
|
||||
loadUserList();
|
||||
}
|
||||
|
||||
const userDataList = ref<any[]>([]);
|
||||
async function loadUserList() {
|
||||
const url = '/sys/user/selectUserList';
|
||||
let params = {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
if (props.searchText) {
|
||||
params['keyword'] = props.searchText;
|
||||
}
|
||||
if (selectedRoleId.value) {
|
||||
params['roleId'] = selectedRoleId.value;
|
||||
}
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
userDataList.value = records;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('role-loadUserList', data);
|
||||
}
|
||||
watch(
|
||||
() => props.searchText,
|
||||
() => {
|
||||
loadUserList();
|
||||
}
|
||||
);
|
||||
|
||||
function onSelectUser(info) {
|
||||
emit('selected', info);
|
||||
}
|
||||
function unSelectUser(id) {
|
||||
emit('unSelect', id);
|
||||
}
|
||||
|
||||
const maxHeight = ref(300);
|
||||
maxHeight.value = window.innerHeight - 300;
|
||||
const containerStyle = computed(() => {
|
||||
return {
|
||||
'overflow-y': 'auto',
|
||||
'max-height': maxHeight.value + 'px',
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
containerStyle,
|
||||
treeData,
|
||||
selectedKeys,
|
||||
onSelect,
|
||||
onSelectUser,
|
||||
unSelectUser,
|
||||
userDataList,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,347 @@
|
|||
<template>
|
||||
<BasicModal
|
||||
@register="register"
|
||||
:getContainer="getContainer"
|
||||
:canFullscreen="false"
|
||||
title="选择用户"
|
||||
:width="600"
|
||||
wrapClassName="j-user-select-modal2"
|
||||
>
|
||||
<!-- 部门下拉框 -->
|
||||
<a-select v-model:value="selectedDepart" style="width: 100%" class="depart-select" @change="onDepartChange">
|
||||
<a-select-option v-for="item in departOptions" :value="item.value">{{ item.label }}</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<div style="position: relative; min-height: 350px">
|
||||
<!-- 用户搜索框 -->
|
||||
<div :class="searchInputStatus ? 'my-search all-width' : 'my-search'">
|
||||
<span :class="searchInputStatus ? 'hidden' : ''" style="margin-left: 10px"
|
||||
><SearchOutlined style="color: #c0c0c0" @click="showSearchInput"
|
||||
/></span>
|
||||
<div style="width: 100%" :class="searchInputStatus ? '' : 'hidden'">
|
||||
<a-input v-model:value="searchText" @pressEnter="onSearchUser" style="width: 100%" placeholder="请输入用户名按回车搜索">
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #c0c0c0" />
|
||||
</template>
|
||||
<template #suffix>
|
||||
<CloseOutlined title="退出搜索" @click="clearSearch" />
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tabs -->
|
||||
<div class="my-tabs">
|
||||
<a-tabs v-model:activeKey="myActiveKey" :centered="true" @change="onChangeTab">
|
||||
<!-- 所有用户 -->
|
||||
<a-tab-pane key="1" tab="全部" forceRender>
|
||||
<user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" depart @selected="onSelectUser" @unSelect="unSelectUser" />
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 部门用户 -->
|
||||
<a-tab-pane key="2" tab="按部门" forceRender>
|
||||
<depart-user-list
|
||||
:searchText="searchText"
|
||||
:selectedIdList="selectedIdList"
|
||||
:excludeUserIdList="excludeUserIdList"
|
||||
@loaded="initDepartOptions"
|
||||
@selected="onSelectUser"
|
||||
@unSelect="unSelectUser"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 角色用户 -->
|
||||
<a-tab-pane key="3" tab="按角色" forceRender>
|
||||
<role-user-list :excludeUserIdList="excludeUserIdList" :searchText="searchText" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 选中用户 -->
|
||||
<div class="selected-users" style="width: 100%; overflow-x: hidden">
|
||||
<SelectedUserItem v-for="item in selectedUserList" :info="item" @unSelect="unSelectUser" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%">
|
||||
<div class="select-user-page-info">
|
||||
<a-pagination
|
||||
v-if="myActiveKey == '1'"
|
||||
v-model:current="pageNo"
|
||||
size="small"
|
||||
:total="totalRecord"
|
||||
show-quick-jumper
|
||||
@change="onPageChange"
|
||||
/>
|
||||
</div>
|
||||
<a-button type="primary" @click="handleOk">确 定</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||
import UserList from './UserList.vue';
|
||||
import SelectedUserItem from './SelectedUserItem.vue';
|
||||
import DepartUserList from './UserListAndDepart.vue';
|
||||
import RoleUserList from './UserListAndRole.vue';
|
||||
import { Pagination } from 'ant-design-vue';
|
||||
const APagination = Pagination;
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
import { computed, ref, toRaw } from 'vue';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
export default {
|
||||
name: 'UserSelectModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
SelectedUserItem,
|
||||
UserList,
|
||||
DepartUserList,
|
||||
RoleUserList,
|
||||
APagination,
|
||||
},
|
||||
props: {
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
getContainer: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
//是否排除我自己
|
||||
izExcludeMy: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['selected', 'register'],
|
||||
setup(props, { emit }) {
|
||||
const myActiveKey = ref('1');
|
||||
const selectedUserList = ref<any[]>([]);
|
||||
const userStore = useUserStore();
|
||||
const selectedIdList = computed(() => {
|
||||
let arr = selectedUserList.value;
|
||||
if (!arr || arr.length == 0) {
|
||||
return [];
|
||||
} else {
|
||||
return arr.map((k) => k.id);
|
||||
}
|
||||
});
|
||||
// QQYUN-4152【应用】已经存在的用户,添加的时候还可以重复选择
|
||||
const excludeUserIdList = ref<any[]>([]);
|
||||
|
||||
// 弹窗事件
|
||||
const [register] = useModalInner((data) => {
|
||||
let list = data.list;
|
||||
if (list && list.length > 0) {
|
||||
selectedUserList.value = [...list];
|
||||
} else {
|
||||
selectedUserList.value = [];
|
||||
}
|
||||
if(data.excludeUserIdList){
|
||||
excludeUserIdList.value = data.excludeUserIdList;
|
||||
}else{
|
||||
excludeUserIdList.value = [];
|
||||
}
|
||||
//如果排除我自己,直接excludeUserIdList.push排除即可
|
||||
if (props.izExcludeMy) {
|
||||
excludeUserIdList.value.push(userStore.getUserInfo.id);
|
||||
}
|
||||
});
|
||||
|
||||
// 确定事件
|
||||
function handleOk() {
|
||||
let arr = toRaw(selectedUserList.value);
|
||||
emit('selected', arr);
|
||||
}
|
||||
|
||||
/*--------------部门下拉框,用于筛选用户---------------*/
|
||||
const selectedDepart = ref('');
|
||||
const departOptions = ref<any[]>([]);
|
||||
function initDepartOptions(options) {
|
||||
departOptions.value = [{ value: '', label: '全部用户' }, ...options];
|
||||
selectedDepart.value = '';
|
||||
}
|
||||
function onDepartChange() {
|
||||
loadUserList();
|
||||
}
|
||||
/*--------------部门下拉框,用于筛选用户---------------*/
|
||||
|
||||
/*--------------第一页 搜索框---------------*/
|
||||
const searchInputStatus = ref(false);
|
||||
const searchText = ref('');
|
||||
function showSearchInput(e) {
|
||||
e && prevent(e);
|
||||
searchInputStatus.value = true;
|
||||
}
|
||||
function onSearchUser() {
|
||||
console.log('onSearchUser');
|
||||
loadUserList();
|
||||
}
|
||||
function clearSearch(e) {
|
||||
e && prevent(e);
|
||||
searchText.value = '';
|
||||
searchInputStatus.value = false;
|
||||
loadUserList();
|
||||
}
|
||||
/*--------------第一页 搜索框---------------*/
|
||||
|
||||
/*--------------加载数据---------------*/
|
||||
const pageNo = ref(1);
|
||||
const totalRecord = ref(0);
|
||||
const userDataList = ref<any[]>([]);
|
||||
async function onPageChange() {
|
||||
console.log('onPageChange', pageNo.value);
|
||||
await loadUserList();
|
||||
}
|
||||
async function loadUserList() {
|
||||
const url = '/sys/user/selectUserList';
|
||||
let params = {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: 10,
|
||||
};
|
||||
if (searchText.value) {
|
||||
params['keyword'] = searchText.value;
|
||||
}
|
||||
if (selectedDepart.value) {
|
||||
params['departId'] = selectedDepart.value;
|
||||
}
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records, total } = data.result;
|
||||
totalRecord.value = total;
|
||||
userDataList.value = records;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('loadUserList', data);
|
||||
}
|
||||
/*--------------加载数据---------------*/
|
||||
|
||||
/*--------------选中/取消选中---------------*/
|
||||
function onSelectUser(info) {
|
||||
if (props.multi === true) {
|
||||
let arr = selectedUserList.value;
|
||||
let idList = selectedIdList.value;
|
||||
if (idList.indexOf(info.id) < 0) {
|
||||
arr.push({ ...info });
|
||||
selectedUserList.value = arr;
|
||||
}
|
||||
} else {
|
||||
selectedUserList.value = [{ ...info }];
|
||||
}
|
||||
}
|
||||
function unSelectUser(id) {
|
||||
let arr = selectedUserList.value;
|
||||
let index = -1;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].id === id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
arr.splice(index, 1);
|
||||
selectedUserList.value = arr;
|
||||
}
|
||||
}
|
||||
/*--------------选中/取消选中---------------*/
|
||||
|
||||
function onChangeTab(tab) {
|
||||
myActiveKey.value = tab;
|
||||
}
|
||||
|
||||
function prevent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
//加载第一页数据
|
||||
loadUserList();
|
||||
|
||||
return {
|
||||
selectedDepart,
|
||||
departOptions,
|
||||
initDepartOptions,
|
||||
onDepartChange,
|
||||
|
||||
register,
|
||||
handleOk,
|
||||
|
||||
searchText,
|
||||
searchInputStatus,
|
||||
showSearchInput,
|
||||
onSearchUser,
|
||||
clearSearch,
|
||||
|
||||
myActiveKey,
|
||||
onChangeTab,
|
||||
|
||||
pageNo,
|
||||
totalRecord,
|
||||
onPageChange,
|
||||
userDataList,
|
||||
selectedUserList,
|
||||
selectedIdList,
|
||||
onSelectUser,
|
||||
unSelectUser,
|
||||
excludeUserIdList
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-user-select-modal2 {
|
||||
.depart-select {
|
||||
.ant-select-selector {
|
||||
color: #fff !important;
|
||||
background-color: #409eff !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
.ant-select-selection-item,
|
||||
.ant-select-arrow {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
.my-search {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
z-index: 1;
|
||||
&.all-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #0a8fe9 !important;
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.my-tabs {
|
||||
}
|
||||
|
||||
.selected-users {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,245 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="isSearchFormComp" @click="click2Add" :class="disabled?'disabled-user-select':''" style="padding:0 5px;background-color: #fff;border: 1px solid #ccc;border-radius: 3px;box-sizing: border-box;display:flex;color: #9e9e9e;font-size: 14px;flex-wrap: wrap;min-height: 32px;">
|
||||
<template v-if="selectedUserList.length > 0">
|
||||
<SelectedUserItem v-for="item in showUserList" :info="item" @unSelect="unSelectUser" query />
|
||||
</template>
|
||||
<span v-else style="height: 30px;line-height: 30px;display: inline-block;margin-left: 7px;color: #bfbfbf;">请选择用户</span>
|
||||
<div v-if="ellipsisInfo.status" class="user-selected-item">
|
||||
<div class="user-select-ellipsis">
|
||||
<span style="color: red">+{{ellipsisInfo.count}}...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else style="display: flex; flex-wrap: wrap; flex-direction: row" >
|
||||
<template v-if="selectedUserList.length > 0">
|
||||
<SelectedUserItem v-for="item in selectedUserList" :info="item" @unSelect="unSelectUser" />
|
||||
</template>
|
||||
<a-button v-if="showAddButton" shape="circle" @click="onShowModal"><PlusOutlined /></a-button>
|
||||
</div>
|
||||
|
||||
<user-select-modal :multi="multi" :getContainer="getContainer" @register="registerModal" @selected="onSelected" :izExcludeMy="izExcludeMy"></user-select-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref, computed, toRaw } from 'vue';
|
||||
import { Form } from 'ant-design-vue';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import UserSelectModal from './UserSelectModal.vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import SelectedUserItem from './SelectedUserItem.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserSelect',
|
||||
components: {
|
||||
PlusOutlined,
|
||||
UserSelectModal,
|
||||
SelectedUserItem,
|
||||
},
|
||||
props: {
|
||||
store: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
getContainer: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
// 是否作为查询条件
|
||||
query:{
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//最多显示几个人员-query为true有效
|
||||
maxCount:{
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
disabled:{
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//是否排除我自己
|
||||
izExcludeMy:{
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
emits: ['update:value', 'change'],
|
||||
setup(props, { emit }) {
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
const loading = ref(true);
|
||||
const selectedUserList = ref<any[]>([]);
|
||||
const showUserList = computed(()=>{
|
||||
let list = selectedUserList.value
|
||||
let max = props.maxCount;
|
||||
if(list.length<=max){
|
||||
return list;
|
||||
}
|
||||
return list.filter((_item, index)=>index<max);
|
||||
});
|
||||
const ellipsisInfo = computed(()=>{
|
||||
let max = props.maxCount;
|
||||
let len = selectedUserList.value.length
|
||||
if(len > max){
|
||||
return {status: true, count: len-max};
|
||||
}else{
|
||||
return {status: false}
|
||||
}
|
||||
});
|
||||
|
||||
// 注册弹窗
|
||||
const [registerModal, { openModal, closeModal }] = useModal();
|
||||
function onShowModal() {
|
||||
if(props.disabled===true){
|
||||
return ;
|
||||
}
|
||||
let list = toRaw(selectedUserList.value);
|
||||
openModal(true, {
|
||||
list,
|
||||
});
|
||||
}
|
||||
|
||||
function onSelected(arr) {
|
||||
console.log('onSelected', arr);
|
||||
selectedUserList.value = arr;
|
||||
onSelectedChange();
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function onSelectedChange() {
|
||||
loading.value = false;
|
||||
let temp: any[] = [];
|
||||
let arr = selectedUserList.value;
|
||||
if (arr && arr.length > 0) {
|
||||
temp = arr.map((k) => {
|
||||
return k[props.store];
|
||||
});
|
||||
}
|
||||
let str = temp.join(',');
|
||||
emit('update:value', str);
|
||||
emit('change', str);
|
||||
formItemContext.onFieldChange();
|
||||
console.log('选中数据', str);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
if (loading.value === true) {
|
||||
await getUserList(val);
|
||||
}
|
||||
} else {
|
||||
selectedUserList.value = [];
|
||||
}
|
||||
loading.value = true;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
async function getUserList(ids) {
|
||||
const url = '/sys/user/list';
|
||||
let params = {
|
||||
[props.store]: ids,
|
||||
};
|
||||
selectedUserList.value = [];
|
||||
const data = await defHttp.get({ url, params }, { isTransformResponse: false });
|
||||
if (data.success) {
|
||||
const { records } = data.result;
|
||||
selectedUserList.value = records;
|
||||
} else {
|
||||
console.error(data.message);
|
||||
}
|
||||
console.log('getUserList', data);
|
||||
}
|
||||
|
||||
const showAddButton = computed(() => {
|
||||
if(props.disabled === true){
|
||||
return false;
|
||||
}
|
||||
if (props.multi === true) {
|
||||
return true;
|
||||
} else {
|
||||
if (selectedUserList.value.length > 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function unSelectUser(id) {
|
||||
console.log('unSelectUser', id);
|
||||
let arr = selectedUserList.value;
|
||||
let index = -1;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].id == id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
arr.splice(index, 1);
|
||||
selectedUserList.value = arr;
|
||||
|
||||
onSelectedChange();
|
||||
}
|
||||
}
|
||||
|
||||
function click2Add(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onShowModal();
|
||||
}
|
||||
|
||||
const isSearchFormComp = computed(()=>{
|
||||
if(props.query===true){
|
||||
return true;
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
registerModal,
|
||||
onShowModal,
|
||||
isSearchFormComp,
|
||||
onSelected,
|
||||
showAddButton,
|
||||
unSelectUser,
|
||||
selectedUserList,
|
||||
showUserList,
|
||||
ellipsisInfo,
|
||||
click2Add
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.user-select-ellipsis{
|
||||
width: 40px;
|
||||
height: 24px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
border-radius: 8px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
.disabled-user-select{
|
||||
cursor: not-allowed;
|
||||
background-color: #f5f5f5 !important;
|
||||
}
|
||||
</style>
|
|
@ -29,6 +29,9 @@ export function useTreeBiz(treeRef, getList, props) {
|
|||
watch(
|
||||
selectValues,
|
||||
({ value: values }: Recordable) => {
|
||||
if(!values){
|
||||
return;
|
||||
}
|
||||
if (openModal.value == false && values.length > 0) {
|
||||
loadingEcho.value = isFirstLoadEcho;
|
||||
isFirstLoadEcho = false;
|
||||
|
|
|
@ -115,4 +115,7 @@ export const basicProps = {
|
|||
labelAlign: propTypes.string,
|
||||
|
||||
rowProps: Object as PropType<RowProps>,
|
||||
|
||||
// 当表单是查询条件的时候 当表单改变后自动查询,不需要点击查询按钮
|
||||
autoSearch: propTypes.bool.def(false),
|
||||
};
|
||||
|
|
|
@ -141,6 +141,14 @@ export type ComponentType =
|
|||
| 'JSearchSelect'
|
||||
| 'JAddInput'
|
||||
| 'Time'
|
||||
| 'OnlineSelectCascade'
|
||||
| 'LinkTableCard'
|
||||
| 'LinkTableSelect'
|
||||
| 'LinkTableForQuery'
|
||||
| 'CascaderPcaForQuery'
|
||||
| 'UserSelect'
|
||||
| 'RangeDate'
|
||||
| 'RangeNumber'
|
||||
| 'linkRecordSelect'
|
||||
| 'RangeTime'
|
||||
| 'JRangeNumber';
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
copy: propTypes.bool.def(false),
|
||||
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
|
||||
disabled: propTypes.bool.def(true),
|
||||
clearSelect: propTypes.bool.def(false)
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change', 'update:value']);
|
||||
|
@ -126,6 +127,13 @@
|
|||
}
|
||||
|
||||
function handleClick(icon: string) {
|
||||
if(props.clearSelect === true){
|
||||
if(currentSelect.value===icon){
|
||||
currentSelect.value = ''
|
||||
}else{
|
||||
currentSelect.value = icon;
|
||||
}
|
||||
}else{
|
||||
currentSelect.value = icon;
|
||||
if (props.copy) {
|
||||
clipboardRef.value = icon;
|
||||
|
@ -135,6 +143,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function handleSearchChange(e: ChangeEvent) {
|
||||
const value = e.target.value;
|
||||
if (!value) {
|
||||
|
|
|
@ -142,10 +142,13 @@
|
|||
});
|
||||
|
||||
watchEffect(() => {
|
||||
visibleRef.value = !!props.visible;
|
||||
fullScreenRef.value = !!props.defaultFullscreen;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
visibleRef.value = !!props.visible;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => unref(visibleRef),
|
||||
(v) => {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<!-- 是否开启评论区域 -->
|
||||
<template v-if="enableComment">
|
||||
<Tooltip title="关闭" placement="bottom" v-if="commentSpan>0">
|
||||
<Tooltip title="收起" placement="bottom" v-if="commentSpan>0">
|
||||
<RightSquareOutlined @click="handleCloseComment" style="font-size: 16px"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="展开" placement="bottom" v-else>
|
||||
|
@ -50,7 +50,7 @@
|
|||
prefixCls,
|
||||
`${prefixCls}--custom`,
|
||||
{
|
||||
[`${prefixCls}--can-full`]: props.canFullscreen,
|
||||
[`${prefixCls}--can-full`]: props.canFullscreen || props.enableComment,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
|
|
@ -205,9 +205,13 @@
|
|||
footer: unref(getFooterProps),
|
||||
...unref(getExpandOption),
|
||||
};
|
||||
if (slots.expandedRowRender) {
|
||||
|
||||
//update-begin---author:wangshuai ---date:20230214 for:[QQYUN-4237]代码生成 内嵌子表模式 没有滚动条------------
|
||||
//额外的展开行存在插槽时会将滚动移除掉,注释掉
|
||||
/*if (slots.expandedRowRender) {
|
||||
propsData = omit(propsData, 'scroll');
|
||||
}
|
||||
}*/
|
||||
//update-end---author:wangshuai ---date:20230214 for:[QQYUN-4237]代码生成 内嵌子表模式 没有滚动条------------
|
||||
|
||||
propsData = omit(propsData, ['class', 'onChange']);
|
||||
return propsData;
|
||||
|
|
|
@ -65,7 +65,9 @@ export function useTableScroll(
|
|||
if (!tableEl) return;
|
||||
|
||||
if (!bodyEl) {
|
||||
bodyEl = tableEl.querySelector('.ant-table-body');
|
||||
//update-begin-author:taoyan date:2023-2-11 for: issues/355 前端-jeecgboot-vue3 3.4.4版本,BasicTable高度自适应功能失效,设置BasicTable组件maxHeight失效; 原因已找到,请看详情
|
||||
bodyEl = tableEl.querySelector('.ant-table-tbody');
|
||||
//update-end-author:taoyan date:2023-2-11 for: issues/355 前端-jeecgboot-vue3 3.4.4版本,BasicTable高度自适应功能失效,设置BasicTable组件maxHeight失效; 原因已找到,请看详情
|
||||
if (!bodyEl) return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +93,7 @@ export function useTableScroll(
|
|||
await nextTick();
|
||||
//Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
|
||||
|
||||
const headEl = tableEl.querySelector('.ant-table-thead ');
|
||||
const headEl = tableEl.querySelector('.ant-table-thead');
|
||||
|
||||
if (!headEl) return;
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ export interface ActionItem extends ButtonProps {
|
|||
tooltip?: string | TooltipProps;
|
||||
// 自定义类名
|
||||
class?: string | Record<string, boolean> | any[];
|
||||
// 自定义图标颜色
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
export interface PopConfirm {
|
||||
|
|
|
@ -89,6 +89,9 @@
|
|||
let props = originColumn.value.props || {};
|
||||
props['mode'] = 'multiple';
|
||||
props['maxTagCount'] = 1;
|
||||
//update-begin-author:taoyan date:2022-12-5 for: issues/271 Online表单主子表单下拉多选无法搜索
|
||||
originColumn.value.allowSearch = true;
|
||||
//update-end-author:taoyan date:2022-12-5 for: issues/271 Online表单主子表单下拉多选无法搜索
|
||||
originColumn.value.props = props;
|
||||
} else if (searchTypes.includes(props.type)) {
|
||||
// 处理搜索
|
||||
|
|
|
@ -353,6 +353,7 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
|
|||
insertIndex: index,
|
||||
$table: xTable,
|
||||
target: instanceRef.value,
|
||||
isModalData: options?.isModalData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -368,6 +369,8 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
|
|||
setActive?: boolean;
|
||||
//是否需要触发change事件
|
||||
emitChange?:boolean
|
||||
// 是否是modal弹窗添加的数据
|
||||
isModalData?:boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
queryParam,
|
||||
dictOptions,
|
||||
},
|
||||
] = usePopBiz(getBindValue);
|
||||
] = usePopBiz(getBindValue, tableRef);
|
||||
|
||||
const showSearchFlag = computed(() => unref(queryInfo) && unref(queryInfo).length > 0);
|
||||
/**
|
||||
|
|
|
@ -559,6 +559,11 @@ export function usePopBiz(props, tableRef?) {
|
|||
}
|
||||
}
|
||||
dataSource.value = data.records;
|
||||
//update-begin-author:taoyan date:2023-2-11 for:issues/356 在线报表分页有问题
|
||||
tableRef.value && tableRef.value.setPagination({
|
||||
total: Number(data.total)
|
||||
})
|
||||
//update-end-author:taoyan date:2023-2-11 for:issues/356 在线报表分页有问题
|
||||
} else {
|
||||
pagination.total = 0;
|
||||
dataSource.value = [];
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<div class="user-avatar-info">
|
||||
<a-popover title="" :overlayStyle="{width: '250px'}">
|
||||
<template #content>
|
||||
<div style="display: flex;flex-direction: row;align-items: center">
|
||||
<div style="width: 60px;text-align: center">
|
||||
<a-avatar v-if="userAvatar" :src="userAvatar" :size="47"/>
|
||||
<a-avatar v-else :size="47">{{ getAvatarText() }}</a-avatar>
|
||||
</div>
|
||||
<div style="flex: 1;display: flex;flex-direction: column;margin-left: 12px">
|
||||
<div style="color: #000;display: inline-block;font-size: 15px;font-weight: 700;margin-top: 3px;vertical-align: top;width: 170px;">
|
||||
{{ userLabel }}
|
||||
</div>
|
||||
<div style="color: #757575;display: block;margin-top: 4px;">
|
||||
{{ phone }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<span style="cursor: pointer">
|
||||
<a-avatar v-if="userAvatar" :src="userAvatar" :loadError="loadError"/>
|
||||
<a-avatar v-else>{{ getAvatarText() }}</a-avatar>
|
||||
</span>
|
||||
</a-popover>
|
||||
<span class="realname-ellipsis">
|
||||
{{ userLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, watchEffect, defineComponent } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserAvatar',
|
||||
props: {
|
||||
username: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
detail:{
|
||||
type: Object,
|
||||
default: ()=>{},
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const userAvatar = ref('');
|
||||
const userLabel = ref('');
|
||||
const phone = ref('');
|
||||
|
||||
watchEffect(async ()=>{
|
||||
userAvatar.value = '';
|
||||
userLabel.value = '';
|
||||
phone.value = '';
|
||||
let username = props.username;
|
||||
if(username){
|
||||
await initUserInfo(username);
|
||||
}
|
||||
let userInfo = props.detail;
|
||||
if(userInfo){
|
||||
if(userInfo.avatar){
|
||||
userAvatar.value = getFileAccessHttpUrl(userInfo.avatar);
|
||||
}
|
||||
if(userInfo.realname){
|
||||
userLabel.value = userInfo.realname;
|
||||
}
|
||||
if(userInfo.phone){
|
||||
phone.value = userInfo.phone;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function initUserInfo(val) {
|
||||
const params = {
|
||||
username: val,
|
||||
};
|
||||
const url = '/sys/user/getMultiUser';
|
||||
const data = await defHttp.get({ url, params }, {isTransformResponse: false});
|
||||
if(data && data.length > 0){
|
||||
let temp = data[0].avatar;
|
||||
if (temp) {
|
||||
userAvatar.value = getFileAccessHttpUrl(temp)
|
||||
}
|
||||
userLabel.value = data[0].realname;
|
||||
phone.value = data[0].phone;
|
||||
}else{
|
||||
console.log(data)
|
||||
}
|
||||
}
|
||||
|
||||
function getAvatarText() {
|
||||
let text = userLabel.value;
|
||||
if (!text) {
|
||||
text = props.username;
|
||||
}
|
||||
if (text) {
|
||||
if (text.length > 2) {
|
||||
return text.substr(0, 2);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function loadError() {
|
||||
userAvatar.value = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userAvatar,
|
||||
userLabel,
|
||||
getAvatarText,
|
||||
phone,
|
||||
loadError
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.user-avatar-info{
|
||||
.ant-avatar-image{
|
||||
cursor: pointer;
|
||||
}
|
||||
.realname-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div class="comment-tabs-warp" v-if="showStatus">
|
||||
<a-tabs @change="handleChange" :animated="false">
|
||||
<a-tab-pane tab="评论" key="comment" class="comment-list-tab">
|
||||
<a-tab-pane v-if="showComment" tab="评论" key="comment" class="comment-list-tab">
|
||||
<comment-list :tableName="tableName" :dataId="dataId" :datetime="datetime1"></comment-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="文件" key="file">
|
||||
<a-tab-pane v-if="showFiles" tab="文件" key="file">
|
||||
<comment-files :tableName="tableName" :dataId="dataId" :datetime="datetime2"></comment-files>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="日志" key="log">
|
||||
<a-tab-pane v-if="showDataLog" tab="日志" key="log">
|
||||
<data-log-list :tableName="tableName" :dataId="dataId" :datetime="datetime3"></data-log-list>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
@ -35,6 +35,12 @@
|
|||
props: {
|
||||
tableName: propTypes.string.def(''),
|
||||
dataId: propTypes.string.def(''),
|
||||
// 显示评论
|
||||
showComment: propTypes.bool.def(true),
|
||||
// 显示文件
|
||||
showFiles: propTypes.bool.def(true),
|
||||
// 显示日志
|
||||
showDataLog: propTypes.bool.def(true),
|
||||
},
|
||||
setup(props) {
|
||||
const showStatus = computed(() => {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick">
|
||||
<a-menu-item v-if="syncToApp" key="to-app">同步到{{ name }}</a-menu-item>
|
||||
<a-menu-item v-if="syncToLocal" key="to-local">同步到本地</a-menu-item>
|
||||
<a-menu-item v-if="getSyncToLocal" key="to-local">同步到本地</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
/* JThirdAppButton 的子组件,不可单独使用 */
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
type: String,
|
||||
|
@ -26,6 +27,14 @@
|
|||
// 声明Emits
|
||||
const emit = defineEmits(['to-app', 'to-local']);
|
||||
|
||||
const getSyncToLocal = computed(() => {
|
||||
// 由于企业微信接口变更,将不再支持同步到本地
|
||||
if (props.type === 'wechatEnterprise') {
|
||||
return false;
|
||||
}
|
||||
return props.syncToLocal;
|
||||
});
|
||||
|
||||
function handleMenuClick(event) {
|
||||
emit(event.key, { type: props.type });
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ export const TENANT_ID = 'TENANT_ID';
|
|||
// login info key
|
||||
export const LOGIN_INFO_KEY = 'LOGIN__INFO__';
|
||||
|
||||
// 聊天UID key
|
||||
export const JEECG_CHAT_UID = 'JEECG_CHAT_UID';
|
||||
|
||||
export enum CacheTypeEnum {
|
||||
SESSION,
|
||||
LOCAL,
|
||||
|
|
|
@ -42,7 +42,7 @@ export enum ConfigEnum {
|
|||
// Sign
|
||||
Sign = 'X-Sign',
|
||||
// 租户id
|
||||
TENANT_ID = 'tenant-id',
|
||||
TENANT_ID = 'X-Tenant-Id',
|
||||
// 版本
|
||||
VERSION = 'X-Version',
|
||||
// 低代码应用ID
|
||||
|
|
|
@ -9,4 +9,6 @@ export enum PageEnum {
|
|||
ERROR_LOG_PAGE = '/error-log/list',
|
||||
// auth2登录路由路径
|
||||
OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login',
|
||||
//文件路由
|
||||
SYS_FILES_PATH = '/file/share',
|
||||
}
|
||||
|
|
|
@ -73,8 +73,10 @@ export function useMethods() {
|
|||
<span>具体详情请<a href = ${href} download = ${fileName}> 点击下载 </a> </span>
|
||||
</div>`,
|
||||
});
|
||||
} else if (fileInfo.code === 500) {
|
||||
//update-begin---author:wangshuai ---date:20221121 for:[VUEN-2827]导入无权限,提示图标错误------------
|
||||
} else if (fileInfo.code === 500 || fileInfo.code === 510) {
|
||||
createMessage.error(fileInfo.message || `${data.file.name} 导入失败`);
|
||||
//update-end---author:wangshuai ---date:20221121 for:[VUEN-2827]导入无权限,提示图标错误------------
|
||||
} else {
|
||||
createMessage.success(fileInfo.message || `${data.file.name} 文件上传成功`);
|
||||
}
|
||||
|
|
|
@ -88,11 +88,19 @@ const getBaseOptions = () => {
|
|||
};
|
||||
|
||||
function createModalOptions(options: ModalOptionsPartial, icon: string): ModalOptionsPartial {
|
||||
//update-begin-author:taoyan date:2023-1-10 for: 可以自定义图标
|
||||
let titleIcon:any = ''
|
||||
if(options.icon){
|
||||
titleIcon = options.icon;
|
||||
}else{
|
||||
titleIcon = getIcon(icon)
|
||||
}
|
||||
//update-end-author:taoyan date:2023-1-10 for: 可以自定义图标
|
||||
return {
|
||||
...getBaseOptions(),
|
||||
...options,
|
||||
content: renderContent(options),
|
||||
icon: getIcon(icon),
|
||||
icon: titleIcon
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,15 @@ import { isArray } from '/@/utils/is';
|
|||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
|
||||
// User permissions related operations
|
||||
export function usePermission(formData?) {
|
||||
export function usePermission() {
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
//动态加载流程节点表单权限
|
||||
let formData: any = {};
|
||||
function initBpmFormData(_bpmFormData) {
|
||||
formData = _bpmFormData;
|
||||
}
|
||||
const { closeAll } = useTabs(router);
|
||||
|
||||
//==================================工作流权限判断-begin=========================================
|
||||
|
@ -165,5 +170,5 @@ export function usePermission(formData?) {
|
|||
}
|
||||
//update-end-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
|
||||
|
||||
return { changeRole, hasPermission, togglePermissionMode, refreshMenu, isDisabledAuth };
|
||||
return { changeRole, hasPermission, togglePermissionMode, refreshMenu, isDisabledAuth, initBpmFormData };
|
||||
}
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
initWebSocket();
|
||||
});
|
||||
|
||||
const messageCount = ref<number>(0)
|
||||
function mapAnnouncement(item) {
|
||||
return {
|
||||
...item,
|
||||
|
@ -92,12 +91,6 @@
|
|||
listData.value[1].list = sysMsgList.map(mapAnnouncement);
|
||||
listData.value[0].count = anntMsgTotal;
|
||||
listData.value[1].count = sysMsgTotal;
|
||||
//update-begin-author:taoyan date:2022-8-30 for: 消息数量改变触发chat组件事件
|
||||
let msgCount = anntMsgTotal+sysMsgTotal;
|
||||
//update-begin-author:wangshuai date:2022-09-02 for: 消息未读数为0也需要传递,因为聊天需要计算总数
|
||||
messageCount.value = msgCount
|
||||
//update-end-author:wangshuai date:2022-09-02 for: 消息未读数为0也需要传递,因为聊天需要计算总数
|
||||
//update-end-author:taoyan date:2022-8-30 for: 消息数量改变触发chat组件事件
|
||||
} catch (e) {
|
||||
console.warn('系统消息通知异常:', e);
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
* 提交数据
|
||||
*/
|
||||
async function handleSubmit() {
|
||||
if (unref(isMultiTenant) && !unref(tenantSelected)) {
|
||||
if (unref(isMultiTenant) && unref(tenantSelected)==null) {
|
||||
validate_status.value = 'error';
|
||||
return false;
|
||||
}
|
||||
|
@ -165,6 +165,9 @@
|
|||
userStore.setTenant(unref(tenantSelected));
|
||||
}
|
||||
createMessage.success('切换成功');
|
||||
|
||||
//切换租户后要刷新首页
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log('登录选择出现问题', e);
|
||||
|
@ -187,6 +190,7 @@
|
|||
const result = await selectDepart({
|
||||
username: userStore.getUserInfo.username,
|
||||
orgCode: unref(departSelected),
|
||||
loginTenantId: unref(tenantSelected),
|
||||
});
|
||||
if (result.userInfo) {
|
||||
const userInfo = result.userInfo;
|
||||
|
|
|
@ -156,7 +156,9 @@
|
|||
updatePassword();
|
||||
break;
|
||||
case 'account':
|
||||
go(`/page-demo/account/setting`);
|
||||
//update-begin---author:wangshuai ---date:20221125 for:进入用户设置页面------------
|
||||
go(`/system/usersetting`);
|
||||
//update-end---author:wangshuai ---date:20221125 for:进入用户设置页面--------------
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/>
|
||||
<LayoutBreadcrumb v-if="getShowContent && getShowBread" :theme="getHeaderTheme" />
|
||||
<!-- 欢迎语 -->
|
||||
<span v-if="getShowContent && getShowBreadTitle" :class="[prefixCls, `${prefixCls}--${getHeaderTheme}`,'headerIntroductionClass']"> 欢迎进入 {{ title }} </span>
|
||||
<span v-if="getShowContent && getShowBreadTitle && !getIsMobile" :class="[prefixCls, `${prefixCls}--${getHeaderTheme}`,'headerIntroductionClass']"> 欢迎进入 {{ title }} </span>
|
||||
</div>
|
||||
<!-- left end -->
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ export default {
|
|||
// user dropdown
|
||||
dropdownItemDoc: 'Document',
|
||||
dropdownItemLoginOut: 'Login Out',
|
||||
dropdownItemSwitchPassword: 'Password Change',
|
||||
dropdownItemSwitchDepart: 'Switch Department',
|
||||
dropdownItemRefreshCache: 'Clean cache',
|
||||
dropdownItemSwitchAccount: 'Account Setting',
|
||||
|
||||
tooltipErrorLog: 'Error log',
|
||||
tooltipLock: 'Lock screen',
|
||||
|
|
|
@ -94,9 +94,17 @@ export default {
|
|||
|
||||
userName: 'Username',
|
||||
password: 'Password',
|
||||
inputCode: 'Verification code',
|
||||
confirmPassword: 'Confirm Password',
|
||||
email: 'Email',
|
||||
smsCode: 'SMS code',
|
||||
mobile: 'Mobile',
|
||||
|
||||
//重置密码页面英文
|
||||
authentication:'authentication',
|
||||
resetLoginPassword:'reset login password',
|
||||
resetSuccess:'reset succeeded',
|
||||
nextStep:'next step',
|
||||
goToLogin:'go to login'
|
||||
},
|
||||
};
|
||||
|
|
|
@ -69,7 +69,7 @@ export default {
|
|||
|
||||
signInTitle: 'Jeecg Boot',
|
||||
signInDesc: '是中国最具影响力的 企业级低代码平台!在线开发,可视化拖拽设计,零代码实现80%的基础功能~',
|
||||
policy: '我同意xxx隐私政策',
|
||||
policy: '我同意敲敲云隐私政策',
|
||||
scanSign: `扫码后,即可完成登录`,
|
||||
scanSuccess: `扫码成功,登录中`,
|
||||
|
||||
|
@ -101,5 +101,12 @@ export default {
|
|||
mobile: '手机号码',
|
||||
|
||||
subTitleText: '{0}秒后返回登录页面',
|
||||
|
||||
//重置密码页面中文
|
||||
authentication:'验证身份',
|
||||
resetLoginPassword:'重置登录密码',
|
||||
resetSuccess:'重置成功',
|
||||
nextStep:'下一步',
|
||||
goToLogin:'去登录'
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ import { setupI18n } from '/@/locales/setupI18n';
|
|||
import { registerGlobComp } from '/@/components/registerGlobComp';
|
||||
import { registerThirdComp } from '/@/settings/registerThirdComp';
|
||||
import { useSso } from '/@/hooks/web/useSso';
|
||||
// 注册online模块lib
|
||||
import { registerPackages } from '/@/utils/monorepo/registerPackages';
|
||||
|
||||
// 在本地开发中引入的,以提高浏览器响应速度
|
||||
|
@ -37,7 +38,7 @@ async function bootstrap() {
|
|||
// 初始化内部系统配置
|
||||
initAppConfigStore();
|
||||
|
||||
// 注册外部模块路由
|
||||
// 注册外部模块路由(注册online模块lib)
|
||||
registerPackages(app);
|
||||
|
||||
// 注册全局组件
|
||||
|
|
|
@ -15,10 +15,15 @@ const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
|||
//auth2登录路由
|
||||
const OAUTH2_LOGIN_PAGE_PATH = PageEnum.OAUTH2_LOGIN_PAGE_PATH;
|
||||
|
||||
//分享免登录路由
|
||||
const SYS_FILES_PATH = PageEnum.SYS_FILES_PATH;
|
||||
|
||||
const ROOT_PATH = RootRoute.path;
|
||||
|
||||
//update-begin---author:wangshuai ---date:20220629 for:[issues/I5BG1I]vue3不支持auth2登录------------
|
||||
const whitePathList: PageEnum[] = [LOGIN_PATH, OAUTH2_LOGIN_PAGE_PATH];
|
||||
//update-begin---author:wangshuai ---date:20221111 for: [VUEN-2472]分享免登录------------
|
||||
const whitePathList: PageEnum[] = [LOGIN_PATH, OAUTH2_LOGIN_PAGE_PATH,SYS_FILES_PATH];
|
||||
//update-end---author:wangshuai ---date:20221111 for: [VUEN-2472]分享免登录------------
|
||||
//update-end---author:wangshuai ---date:20220629 for:[issues/I5BG1I]vue3不支持auth2登录------------
|
||||
|
||||
export function createPermissionGuard(router: Router) {
|
||||
|
|
|
@ -5,8 +5,9 @@ import { getParentLayout, LAYOUT, EXCEPTION_COMPONENT } from '/@/router/constant
|
|||
import { cloneDeep, omit } from 'lodash-es';
|
||||
import { warn } from '/@/utils/log';
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import { getTenantId, getToken } from '/@/utils/auth';
|
||||
import { getTenantId, getToken } from "/@/utils/auth";
|
||||
import { URL_HASH_TAB } from '/@/utils';
|
||||
//引入online lib路由
|
||||
import { packageViews } from '/@/utils/monorepo/dynamicRouter';
|
||||
import {useI18n} from "/@/hooks/web/useI18n";
|
||||
|
||||
|
@ -27,7 +28,7 @@ let dynamicViewsModules: Record<string, () => Promise<Recordable>>;
|
|||
function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
|
||||
if (!dynamicViewsModules) {
|
||||
dynamicViewsModules = import.meta.glob('../../views/**/*.{vue,tsx}');
|
||||
// 跟模块views合并
|
||||
//合并online lib路由
|
||||
dynamicViewsModules = Object.assign({}, dynamicViewsModules, packageViews);
|
||||
}
|
||||
if (!routes) return;
|
||||
|
|
|
@ -31,7 +31,9 @@ export const RootRoute: AppRouteRecordRaw = {
|
|||
export const LoginRoute: AppRouteRecordRaw = {
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('/@/views/sys/login/Login.vue'),
|
||||
//新版后台登录,如果想要使用旧版登录放开即可
|
||||
// component: () => import('/@/views/sys/login/Login.vue'),
|
||||
component: () => import('/@/views/system/loginmini/MiniLogin.vue'),
|
||||
meta: {
|
||||
title: t('routes.basic.login'),
|
||||
},
|
||||
|
@ -41,7 +43,9 @@ export const LoginRoute: AppRouteRecordRaw = {
|
|||
export const Oauth2LoginRoute: AppRouteRecordRaw = {
|
||||
path: '/oauth2-app/login',
|
||||
name: 'oauth2-app-login',
|
||||
component: () => import('/@/views/sys/login/OAuth2Login.vue'),
|
||||
//新版钉钉免登录,如果想要使用旧版放开即可
|
||||
// component: () => import('/@/views/sys/login/OAuth2Login.vue'),
|
||||
component: () => import('/@/views/system/loginmini/OAuth2Login.vue'),
|
||||
meta: {
|
||||
title: t('routes.oauth2.login'),
|
||||
},
|
||||
|
|
|
@ -14,6 +14,10 @@ const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
|
|||
interface LocaleState {
|
||||
localInfo: LocaleSetting;
|
||||
pathTitleMap: object;
|
||||
// myapps主题色(低代码应用列表首页)
|
||||
appIndexTheme: string
|
||||
// myapps - 跳转前路由地址
|
||||
appMainPth: string
|
||||
}
|
||||
|
||||
export const useLocaleStore = defineStore({
|
||||
|
@ -21,6 +25,8 @@ export const useLocaleStore = defineStore({
|
|||
state: (): LocaleState => ({
|
||||
localInfo: lsLocaleSetting,
|
||||
pathTitleMap: {},
|
||||
appIndexTheme: '',
|
||||
appMainPth: ''
|
||||
}),
|
||||
getters: {
|
||||
getShowPicker(): boolean {
|
||||
|
@ -34,6 +40,12 @@ export const useLocaleStore = defineStore({
|
|||
return (path) => state.pathTitleMap[path];
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
getAppIndexTheme(): string {
|
||||
return this.appIndexTheme;
|
||||
},
|
||||
getAppMainPth(): string {
|
||||
return this.appMainPth;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
|
@ -58,6 +70,12 @@ export const useLocaleStore = defineStore({
|
|||
this.pathTitleMap[path] = title;
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
setAppIndexTheme(theme) {
|
||||
this.appIndexTheme = theme;
|
||||
},
|
||||
setAppMainPth(path) {
|
||||
this.appMainPth = path;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -123,9 +123,10 @@ export const useUserStore = defineStore({
|
|||
try {
|
||||
const { goHome = true, mode, ...loginParams } = params;
|
||||
const data = await loginApi(loginParams, mode);
|
||||
const { token } = data;
|
||||
const { token, userInfo } = data;
|
||||
// save token
|
||||
this.setToken(token);
|
||||
this.setTenant(userInfo.loginTenantId);
|
||||
return this.afterLoginAction(goHome, data);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
|
@ -246,6 +247,7 @@ export const useUserStore = defineStore({
|
|||
this.setSessionTimeout(false);
|
||||
this.setUserInfo(null);
|
||||
this.setLoginInfo(null);
|
||||
this.setTenant(null);
|
||||
//update-begin-author:liusq date:2022-5-5 for:退出登录后清除拖拽模块的接口前缀
|
||||
localStorage.removeItem(JDragConfigEnum.DRAG_BASE_URL);
|
||||
//update-end-author:liusq date:2022-5-5 for: 退出登录后清除拖拽模块的接口前缀
|
||||
|
|
|
@ -40,7 +40,7 @@ export const rules = {
|
|||
if (required && !value) {
|
||||
return Promise.reject('请输入手机号码1!');
|
||||
}
|
||||
if (!new RegExp(/^1[3|4|5|7|8|9][0-9]\d{8}$/).test(value)) {
|
||||
if (!/^1[3456789]\d{9}$/.test(value)) {
|
||||
return Promise.reject('手机号码格式有误');
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
span: 12,
|
||||
},
|
||||
dynamicRules: ({ values }) => {
|
||||
return values.field8 ? [{ required: true, message: '字段4必填' }] : [];
|
||||
return values.field8 ? [{ required: true, message: '字段必填' }] : [];
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
onSearch: useDebounceFn(onSearch, 300),
|
||||
searchParams,
|
||||
superQueryConfig,
|
||||
handleSuperQuery,
|
||||
handleReset: () => {
|
||||
keyword.value = '';
|
||||
},
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
//ts语法
|
||||
import type { ComputedRef } from 'vue';
|
||||
import { ref, computed, unref, watch, inject } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import JeecgOrderCustomerModal from './components/JeecgOrderCustomerModal.vue';
|
||||
|
@ -42,7 +43,10 @@
|
|||
import { isEmpty } from '/@/utils/is';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
//接收主表id
|
||||
const orderId = inject('orderId') || '';
|
||||
const orderId = inject<ComputedRef<string>>(
|
||||
'orderId',
|
||||
computed(() => '')
|
||||
);
|
||||
//提示弹窗
|
||||
const $message = useMessage();
|
||||
//弹窗model
|
||||
|
@ -51,7 +55,7 @@
|
|||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: customList,
|
||||
api: getCustomList,
|
||||
tableSetting:{
|
||||
cacheKey:'customer'
|
||||
},
|
||||
|
@ -70,13 +74,24 @@
|
|||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const [registerTable, { reload, setSelectedRowKeys }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
watch(orderId, () => {
|
||||
searchInfo['orderId'] = unref(orderId);
|
||||
reload();
|
||||
// 主表id变化时,清空子表的选中状态
|
||||
setSelectedRowKeys([]);
|
||||
});
|
||||
|
||||
async function getCustomList(params) {
|
||||
let { orderId } = params;
|
||||
// 主表Id为空时,不查询子表数据,直接返回空数组
|
||||
if (orderId == null || isEmpty(orderId)) {
|
||||
return [];
|
||||
}
|
||||
return await customList(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
//ts语法
|
||||
import type { ComputedRef } from 'vue';
|
||||
import { ref, computed, unref, watch, inject } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import JeecgOrderTicketModal from './components/JeecgOrderTicketModal.vue';
|
||||
|
@ -42,7 +43,10 @@
|
|||
import { isEmpty } from '/@/utils/is';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
//接收主表id
|
||||
const orderId = inject('orderId');
|
||||
const orderId = inject<ComputedRef<string>>(
|
||||
'orderId',
|
||||
computed(() => '')
|
||||
);
|
||||
//提示弹窗
|
||||
const $message = useMessage();
|
||||
//弹窗model
|
||||
|
@ -51,7 +55,7 @@
|
|||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: ticketList,
|
||||
api: getTicketList,
|
||||
tableSetting:{
|
||||
cacheKey:'ticket'
|
||||
},
|
||||
|
@ -70,13 +74,24 @@
|
|||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const [registerTable, { reload, setSelectedRowKeys }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
watch(orderId, () => {
|
||||
searchInfo['orderId'] = unref(orderId);
|
||||
reload();
|
||||
// 主表id变化时,清空子表的选中状态
|
||||
setSelectedRowKeys([]);
|
||||
});
|
||||
|
||||
async function getTicketList(params) {
|
||||
let { orderId } = params;
|
||||
// 主表Id为空时,不查询子表数据,直接返回空数组
|
||||
if (orderId == null || isEmpty(orderId)) {
|
||||
return [];
|
||||
}
|
||||
return await ticketList(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
|
|
|
@ -179,7 +179,7 @@ export const customerFormSchema: FormSchema[] = [
|
|||
label: '联系方式',
|
||||
field: 'telphone',
|
||||
component: 'Input',
|
||||
rules: [{ required: false, pattern: /^1[3|4|5|7|8|9][0-9]\d{8}$/, message: '手机号码格式有误' }],
|
||||
rules: [{ required: false, pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' }],
|
||||
},
|
||||
{
|
||||
label: 'orderId',
|
||||
|
|
|
@ -138,6 +138,21 @@ export const schemas: FormSchema[] = [
|
|||
label: '选择值',
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'xldx2',
|
||||
component: 'JSelectMultiple',
|
||||
label: '字典下拉多选2',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
dictCode: 'sex',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'xldx2',
|
||||
component: 'JEllipsis',
|
||||
label: '选择值',
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'dxxlk',
|
||||
component: 'JDictSelectTag',
|
||||
|
@ -645,26 +660,21 @@ export const schemas: FormSchema[] = [
|
|||
label: '选中值',
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
|
||||
{
|
||||
field: 'radioButtonGroup',
|
||||
component: 'RadioButtonGroup',
|
||||
label: 'RadioButtonGroup',
|
||||
field: 'userSelect2',
|
||||
component: 'UserSelect',
|
||||
label: '高级用户选择',
|
||||
helpMessage: ['component模式'],
|
||||
colProps: { span: 12 },
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ value: '0',icon: 'ant-design:setting'},
|
||||
{ label: '停用', value: '1',icon: 'mdi:home' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'radioButtonGroup',
|
||||
field: 'userSelect2',
|
||||
component: 'JEllipsis',
|
||||
label: '选中值',
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
|
||||
{
|
||||
field: 'superQuery',
|
||||
component: 'Input',
|
||||
|
|
|
@ -78,7 +78,7 @@ export const baseSetschemas: FormSchema[] = [
|
|||
dynamicRules: ({ model, schema }) => {
|
||||
return [
|
||||
{ ...rules.duplicateCheckRule('sys_user', 'phone', model, schema, false)[0] },
|
||||
{ pattern: /^1[3|4|5|7|8|9][0-9]\d{8}$/, message: '手机号码格式有误' },
|
||||
{ pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' },
|
||||
];
|
||||
},
|
||||
colProps: { span: 18 },
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
|
||||
const { isDisabledAuth, hasPermission, initBpmFormData} = usePermission();
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
|
@ -47,7 +48,9 @@ export const columns: BasicColumn[] = [
|
|||
];
|
||||
|
||||
export function getBpmFormSchema(formData) {
|
||||
const { isDisabledAuth, hasPermission } = usePermission(formData);
|
||||
//注入流程节点表单权限
|
||||
initBpmFormData(formData);
|
||||
|
||||
const formSchema2: FormSchema[] = [
|
||||
{
|
||||
label: '订单号',
|
||||
|
@ -94,7 +97,9 @@ export function getBpmFormSchema(formData) {
|
|||
}
|
||||
|
||||
export function getOrderCustomerFormSchema(formData) {
|
||||
const { isDisabledAuth, hasPermission } = usePermission(formData);
|
||||
//注入流程节点表单权限
|
||||
initBpmFormData(formData);
|
||||
|
||||
const formSchema2: FormSchema[] = [
|
||||
{
|
||||
label: '客户名',
|
||||
|
|
|
@ -46,7 +46,7 @@ export const columns: JVxeColumn[] = [
|
|||
placeholder: '请输入${title}',
|
||||
validateRules: [
|
||||
{
|
||||
pattern: '^1(3|4|5|7|8)\\d{9}$',
|
||||
pattern: '^1[3456789]\\d{9}$',
|
||||
message: '${title}格式不正确',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
const rememberMe = ref(false);
|
||||
|
||||
const formData = reactive({
|
||||
account: 'admin',
|
||||
account: 'jeecg',
|
||||
password: '123456',
|
||||
inputCode: '',
|
||||
});
|
||||
|
|
|
@ -137,6 +137,12 @@
|
|||
* 处理部门情况
|
||||
*/
|
||||
function bizDepart(loginResult) {
|
||||
//如果登录接口返回了用户上次登录租户ID,则不需要重新选择
|
||||
if(loginResult.userInfo?.orgCode && loginResult.userInfo?.orgCode!==''){
|
||||
isMultiDepart.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let multi_depart = loginResult.multi_depart;
|
||||
//0:无部门 1:一个部门 2:多个部门
|
||||
if (multi_depart == 0) {
|
||||
|
@ -158,6 +164,12 @@
|
|||
* 处理租户情况
|
||||
*/
|
||||
function bizTenantList(loginResult) {
|
||||
//如果登录接口返回了用户上次登录租户ID,则不需要重新选择
|
||||
if(loginResult.userInfo?.loginTenantId && loginResult.userInfo?.loginTenantId!==0){
|
||||
isMultiTenant.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let tenantArr = loginResult.tenantList;
|
||||
if (Array.isArray(tenantArr)) {
|
||||
if (tenantArr.length === 0) {
|
||||
|
@ -210,10 +222,10 @@
|
|||
*/
|
||||
function departResolve() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!unref(isMultiDepart)) {
|
||||
if (!unref(isMultiDepart) && !unref(isMultiTenant)) {
|
||||
resolve();
|
||||
} else {
|
||||
let params = { orgCode: formState.orgCode, username: unref(username) };
|
||||
let params = { orgCode: formState.orgCode,loginTenantId: formState.tenantId, username: unref(username) };
|
||||
defHttp.put({ url: '/sys/selectDepart', params }).then((res) => {
|
||||
if (res.userInfo) {
|
||||
userStore.setUserInfo(res.userInfo);
|
||||
|
|
|
@ -21,6 +21,9 @@ export enum Api {
|
|||
|
||||
getCurrentUserDeparts = '/sys/user/getCurrentUserDeparts',
|
||||
selectDepart = '/sys/selectDepart',
|
||||
getUpdateDepartInfo = '/sys/user/getUpdateDepartInfo',
|
||||
doUpdateDepartInfo = '/sys/user/doUpdateDepartInfo',
|
||||
changeDepartChargePerson = '/sys/user/changeDepartChargePerson',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,3 +96,27 @@ export const getUserDeparts = (params?) => defHttp.get({ url: Api.getCurrentUser
|
|||
* 切换选择部门
|
||||
*/
|
||||
export const selectDepart = (params?) => defHttp.put({ url: Api.selectDepart, params });
|
||||
|
||||
/**
|
||||
* 编辑部门前获取部门相关信息
|
||||
* @param id
|
||||
*/
|
||||
export const getUpdateDepartInfo = (id) => defHttp.get({ url: Api.getUpdateDepartInfo, params: {id} });
|
||||
|
||||
/**
|
||||
* 编辑部门
|
||||
* @param params
|
||||
*/
|
||||
export const doUpdateDepartInfo = (params) => defHttp.put({ url: Api.doUpdateDepartInfo, params });
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
* @param id
|
||||
*/
|
||||
export const deleteDepart = (id) => defHttp.delete({ url: Api.delete, params:{ id } }, { joinParamsToUrl: true });
|
||||
|
||||
/**
|
||||
* 设置负责人 取消负责人
|
||||
* @param params
|
||||
*/
|
||||
export const changeDepartChargePerson = (params) => defHttp.put({ url: Api.changeDepartChargePerson, params });
|
||||
|
|
|
@ -9,7 +9,7 @@ export const columns: BasicColumn[] = [
|
|||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '字典编号',
|
||||
title: '字典编码',
|
||||
dataIndex: 'dictCode',
|
||||
width: 240,
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ export const recycleBincolumns: BasicColumn[] = [
|
|||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '字典编号',
|
||||
title: '字典编码',
|
||||
dataIndex: 'dictCode',
|
||||
width: 120,
|
||||
},
|
||||
|
|
|
@ -211,4 +211,10 @@ export const formSchema: FormSchema[] = [
|
|||
placeholder: '请输入个人简介',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'updateCount',
|
||||
label: '乐观锁',
|
||||
show: false,
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -53,12 +53,15 @@
|
|||
<a-button preIcon="ant-design:import-outlined" type="primary">导入</a-button>
|
||||
</a-upload>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="handleExportXls('单表示例', getExportUrl,exportParams)">导出</a-button>
|
||||
<a-button preIcon="ant-design:filter" type="primary" @click="">高级查询</a-button>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="openTab">打开Tab页</a-button>
|
||||
<a-button preIcon="ant-design:retweet-outlined" type="primary" @click="customSearch = !customSearch">{{
|
||||
customSearch ? '表单配置查询' : '自定义查询'
|
||||
}}</a-button>
|
||||
<a-button preIcon="ant-design:import-outlined" type="primary" @click="handleImport">弹窗导入</a-button>
|
||||
|
||||
<super-query :config="superQueryConfig" @search="handleSuperQuery"/>
|
||||
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
|
@ -122,7 +125,7 @@
|
|||
},
|
||||
//自定义默认排序
|
||||
defSort: {
|
||||
column: 'sex,salaryMoney',
|
||||
column: 'createTime,sex',
|
||||
order: 'desc',
|
||||
},
|
||||
striped: true,
|
||||
|
@ -285,6 +288,17 @@
|
|||
}
|
||||
//自定义查询----end---------
|
||||
|
||||
const superQueryConfig = reactive({
|
||||
name:{ title: "名称", view: "text", type: "string", order: 1 },
|
||||
sex:{ title: "性别", view: "list", type: "string", dictCode:'sex', order: 2 },
|
||||
});
|
||||
|
||||
function handleSuperQuery(params) {
|
||||
Object.keys(params).map(k=>{
|
||||
queryParam[k] = params[k]
|
||||
});
|
||||
searchQuery();
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.jeecg-basic-table-form-container {
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="adTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox aui-formEwm">
|
||||
<div class="aui-formWell">
|
||||
<form>
|
||||
<div class="aui-flex aui-form-nav investment_title" style="padding-bottom: 19px">
|
||||
<div class="aui-flex-box activeNav">{{t('sys.login.qrSignInFormTitle')}}</div>
|
||||
</div>
|
||||
<div class="aui-form-box">
|
||||
<div class="aui-account" style="padding: 30px 0">
|
||||
<div class="aui-ewm">
|
||||
<QrCode :value="qrCodeUrl" class="enter-x flex justify-center xl:justify-start" :width="280" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<a class="aui-linek-code aui-link-register" @click="goBackHandleClick">{{t('sys.login.backSignIn')}}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="aui-flex aui-third-text">
|
||||
<div class="aui-flex-box aui-third-border">
|
||||
<span>{{ t('sys.login.otherSignIn') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex" :class="`${prefixCls}-sign-in-way`">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="github" @click="onThirdLogin('github')"><GithubFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="企业微信" @click="onThirdLogin('wechat_enterprise')"><icon-font class="item-icon" type="icon-qiyeweixin3" /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="钉钉" @click="onThirdLogin('dingtalk')"><DingtalkCircleFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="微信" @click="onThirdLogin('wechat_open')"><WechatFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第三方登录相关弹框 -->
|
||||
<ThirdModal ref="thirdModalRef"></ThirdModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="mini-code-login">
|
||||
import { ref, onUnmounted } from 'vue';
|
||||
import { getLoginQrcode, getQrcodeToken } from '/@/api/sys/user';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { QrCode } from '/@/components/Qrcode/index';
|
||||
import ThirdModal from '/@/views/sys/login/ThirdModal.vue';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from "/@/hooks/web/useDesign";
|
||||
import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
|
||||
});
|
||||
const { prefixCls } = useDesign('minilogin');
|
||||
const { t } = useI18n();
|
||||
const qrCodeUrl = ref<string>('');
|
||||
let timer: IntervalHandle;
|
||||
const state = ref('0');
|
||||
const thirdModalRef = ref();
|
||||
const userStore = useUserStore();
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
|
||||
//加载二维码信息
|
||||
function loadQrCode() {
|
||||
state.value = '0';
|
||||
getLoginQrcode().then((res) => {
|
||||
qrCodeUrl.value = res.qrcodeId;
|
||||
if (res.qrcodeId) {
|
||||
openTimer(res.qrcodeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
//监控扫码状态
|
||||
function watchQrcodeToken(qrcodeId) {
|
||||
getQrcodeToken({ qrcodeId: qrcodeId }).then((res) => {
|
||||
let token = res.token;
|
||||
if (token == '-2') {
|
||||
//二维码过期重新获取
|
||||
loadQrCode();
|
||||
clearInterval(timer);
|
||||
}
|
||||
//扫码成功
|
||||
if (res.success) {
|
||||
state.value = '2';
|
||||
clearInterval(timer);
|
||||
setTimeout(() => {
|
||||
userStore.qrCodeLogin(token);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 开启定时器 */
|
||||
function openTimer(qrcodeId) {
|
||||
watchQrcodeToken(qrcodeId);
|
||||
closeTimer();
|
||||
timer = setInterval(() => {
|
||||
watchQrcodeToken(qrcodeId);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
/** 关闭定时器 */
|
||||
function closeTimer() {
|
||||
if (timer) clearInterval(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param type
|
||||
*/
|
||||
function onThirdLogin(type) {
|
||||
thirdModalRef.value.onThirdLogin(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initFrom() {
|
||||
loadQrCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBackHandleClick() {
|
||||
emit('go-back');
|
||||
closeTimer();
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
closeTimer();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initFrom,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
|
@ -0,0 +1,282 @@
|
|||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="adTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<div class="aui-step-box">
|
||||
<div class="aui-step-item" :class="activeKey === 1 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>1</em>
|
||||
<p>{{t('sys.login.authentication')}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-step-item" :class="activeKey === 2 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>2</em>
|
||||
<p>{{t('sys.login.resetLoginPassword')}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-step-item" :class="activeKey === 3 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>3</em>
|
||||
<p>{{t('sys.login.resetSuccess')}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="" style="height: 230px; position: relative">
|
||||
<a-form ref="formRef" :model="formData" v-if="activeKey === 1">
|
||||
<!-- 身份验证 begin -->
|
||||
<div class="aui-account aui-account-line aui-forgot">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input type="text" :placeholder="t('sys.login.mobile')" v-model:value="formData.mobile" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-form-item>
|
||||
<a-input type="text" :placeholder="t('sys.login.smsCode')" v-model:value="formData.smscode" />
|
||||
</a-form-item>
|
||||
<div v-if="showInterval" class="aui-code-line" @click="getLoginCode">{{t('component.countdown.normalText')}}</div>
|
||||
<div v-else class="aui-code-line">{{t('component.countdown.sendText',[unref(timeRuning)])}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 身份验证 end -->
|
||||
</a-form>
|
||||
<a-form ref="pwdFormRef" :model="pwdFormData" v-else-if="activeKey === 2">
|
||||
<!-- 重置密码 begin -->
|
||||
<div class="aui-account aui-account-line aui-forgot">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input type="password" :placeholder="t('sys.login.passwordPlaceholder')" v-model:value="pwdFormData.password" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input type="password" :placeholder="t('sys.login.confirmPassword')" v-model:value="pwdFormData.confirmPassword" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<!-- 重置密码 end -->
|
||||
</a-form>
|
||||
<!-- 重置成功 begin -->
|
||||
<div class="aui-success" v-else>
|
||||
<div class="aui-success-icon">
|
||||
<img :src="successImg"/>
|
||||
</div>
|
||||
<h3>恭喜您,重置密码成功!</h3>
|
||||
</div>
|
||||
<!-- 重置成功 end -->
|
||||
</div>
|
||||
<div class="aui-formButton" style="padding-bottom: 40px">
|
||||
<div class="aui-flex" v-if="activeKey === 1 || activeKey === 2">
|
||||
<a class="aui-link-login aui-flex-box" @click="nextStepClick">{{t('sys.login.nextStep')}}</a>
|
||||
</div>
|
||||
<div class="aui-flex" v-else>
|
||||
<a class="aui-linek-code aui-flex-box" @click="toLogin">{{t('sys.login.goToLogin')}}</a>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="goBack"> {{ t('sys.login.backSignIn') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="mini-forgotpad" setup>
|
||||
import { reactive, ref, toRaw, unref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { SmsEnum, useFormRules, useFormValid, useLoginState } from '/@/views/sys/login/useLogin';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getCaptcha, passwordChange, phoneVerify } from '/@/api/sys/user';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png'
|
||||
import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png'
|
||||
import successImg from '/@/assets/loginmini/icon/icon-success.png'
|
||||
|
||||
//下一步控制
|
||||
const activeKey = ref<number>(1);
|
||||
const { t } = useI18n();
|
||||
const { handleBackLogin } = useLoginState();
|
||||
const { notification, createMessage, createErrorModal } = useMessage();
|
||||
//是否显示获取验证码
|
||||
const showInterval = ref<boolean>(true);
|
||||
//60s
|
||||
const timeRuning = ref<number>(60);
|
||||
//定时器
|
||||
const timer = ref<any>(null);
|
||||
const formRef = ref();
|
||||
const pwdFormRef = ref();
|
||||
//账号数据
|
||||
const accountInfo = reactive<any>({});
|
||||
//手机号表单
|
||||
const formData = reactive({
|
||||
mobile: '',
|
||||
smscode: '',
|
||||
});
|
||||
//密码表单
|
||||
const pwdFormData = reactive<any>({
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
});
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
|
||||
/**
|
||||
* 下一步
|
||||
*/
|
||||
async function handleNext() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.smscode) {
|
||||
createMessage.warn(t('sys.login.smsPlaceholder'));
|
||||
return;
|
||||
}
|
||||
const resultInfo = await phoneVerify(
|
||||
toRaw({
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
})
|
||||
);
|
||||
if (resultInfo.success) {
|
||||
Object.assign(accountInfo, {
|
||||
username: resultInfo.result.username,
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
});
|
||||
activeKey.value = 2;
|
||||
setTimeout(()=>{
|
||||
pwdFormRef.value.resetFields();
|
||||
},300)
|
||||
} else {
|
||||
notification.error({
|
||||
message: '错误提示',
|
||||
description: resultInfo.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成修改密码
|
||||
*/
|
||||
async function finishedPwd() {
|
||||
if (!pwdFormData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!pwdFormData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.confirmPassword'));
|
||||
return;
|
||||
}
|
||||
if (pwdFormData.password !== pwdFormData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.diffPwd'));
|
||||
return;
|
||||
}
|
||||
const resultInfo = await passwordChange(
|
||||
toRaw({
|
||||
username: accountInfo.username,
|
||||
password: pwdFormData.password,
|
||||
smscode: accountInfo.smscode,
|
||||
phone: accountInfo.phone,
|
||||
})
|
||||
);
|
||||
if (resultInfo.success) {
|
||||
accountInfo.password = pwdFormData.password;
|
||||
//修改密码
|
||||
activeKey.value = 3;
|
||||
} else {
|
||||
//错误提示
|
||||
createErrorModal({
|
||||
title: t('sys.api.errorTip'),
|
||||
content: resultInfo.message || t('sys.api.networkExceptionMsg'),
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 下一步
|
||||
*/
|
||||
function nextStepClick() {
|
||||
if (unref(activeKey) == 1) {
|
||||
handleNext();
|
||||
} else if (unref(activeKey) == 2) {
|
||||
finishedPwd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去登录
|
||||
*/
|
||||
function toLogin() {
|
||||
emit('success', { username: accountInfo.username, password: accountInfo.password });
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBack() {
|
||||
emit('go-back');
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*/
|
||||
async function getLoginCode() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
const result = await getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.FORGET_PASSWORD });
|
||||
if (result) {
|
||||
const TIME_COUNT = 60;
|
||||
if (!unref(timer)) {
|
||||
timeRuning.value = TIME_COUNT;
|
||||
showInterval.value = false;
|
||||
timer.value = setInterval(() => {
|
||||
if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
|
||||
timeRuning.value = timeRuning.value - 1;
|
||||
} else {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initForm() {
|
||||
activeKey.value = 1;
|
||||
Object.assign(formData, { phone: '', smscode: '' });
|
||||
Object.assign(pwdFormData, { password: '', confirmPassword: '' });
|
||||
Object.assign(accountInfo, {});
|
||||
if(unref(timer)){
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
showInterval.value = true;
|
||||
}
|
||||
setTimeout(()=>{
|
||||
formRef.value.resetFields();
|
||||
},300)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initForm,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
|
@ -0,0 +1,557 @@
|
|||
<template>
|
||||
<div :class="prefixCls" class="login-background-img">
|
||||
<AppLocalePicker class="absolute top-4 right-4 enter-x xl:text-gray-600" :showText="false"/>
|
||||
<AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
|
||||
<div class="aui-logo" v-if="!getIsMobile">
|
||||
<div>
|
||||
<h3>
|
||||
<img :src="logoImg" alt="jeecg" />
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="aui-phone-logo">
|
||||
<img :src="logoImg" alt="jeecg" />
|
||||
</div>
|
||||
<div v-show="type === 'login'">
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="adTextImg" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<div class="aui-flex aui-form-nav investment_title">
|
||||
<div class="aui-flex-box" :class="activeIndex === 'accountLogin' ? 'activeNav on' : ''" @click="loginClick('accountLogin')"
|
||||
>{{ t('sys.login.signInFormTitle') }}
|
||||
</div>
|
||||
<div class="aui-flex-box" :class="activeIndex === 'phoneLogin' ? 'activeNav on' : ''" @click="loginClick('phoneLogin')"
|
||||
>{{ t('sys.login.mobileSignInFormTitle') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-form-box" style="height: 180px">
|
||||
<a-form ref="loginRef" :model="formData" v-if="activeIndex === 'accountLogin'" @keyup.enter.native="loginHandleClick">
|
||||
<div class="aui-account">
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input class="fix-auto-fill" :placeholder="t('sys.login.userName')" v-model:value="formData.username" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-password"></i>
|
||||
<a-form-item>
|
||||
<a-input class="fix-auto-fill" type="password" :placeholder="t('sys.login.password')" v-model:value="formData.password" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.inputCode')" v-model:value="formData.inputCode" />
|
||||
</a-form-item>
|
||||
<div class="aui-code">
|
||||
<img v-if="randCodeData.requestCodeSuccess" :src="randCodeData.randCodeImage" @click="handleChangeCheckCode" />
|
||||
<img v-else style="margin-top: 2px; max-width: initial" :src="codeImg" @click="handleChangeCheckCode" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-choice">
|
||||
<a-input class="fix-auto-fill" type="checkbox" v-model:value="rememberMe" />
|
||||
<span style="margin-left: 5px">{{ t('sys.login.rememberMe') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-forget">
|
||||
<a @click="forgetHandelClick"> {{ t('sys.login.forgetPassword') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
<a-form v-else ref="phoneFormRef" :model="phoneFormData" @keyup.enter.native="loginHandleClick">
|
||||
<div class="aui-account phone">
|
||||
<div class="aui-inputClear phoneClear">
|
||||
<a-input class="fix-auto-fill" :placeholder="t('sys.login.mobile')" v-model:value="phoneFormData.mobile" />
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<a-input class="fix-auto-fill" :maxlength="6" :placeholder="t('sys.login.smsCode')" v-model:value="phoneFormData.smscode" />
|
||||
<div v-if="showInterval" class="aui-code" @click="getLoginCode">
|
||||
<a>{{ t('component.countdown.normalText') }}</a>
|
||||
</div>
|
||||
<div v-else class="aui-code">
|
||||
<span class="aui-get-code code-shape">{{ t('component.countdown.sendText', [unref(timeRuning)]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<div class="aui-flex">
|
||||
<a-button :loading="loginLoading" class="aui-link-login aui-flex-box" type="primary" @click="loginHandleClick">
|
||||
{{ t('sys.login.loginButton') }}</a-button>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="codeHandleClick">{{ t('sys.login.qrSignInFormTitle') }}</a>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="registerHandleClick">{{ t('sys.login.registerButton') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-form @keyup.enter.native="loginHandleClick">
|
||||
<div class="aui-flex aui-third-text">
|
||||
<div class="aui-flex-box aui-third-border">
|
||||
<span>{{ t('sys.login.otherSignIn') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex" :class="`${prefixCls}-sign-in-way`">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a title="github" @click="onThirdLogin('github')"><GithubFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a title="企业微信" @click="onThirdLogin('wechat_enterprise')"><icon-font class="item-icon" type="icon-qiyeweixin3" /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a title="钉钉" @click="onThirdLogin('dingtalk')"><DingtalkCircleFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a title="微信" @click="onThirdLogin('wechat_open')"><WechatFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="type === 'forgot'" :class="`${prefixCls}-form`">
|
||||
<MiniForgotpad ref="forgotRef" @go-back="goBack" @success="handleSuccess" />
|
||||
</div>
|
||||
<div v-show="type === 'register'" :class="`${prefixCls}-form`">
|
||||
<MiniRegister ref="registerRef" @go-back="goBack" @success="handleSuccess" />
|
||||
</div>
|
||||
<div v-show="type === 'codeLogin'" :class="`${prefixCls}-form`">
|
||||
<MiniCodelogin ref="codeRef" @go-back="goBack" @success="handleSuccess" />
|
||||
</div>
|
||||
<!-- 第三方登录相关弹框 -->
|
||||
<ThirdModal ref="thirdModalRef"></ThirdModal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="login-mini">
|
||||
import { getCaptcha, getCodeInfo } from '/@/api/sys/user';
|
||||
import { computed, onMounted, reactive, ref, toRaw, unref } from 'vue';
|
||||
import codeImg from '/@/assets/images/checkcode.png';
|
||||
import { Rule } from '/@/components/Form';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { SmsEnum } from '/@/views/sys/login/useLogin';
|
||||
import ThirdModal from '/@/views/sys/login/ThirdModal.vue';
|
||||
import MiniForgotpad from './MiniForgotpad.vue';
|
||||
import MiniRegister from './MiniRegister.vue';
|
||||
import MiniCodelogin from './MiniCodelogin.vue';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application';
|
||||
import { useLocaleStore } from '/@/store/modules/locale';
|
||||
import { useDesign } from "/@/hooks/web/useDesign";
|
||||
import { useAppInject } from "/@/hooks/web/useAppInject";
|
||||
import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
|
||||
});
|
||||
const { prefixCls } = useDesign('mini-login');
|
||||
const { notification, createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const localeStore = useLocaleStore();
|
||||
const showLocale = localeStore.getShowPicker;
|
||||
const randCodeData = reactive<any>({
|
||||
randCodeImage: '',
|
||||
requestCodeSuccess: false,
|
||||
checkKey: null,
|
||||
});
|
||||
const rememberMe = ref<string>('0');
|
||||
//手机号登录还是账号登录
|
||||
const activeIndex = ref<string>('accountLogin');
|
||||
const type = ref<string>('login');
|
||||
//账号登录表单字段
|
||||
const formData = reactive<any>({
|
||||
inputCode: '',
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
});
|
||||
//手机登录表单字段
|
||||
const phoneFormData = reactive<any>({
|
||||
mobile: '',
|
||||
smscode: '',
|
||||
});
|
||||
const loginRef = ref();
|
||||
//第三方登录弹窗
|
||||
const thirdModalRef = ref();
|
||||
//扫码登录
|
||||
const codeRef = ref();
|
||||
//是否显示获取验证码
|
||||
const showInterval = ref<boolean>(true);
|
||||
//60s
|
||||
const timeRuning = ref<number>(60);
|
||||
//定时器
|
||||
const timer = ref<any>(null);
|
||||
//忘记密码
|
||||
const forgotRef = ref();
|
||||
//注册
|
||||
const registerRef = ref();
|
||||
const loginLoading = ref<boolean>(false);
|
||||
const { getIsMobile } = useAppInject();
|
||||
|
||||
defineProps({
|
||||
sessionTimeout: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
function handleChangeCheckCode() {
|
||||
formData.inputCode = '';
|
||||
|
||||
randCodeData.checkKey = 1629428467008;
|
||||
getCodeInfo(randCodeData.checkKey).then((res) => {
|
||||
randCodeData.randCodeImage = res;
|
||||
randCodeData.requestCodeSuccess = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换登录方式
|
||||
*/
|
||||
function loginClick(type) {
|
||||
activeIndex.value = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号或者手机登录
|
||||
*/
|
||||
async function loginHandleClick() {
|
||||
if (unref(activeIndex) === 'accountLogin') {
|
||||
accountLogin();
|
||||
} else {
|
||||
//手机号登录
|
||||
phoneLogin();
|
||||
}
|
||||
}
|
||||
|
||||
async function accountLogin() {
|
||||
if (!formData.username) {
|
||||
createMessage.warn(t('sys.login.accountPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loginLoading.value = true;
|
||||
const { userInfo } = await userStore.login(
|
||||
toRaw({
|
||||
password: formData.password,
|
||||
username: formData.username,
|
||||
captcha: formData.inputCode,
|
||||
checkKey: randCodeData.checkKey,
|
||||
mode: 'none', //不要默认的错误提示
|
||||
})
|
||||
);
|
||||
if (userInfo) {
|
||||
notification.success({
|
||||
message: t('sys.login.loginSuccessTitle'),
|
||||
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.login.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
handleChangeCheckCode();
|
||||
} finally {
|
||||
loginLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
async function phoneLogin() {
|
||||
if (!phoneFormData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!phoneFormData.smscode) {
|
||||
createMessage.warn(t('sys.login.smsPlaceholder'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loginLoading.value = true;
|
||||
const { userInfo }: any = await userStore.phoneLogin({
|
||||
mobile: phoneFormData.mobile,
|
||||
captcha: phoneFormData.smscode,
|
||||
mode: 'none', //不要默认的错误提示
|
||||
});
|
||||
if (userInfo) {
|
||||
notification.success({
|
||||
message: t('sys.login.loginSuccessTitle'),
|
||||
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.login.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
} finally {
|
||||
loginLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*/
|
||||
async function getLoginCode() {
|
||||
if (!phoneFormData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
const result = await getCaptcha({ mobile: phoneFormData.mobile, smsmode: SmsEnum.FORGET_PASSWORD });
|
||||
if (result) {
|
||||
const TIME_COUNT = 60;
|
||||
if (!unref(timer)) {
|
||||
timeRuning.value = TIME_COUNT;
|
||||
showInterval.value = false;
|
||||
timer.value = setInterval(() => {
|
||||
if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
|
||||
timeRuning.value = timeRuning.value - 1;
|
||||
} else {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param type
|
||||
*/
|
||||
function onThirdLogin(type) {
|
||||
thirdModalRef.value.onThirdLogin(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码
|
||||
*/
|
||||
function forgetHandelClick() {
|
||||
type.value = 'forgot';
|
||||
setTimeout(() => {
|
||||
forgotRef.value.initForm();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回登录页面
|
||||
*/
|
||||
function goBack() {
|
||||
activeIndex.value = 'accountLogin';
|
||||
type.value = 'login';
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码/注册账号回调事件
|
||||
* @param value
|
||||
*/
|
||||
function handleSuccess(value) {
|
||||
Object.assign(formData, value);
|
||||
Object.assign(phoneFormData, { mobile: "", smscode: "" });
|
||||
type.value = 'login';
|
||||
activeIndex.value = 'accountLogin';
|
||||
handleChangeCheckCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
function registerHandleClick() {
|
||||
type.value = 'register';
|
||||
setTimeout(() => {
|
||||
registerRef.value.initForm();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
function codeHandleClick() {
|
||||
type.value = 'codeLogin';
|
||||
setTimeout(() => {
|
||||
codeRef.value.initFrom();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
//加载验证码
|
||||
handleChangeCheckCode();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
|
||||
:deep(.ant-input:focus) {
|
||||
box-shadow: none;
|
||||
}
|
||||
.aui-get-code {
|
||||
float: right;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
background: #ffffff;
|
||||
color: #1573e9;
|
||||
border-radius: 100px;
|
||||
padding: 5px 16px;
|
||||
margin: 7px;
|
||||
border: 1px solid #1573e9;
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
.aui-get-code:hover {
|
||||
color: #1573e9;
|
||||
}
|
||||
|
||||
.code-shape {
|
||||
border-color: #dadada !important;
|
||||
color: #aaa !important;
|
||||
}
|
||||
|
||||
:deep(.jeecg-dark-switch){
|
||||
position:absolute;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.aui-link-login{
|
||||
height: 42px;
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 8px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.aui-phone-logo{
|
||||
position: absolute;
|
||||
margin-left: 10px;
|
||||
width: 60px;
|
||||
top:2px;
|
||||
z-index: 4;
|
||||
}
|
||||
.top-3{
|
||||
top: 0.45rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-mini-login';
|
||||
@dark-bg: #293146;
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
background-color: @dark-bg !important;
|
||||
background-image: none;
|
||||
|
||||
&::before {
|
||||
background-image: url(/@/assets/svg/login-bg-dark.svg);
|
||||
}
|
||||
.aui-inputClear{
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
.ant-input,
|
||||
.ant-input-password {
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
|
||||
.ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
|
||||
border: 1px solid #4a5569 !important;
|
||||
}
|
||||
|
||||
&-form {
|
||||
background: @dark-bg !important;
|
||||
}
|
||||
|
||||
.app-iconify {
|
||||
color: #fff !important;
|
||||
}
|
||||
.aui-inputClear input,.aui-input-line input,.aui-choice{
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formBox{
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
.aui-third-text span{
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
.aui-form-nav .aui-flex-box{
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formButton .aui-linek-code{
|
||||
background: @dark-bg !important;
|
||||
color: white !important;
|
||||
}
|
||||
.aui-code-line{
|
||||
border-left: none !important;
|
||||
}
|
||||
.ant-checkbox-inner,.aui-success h3{
|
||||
border-color: #c9d1d9;
|
||||
}
|
||||
}
|
||||
|
||||
input.fix-auto-fill,
|
||||
.fix-auto-fill input {
|
||||
-webkit-text-fill-color: #c9d1d9 !important;
|
||||
box-shadow: inherit !important;
|
||||
}
|
||||
|
||||
&-sign-in-way {
|
||||
.anticon {
|
||||
font-size: 22px !important;
|
||||
color: #888 !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-divider-inner-text {
|
||||
font-size: 12px !important;
|
||||
color: @text-color-secondary !important;
|
||||
}
|
||||
.aui-third-login a{
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,266 @@
|
|||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="jeecgAdTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<a-form ref="formRef" :model="formData">
|
||||
<div class="aui-flex aui-form-nav aui-clear-left" style="padding-bottom: 21px">
|
||||
<div class="aui-flex-box activeNav on">{{t('sys.login.signUpFormTitle')}}</div>
|
||||
</div>
|
||||
<div class="aui-form-box">
|
||||
<div class="aui-account aui-account-line">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:user-outlined"/>
|
||||
<a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.userName')" v-model:value="formData.username" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:mobile-outlined"/>
|
||||
<a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.mobile')" v-model:value="formData.mobile" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:mail-outlined"/>
|
||||
<a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.smsCode')" v-model:value="formData.smscode" />
|
||||
<div v-if="showInterval" class="aui-code-line" @click="getLoginCode">{{t('component.countdown.normalText')}}</div>
|
||||
<div v-else class="aui-code-line">{{t('component.countdown.sendText',[unref(timeRuning)])}}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:lock-outlined"/>
|
||||
<a-input class="fix-auto-fill" :type="pwdIndex==='close'?'password':'text'" :placeholder="t('sys.login.password')" v-model:value="formData.password" />
|
||||
<div class="aui-eye">
|
||||
<img :src="eyeKImg" alt="开启" v-if="pwdIndex==='open'" @click="pwdClick('close')" />
|
||||
<img :src="eyeGImg" alt="关闭" v-else-if="pwdIndex==='close'" @click="pwdClick('open')" />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:lock-outlined"/>
|
||||
<a-input class="fix-auto-fill" :type="confirmPwdIndex==='close'?'password':'text'" :placeholder="t('sys.login.confirmPassword')" v-model:value="formData.confirmPassword" />
|
||||
<div class="aui-eye">
|
||||
<img :src="eyeKImg" alt="开启" v-if="confirmPwdIndex==='open'" @click="confirmPwdClick('close')" />
|
||||
<img :src="eyeGImg" alt="关闭" v-else-if="confirmPwdIndex==='close'" @click="confirmPwdClick('open')" />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item name="policy">
|
||||
<div class="aui-flex">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-choice">
|
||||
<a-checkbox v-model:checked="formData.policy" />
|
||||
<span style="color: #1b90ff;margin-left: 4px">{{ t('sys.login.policy') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<div class="aui-flex">
|
||||
<a class="aui-link-login aui-flex-box" @click="registerHandleClick"> {{ t('sys.login.registerButton') }}</a>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="goBackHandleClick">{{ t('sys.login.backSignIn') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="mini-register">
|
||||
import { ref, reactive, unref, toRaw } from 'vue';
|
||||
import { getCaptcha, register } from '/@/api/sys/user';
|
||||
import { SmsEnum } from '/@/views/sys/login/useLogin';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import jeecgAdTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import eyeKImg from '/@/assets/loginmini/icon/icon-eye-k.png';
|
||||
import eyeGImg from '/@/assets/loginmini/icon/icon-eye-g.png';
|
||||
import { useI18n } from "/@/hooks/web/useI18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notification, createErrorModal, createMessage } = useMessage();
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
const formRef = ref();
|
||||
const formData = reactive<any>({
|
||||
username: '',
|
||||
mobile: '',
|
||||
smscode: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
policy: false,
|
||||
});
|
||||
//是否显示获取验证码
|
||||
const showInterval = ref<boolean>(true);
|
||||
//60s
|
||||
const timeRuning = ref<number>(60);
|
||||
//定时器
|
||||
const timer = ref<any>(null);
|
||||
//密码眼睛打开关闭
|
||||
const pwdIndex = ref<string>('close');
|
||||
//确认密码眼睛打开关闭
|
||||
const confirmPwdIndex = ref<string>('close');
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBackHandleClick() {
|
||||
emit('go-back');
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*/
|
||||
async function getLoginCode() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
const result = await getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.REGISTER });
|
||||
if (result) {
|
||||
const TIME_COUNT = 60;
|
||||
if (!unref(timer)) {
|
||||
timeRuning.value = TIME_COUNT;
|
||||
showInterval.value = false;
|
||||
timer.value = setInterval(() => {
|
||||
if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
|
||||
timeRuning.value = timeRuning.value - 1;
|
||||
} else {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerHandleClick() {
|
||||
if (!formData.username) {
|
||||
createMessage.warn(t('sys.login.accountPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.smscode) {
|
||||
createMessage.warn(t('sys.login.smsPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.confirmPassword'));
|
||||
return;
|
||||
}
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.diffPwd'));
|
||||
return;
|
||||
}
|
||||
if(!formData.policy){
|
||||
createMessage.warn(t('sys.login.policyPlaceholder'));
|
||||
return;
|
||||
}
|
||||
registerAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册账号
|
||||
*/
|
||||
async function registerAccount() {
|
||||
try {
|
||||
const resultInfo = await register(
|
||||
toRaw({
|
||||
username: formData.username,
|
||||
password: formData.password,
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
})
|
||||
);
|
||||
if (resultInfo && resultInfo.data.success) {
|
||||
notification.success({
|
||||
description: resultInfo.data.message || t('sys.api.registerMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
emit('success', { username: formData.username, password: formData.password });
|
||||
initForm();
|
||||
} else {
|
||||
notification.warning({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: resultInfo.data.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initForm() {
|
||||
Object.assign(formData,{username:'',mobile: '', smscode: '', password: '', confirmPassword: '', policy: false})
|
||||
if(!unref(timer)){
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码打开或关闭
|
||||
* @param value
|
||||
*/
|
||||
function pwdClick(value) {
|
||||
pwdIndex.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认密码打开或关闭
|
||||
* @param value
|
||||
*/
|
||||
function confirmPwdClick(value) {
|
||||
confirmPwdIndex.value = value;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initForm
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
.aui-input-line .aui-icon{
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
</style>
|