From 01d423865ae5eddf91f35c8526f5683c3b430870 Mon Sep 17 00:00:00 2001 From: Crunchy <3114200645@qq.com> Date: 星期一, 13 一月 2025 09:30:06 +0800 Subject: [PATCH] 密码强校验添加 --- src/views/admin/user/index.vue | 768 +++++++++++---------------- src/const/crud/admin/user.js | 320 ++++++----- src/views/admin/user/info.vue | 503 ++++++++++-------- 3 files changed, 774 insertions(+), 817 deletions(-) diff --git a/src/const/crud/admin/user.js b/src/const/crud/admin/user.js index 63011da..fbc3db5 100644 --- a/src/const/crud/admin/user.js +++ b/src/const/crud/admin/user.js @@ -14,148 +14,190 @@ * this software without specific prior written permission. * Author: ztt */ -import {getDetails} from '@/api/admin/user' - +import { getDetails } from '@/api/admin/user' var validateUsername = (rule, value, callback) => { - getDetails(value).then(response => { - if (window.boxType === 'edit') callback() - const result = response.data.data - if (result !== null) { - callback(new Error('鐢ㄦ埛鍚嶅凡缁忓瓨鍦�')) - } else { - callback() - } - }) + getDetails(value).then((response) => { + if (window.boxType === 'edit') callback() + const result = response.data.data + if (result !== null) { + callback(new Error('鐢ㄦ埛鍚嶅凡缁忓瓨鍦�')) + } else { + callback() + } + }) } export const tableOption = { - border: true, - index: true, - indexLabel: '搴忓彿', - stripe: true, - menuAlign: 'center', - searchMenuSpan:6, - editBtn: false, - delBtn: false, - align: 'center', - addBtn: false, - column: [{ - fixed: true, - label: 'id', - prop: 'userId', - span: 24, - hide: true, - editDisabled: true, - addDisplay: false - }, { - fixed: true, - label: '鐢ㄦ埛鍚�', - prop: 'username', - editDisabled: true, - slot: true, - search: true, - span: 24, - rules: [{ - required: true, - message: '璇疯緭鍏ョ敤鎴峰悕' - }, - { - min: 3, - max: 20, - message: '闀垮害鍦� 3 鍒� 20 涓瓧绗�', - trigger: 'blur' - }, - {validator: validateUsername, trigger: 'blur'} + border: true, + index: true, + indexLabel: '搴忓彿', + stripe: true, + menuAlign: 'center', + searchMenuSpan: 6, + editBtn: false, + delBtn: false, + align: 'center', + addBtn: false, + column: [ + { + fixed: true, + label: 'id', + prop: 'userId', + span: 24, + hide: true, + editDisabled: true, + addDisplay: false + }, + { + fixed: true, + label: '鐢ㄦ埛鍚�', + prop: 'username', + editDisabled: true, + slot: true, + search: true, + span: 24, + rules: [ + { + required: true, + message: '璇疯緭鍏ョ敤鎴峰悕' + }, + { + min: 3, + max: 20, + message: '闀垮害鍦� 3 鍒� 20 涓瓧绗�', + trigger: 'blur' + }, + { validator: validateUsername, trigger: 'blur' } + ] + }, + { + label: '瀵嗙爜', + prop: 'password', + type: 'password', + hide: true, + slot: true, + formslot: true, + span: 24, + rules: [ + { + required: true, + message: '璇疯緭鍏ュ瘑鐮�', + trigger: 'blur' + }, + { + validator: (rule, value, callback) => { + let strength = 0 + + if (value.length >= 8) strength++ + if (/\d/.test(value)) strength++ + if (/[a-z]/.test(value)) strength++ + if (/[A-Z]/.test(value)) strength++ + if (/[!@#$%^&*]/.test(value)) strength++ + + if (strength < 4) { + callback(new Error('瀵嗙爜寮哄害涓嶅锛岃纭繚瀵嗙爜鍖呭惈锛氬ぇ灏忓啓瀛楁瘝銆佹暟瀛楀拰鐗规畩瀛楃锛屼笖闀垮害涓嶅皯浜�8浣�')) + } else { + callback() + } + }, + trigger: 'blur' + } + ] + }, + { + label: '鎵�灞炲垎缁�', + prop: 'deptId', + formslot: true, + slot: true, + span: 24, + hide: true, + dataType: 'number', + rules: [ + { + required: true, + message: '璇烽�夋嫨閮ㄩ棬', + trigger: 'change' + } + ] + }, + { + label: '鍛樺伐', + prop: 'staffInfo', + formslot: true, + span: 24, + rules: [ + { + required: true, + message: '璇烽�夋嫨鍛樺伐', + trigger: 'change' + } + ] + }, + { + label: '鎵嬫満鍙�', + prop: 'phone', + type: 'tel', + value: '', + span: 24, + rules: [ + { + min: 11, + max: 11, + message: '闀垮害鍦� 11 涓瓧绗�', + trigger: 'blur' + } + ] + }, + { + label: '瑙掕壊', + prop: 'role', + formslot: true, + slot: true, + overHidden: true, + span: 24, + rules: [ + { + required: true, + message: '璇烽�夋嫨瑙掕壊', + trigger: 'blur' + } + ] + }, + { + label: '鐘舵��', + prop: 'lockFlag', + type: 'radio', + slot: true, + border: true, + span: 24, + rules: [ + { + required: true, + message: '璇烽�夋嫨鐘舵��', + trigger: 'blur' + } + ], + dicData: [ + { + label: '鏈夋晥', + value: '0' + }, + { + label: '閿佸畾', + value: '9' + } + ] + }, + { + width: 180, + label: '鍒涘缓鏃堕棿', + prop: 'createTime', + type: 'datetime', + format: 'yyyy-MM-dd HH:mm', + valueFormat: 'yyyy-MM-dd HH:mm:ss', + editDisabled: true, + addDisplay: false, + span: 24 + } ] - }, { - label: '瀵嗙爜', - prop: 'password', - type: 'password', - value: '', - hide: true, - span: 24, - rules: [{ - min: 6, - max: 20, - required: true, - message: '闀垮害鍦� 6 鍒� 20 涓瓧绗�', - trigger: 'blur' - }] - }, { - label: '鎵�灞炲垎缁�', - prop: 'deptId', - formslot: true, - slot: true, - span: 24, - hide: true, - dataType:"number", - rules: [{ - required: true, - message: '璇烽�夋嫨閮ㄩ棬', - trigger: 'change' - }] - }, { - label: '鍛樺伐', - prop: 'staffInfo', - formslot: true, - span: 24, - rules: [{ - required: true, - message: '璇烽�夋嫨鍛樺伐', - trigger: 'change' - }] - }, { - label: '鎵嬫満鍙�', - prop: 'phone', - type: 'tel', - value: '', - span: 24, - rules: [{ - min: 11, - max: 11, - message: '闀垮害鍦� 11 涓瓧绗�', - trigger: 'blur' - }] - }, { - label: '瑙掕壊', - prop: 'role', - formslot: true, - slot: true, - overHidden: true, - span: 24, - rules: [{ - required: true, - message: '璇烽�夋嫨瑙掕壊', - trigger: 'change' - }] - }, { - label: '鐘舵��', - prop: 'lockFlag', - type: 'radio', - slot: true, - border:true, - span: 24, - rules: [{ - required: true, - message: '璇烽�夋嫨鐘舵��', - trigger: 'change' - }], - dicData: [{ - label: '鏈夋晥', - value: '0' - }, { - label: '閿佸畾', - value: '9' - }] - }, { - width: 180, - label: '鍒涘缓鏃堕棿', - prop: 'createTime', - type: 'datetime', - format: 'yyyy-MM-dd HH:mm', - valueFormat: 'yyyy-MM-dd HH:mm:ss', - editDisabled: true, - addDisplay: false, - span: 24 - }] } diff --git a/src/views/admin/user/index.vue b/src/views/admin/user/index.vue index d50b400..f99ad30 100644 --- a/src/views/admin/user/index.vue +++ b/src/views/admin/user/index.vue @@ -16,186 +16,69 @@ --> <template> - <div class="user"> - <basic-container> - <el-row :span="24"> - <el-col :xs="24" :sm="24" :md="5" class="user__tree"> - <avue-tree - :option="treeOption" - :data="treeData" - @node-click="nodeClick" - > + <div class="user"> + <basic-container> + <el-row :span="24"> + <el-col :xs="24" :sm="24" :md="5" class="user__tree"> + <avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick"> <span class="el-tree-node__label" slot-scope="{ node, data }"> - <el-tooltip - class="item" - effect="dark" - content="鏃犳暟鎹潈闄�" - placement="right-start" - v-if="data.isLock" - > + <el-tooltip class="item" effect="dark" content="鏃犳暟鎹潈闄�" placement="right-start" v-if="data.isLock"> <span>{{ node.label }} <i class="el-icon-lock"></i></span> </el-tooltip> <span v-if="!data.isLock">{{ node.label }}</span> </span> - </avue-tree> - </el-col> - <el-col :xs="24" :sm="24" :md="19" class="user__main"> - <avue-crud - ref="crud" - :option="option" - v-model="form" - :page="page" - :table-loading="listLoading" - :before-open="handleOpenBefore" - :data="list" - @on-load="getList" - @search-change="searchChange" - @refresh-change="refreshChange" - @size-change="sizeChange" - @current-change="currentChange" - @row-update="update" - @row-save="create" - > - <template slot="menuLeft"> - <el-button - v-if="sys_user_add" - class="filter-item" - type="primary" - size="small" - icon="el-icon-edit" - @click="$refs.crud.rowAdd()" - >娣诲姞 - </el-button> - <!-- <el-button - v-if="sys_user_add" - class="filter-item" - type="primary" - size="small" - icon="el-icon-upload" - @click="importDialogVisible=true" - >瀵煎叆 - </el-button> --> - </template> - <template slot="username" slot-scope="scope"> - <span>{{ scope.row.username }}</span> - </template> - <template slot="role" slot-scope="scope"> + </avue-tree> + </el-col> + <el-col :xs="24" :sm="24" :md="19" class="user__main"> + <avue-crud ref="crud" :option="option" v-model="form" :page="page" :table-loading="listLoading" :before-open="handleOpenBefore" :data="list" @on-load="getList" @search-change="searchChange" @refresh-change="refreshChange" @size-change="sizeChange" @current-change="currentChange" @row-update="update" @row-save="create"> + <template slot="menuLeft"> + <el-button v-if="sys_user_add" class="filter-item" type="primary" size="small" icon="el-icon-edit" @click="$refs.crud.rowAdd()">娣诲姞 </el-button> + </template> + <template slot="username" slot-scope="scope"> + <span>{{ scope.row.username }}</span> + </template> + <template slot="role" slot-scope="scope"> <span v-for="(role, index) in scope.row.roleList" :key="index"> <el-tag>{{ role.roleName }} </el-tag> </span> - </template> - <template slot="deptId" slot-scope="scope"> - {{ scope.row.deptName }} - </template> - <template slot="lockFlag" slot-scope="scope"> - <el-tag>{{ scope.label }}</el-tag> - </template> - <template slot="menu" slot-scope="scope"> - <el-button - v-if="sys_user_edit" - type="text" - size="small" - icon="el-icon-edit" - @click="handleUpdate(scope.row, scope.index)" - >缂栬緫 - </el-button> - <el-button - v-if="sys_user_del" - type="text" - size="small" - icon="el-icon-delete" - @click="deletes(scope.row, scope.index)" - >鍒犻櫎 - </el-button> - <el-button - v-if="sys_user_lock" - type="text" - size="small" - icon="el-icon-unlock" - @click="unlock(scope.row, scope.index)" - >瑙i攣 - </el-button> - </template> - <template slot="deptIdForm" slot-scope="scope"> - <avue-input-tree - v-model="form.deptId" - :node-click="getNodeData" - :dic="treeDeptData" - :props="defaultProps" - placeholder="璇烽�夋嫨鎵�灞炲垎缁�" - /> - </template> - <template slot="staffInfoForm" slot-scope="scope"> - <el-input - @focus="showStaff = true" - v-model="form.staffInfo" - readonly - > - <i - class="el-icon-arrow-down el-input__icon" - slot="suffix" - @click="showStaff = true" - ></i> - </el-input> - </template> - <template slot="roleForm" slot-scope="scope"> - <avue-select - v-model="role" - :dic="rolesOptions" - :props="roleProps" - multiple - placeholder="璇烽�夋嫨瑙掕壊" - /> - </template> - </avue-crud> - </el-col> - </el-row> - </basic-container> + </template> + <template slot="deptId" slot-scope="scope"> + {{ scope.row.deptName }} + </template> + <template slot="lockFlag" slot-scope="scope"> + <el-tag>{{ scope.label }}</el-tag> + </template> + <template slot="menu" slot-scope="scope"> + <el-button v-if="sys_user_edit" type="text" size="small" icon="el-icon-edit" @click="handleUpdate(scope.row, scope.index)">缂栬緫 </el-button> + <el-button v-if="sys_user_del" type="text" size="small" icon="el-icon-delete" @click="deletes(scope.row, scope.index)">鍒犻櫎 </el-button> + <el-button v-if="sys_user_lock" type="text" size="small" icon="el-icon-unlock" @click="unlock(scope.row, scope.index)">瑙i攣 </el-button> + </template> + <template slot="deptIdForm" slot-scope="scope"> + <avue-input-tree v-model="form.deptId" :node-click="getNodeData" :dic="treeDeptData" :props="defaultProps" placeholder="璇烽�夋嫨鎵�灞炲垎缁�" /> + </template> + <template slot="staffInfoForm" slot-scope="scope"> + <el-input @focus="showStaff = true" v-model="form.staffInfo" readonly> + <i class="el-icon-arrow-down el-input__icon" slot="suffix" @click="showStaff = true"></i> + </el-input> + </template> + <template slot="roleForm" slot-scope="scope"> + <avue-select v-model="role" :dic="rolesOptions" :props="roleProps" multiple placeholder="璇烽�夋嫨瑙掕壊" /> + </template> + <template slot="passwordForm" slot-scope="scope"> + <div class="password-input-container"> + <el-input v-model="form.password" type="password" @input="checkPasswordStrength" placeholder="璇疯緭鍏ュ瘑鐮�" show-password style="width: 100%"> </el-input> + <div v-if="form.password" class="password-strength-indicator"> + 瀵嗙爜寮哄害: <span :class="passwordLevelClass">{{ passwordLevel }}</span> + </div> + </div> + </template> + </avue-crud> + </el-col> + </el-row> + </basic-container> - <staffDialog - :currshowlist.sync="showStaff" - @listenToStaffEvent="selectStaff" - /> - <el-dialog title="瀵煎叆" :visible.sync="importDialogVisible" width="30%"> - <span> - <div> - <div> - <el-upload - style="margin-left:8px;display: inline;" - class="upload-demo" - drag - :headers="headers" - :action="uploadInfo.url" - :beforeUpload="beforeAvatarUpload" - :limit="1" - :show-file-list="false" - :file-list="fileList" - :on-success="fileSuccessUploadScan" - :on-error="handleError" - accept=".xlsx,.xls,.csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" - :auto-upload="true" - ref="uploadScan" - multiple - > - <i class="el-icon-upload"></i> - <div class="el-upload__text"><em>鐐瑰嚮瀵煎叆鏁版嵁</em></div> - <div class="el-upload__tip" slot="tip" > - 鍙兘涓婁紶xlsx/xls鏂囦欢锛屼笖涓嶈秴杩�10M<el-button - type="text" - style="font-size:12px;" - @click="downDataTemplate(uploadInfo.fileName)" - v-if="uploadInfo.Download" - >涓嬭浇妯℃澘</el-button - > - </div> - </el-upload> - </div> - <div></div> - </div> - </span> - <span slot="footer" class="dialog-footer"> </span> - </el-dialog> - </div> + <staffDialog :currshowlist.sync="showStaff" @listenToStaffEvent="selectStaff" /> + </div> </template> <script> import staffDialog from '@/views/common/staff.vue' @@ -206,298 +89,285 @@ import { mapGetters } from 'vuex' import Template from '../../quality/parts/template' import { getObj } from '@/api/basic/staff' -import { getStore } from '@/util/store.js' -import { uploadTemplate } from '@/api/basic/template' + export default { - name: 'SysUser', - components: { Template, staffDialog }, - data() { - return { - fileList: [], // 涓婁紶鏂囦欢鍒楄〃 - // 涓婁紶澶翠俊鎭� - headers: { - Authorization: 'Bearer ' + getStore({ name: 'access_token' }) - }, - uploadInfo: { - // 鏄惁灞曠ず涓婁紶EXCEL浠ュ強瀵瑰簲鐨剈rl - isShow: true, - url: '/mes/user/upload', - download: true, - fileName: '鐢ㄦ埛妯℃澘' - }, - importDialogVisible: false, - showStaff: false, - searchForm: {}, - treeOption: { - nodeKey: 'id', - addBtn: false, - menu: false, - props: { - label: 'name', - value: 'id' + name: 'SysUser', + components: { Template, staffDialog }, + data() { + return { + showStaff: false, + searchForm: {}, + treeOption: { + nodeKey: 'id', + addBtn: false, + menu: false, + props: { + label: 'name', + value: 'id' + } + }, + treeData: [], + option: tableOption, + treeDeptData: [], + checkedKeys: [], + roleProps: { + label: 'roleName', + value: 'roleId' + }, + defaultProps: { + label: 'name', + value: 'id' + }, + page: { + total: 0, // 鎬婚〉鏁� + currentPage: 1, // 褰撳墠椤垫暟 + pageSize: 20, // 姣忛〉鏄剧ず澶氬皯鏉�, + isAsc: false // 鏄惁鍊掑簭 + }, + list: [], + listLoading: true, + role: [], + form: {}, + rolesOptions: [], + passwordLevel: '', // 瀵嗙爜寮哄害绾у埆 + passwordLevelClass: '' // 瀵嗙爜寮哄害鏍峰紡绫� } - }, - treeData: [], - option: tableOption, - treeDeptData: [], - checkedKeys: [], - roleProps: { - label: 'roleName', - value: 'roleId' - }, - defaultProps: { - label: 'name', - value: 'id' - }, - page: { - total: 0, // 鎬婚〉鏁� - currentPage: 1, // 褰撳墠椤垫暟 - pageSize: 20, // 姣忛〉鏄剧ず澶氬皯鏉�, - isAsc: false // 鏄惁鍊掑簭 - }, - list: [], - listLoading: true, - role: [], - form: {}, - rolesOptions: [] - } - }, - computed: { - ...mapGetters(['permissions']) - }, - watch: { - role() { - this.form.role = this.role - } - }, - created() { - this.sys_user_add = this.permissions.sys_user_add - this.sys_user_edit = this.permissions.sys_user_edit - this.sys_user_del = this.permissions.sys_user_del - this.sys_user_lock = this.permissions.sys_user_lock - this.init() - }, - methods: { - // 闄愬埗鏂囦欢涓婁紶澶у皬锛岀洰鍓嶉檺鍒朵负10M锛堝彟鍙互鍔犵被鍨嬮檺鍒讹級 - beforeAvatarUpload(file) { - const fileName = file.name - const fileType = fileName.substring(fileName.lastIndexOf('.') + 1) - const isLt10M = file.size / 1024 / 1024 < 10 - if (fileType !== 'xlsx' && fileType !== 'xls') { - this.$message.error('鏂囦欢鏍煎紡鍙兘涓簒lsx鎴杧ls,璇峰垹闄ゅ悗閲嶆柊涓婁紶') - } - if (!isLt10M) { - this.$message({ - message: '鏂囦欢澶у皬', - type: 'warning' - }) - } - return isLt10M }, - // 鏂囦欢涓婁紶鎴愬姛鍥炶皟浜嬩欢 - fileSuccessUploadScan(response, file, fileList) { - if (response.code != '0') { - this.$message.warning(response.msg) - } else { - if(response.data!=""&&response.data!=[]&&response.data!=null){ - this.$message({ - message: response.data, - type: 'success', - dangerouslyUseHTMLString: true, - }) - }else{ - this.$message({ - message: '涓婁紶鎴愬姛', - type: 'success' - }) - } - this.importDialogVisible=false - this.fileList=[] + computed: { + ...mapGetters(['permissions']) + }, + watch: { + role() { + this.form.role = this.role + } + }, + created() { + this.sys_user_add = this.permissions.sys_user_add + this.sys_user_edit = this.permissions.sys_user_edit + this.sys_user_del = this.permissions.sys_user_del + this.sys_user_lock = this.permissions.sys_user_lock this.init() - // this.getDataList() - } - this.$refs.uploadScan.clearFiles() }, - // 涓婁紶澶辫触 - handleError(err, file, fileList) { - const error = JSON.parse(err.message) - if (error.msg) { - this.$message.error(error.msg) - } else { - this.$message.error('涓婁紶澶辫触') - } - }, - // 涓嬭浇鏁版嵁妯℃澘 - downDataTemplate() { - uploadTemplate("user").then((response) => { - const blob = new Blob([response.data], { - type: 'application/force-download' - }) - let fileName="妯℃澘鏂囦欢"; - if(this.uploadInfo.fileName!=undefined&&this.uploadInfo.fileName!=''&&this.uploadInfo.fileName!=null){ - fileName=this.uploadInfo.fileName - } - const filename = decodeURI(fileName+'.xlsx') - // 鍒涘缓涓�涓秴閾炬帴锛屽皢鏂囦欢娴佽祴杩涘幓锛岀劧鍚庡疄鐜拌繖涓秴閾炬帴鐨勫崟鍑讳簨浠� - const elink = document.createElement('a') - elink.download = filename - elink.style.display = 'none' - elink.href = URL.createObjectURL(blob) - document.body.appendChild(elink) - elink.click() - URL.revokeObjectURL(elink.href) // 閲婃斁URL 瀵硅薄 - document.body.removeChild(elink) - }) - }, - selectStaff(staff) { - this.form.staffInfo = staff.staffName + '-' + staff.staffNo - this.form.phone = staff.phone - this.form.staffId = staff.id - }, - init() { - fetchTree().then((response) => { - this.treeData = response.data.data - }) - }, - nodeClick(data) { - this.page.page = 1 - this.getList(this.page, { deptId: data.id }) - }, - getList(page, params) { - this.listLoading = true - fetchList( - Object.assign( - { - current: page.currentPage, - size: page.pageSize - }, - params, - this.searchForm - ) - ).then((response) => { - this.list = response.data.data.records - this.page.total = response.data.data.total - this.listLoading = false - }) - }, - getNodeData() { - deptRoleList().then((response) => { - this.rolesOptions = response.data.data - }) - }, - searchChange(param, done) { - this.searchForm = param - this.page.currentPage = 1 - this.getList(this.page, param) - done() - }, - sizeChange(pageSize) { - this.page.pageSize = pageSize - }, - currentChange(current) { - this.page.currentPage = current - }, - refreshChange() { - this.getList(this.page) - }, - handleOpenBefore(show, type) { - window.boxType = type - // 鏌ヨ閮ㄩ棬鏍� - fetchTree().then((response) => { - this.treeDeptData = response.data.data - }) - // 鏌ヨ瑙掕壊鍒楄〃 - deptRoleList().then((response) => { - this.rolesOptions = response.data.data - }) + methods: { + selectStaff(staff) { + this.form.staffInfo = staff.staffName + '-' + staff.staffNo + this.form.phone = staff.phone + this.form.staffId = staff.id + }, + init() { + fetchTree().then((response) => { + this.treeData = response.data.data + }) + }, + nodeClick(data) { + this.page.page = 1 + this.getList(this.page, { deptId: data.id }) + }, + getList(page, params) { + this.listLoading = true + fetchList( + Object.assign( + { + current: page.currentPage, + size: page.pageSize + }, + params, + this.searchForm + ) + ).then((response) => { + this.list = response.data.data.records + this.page.total = response.data.data.total + this.listLoading = false + }) + }, + getNodeData() { + deptRoleList().then((response) => { + this.rolesOptions = response.data.data + }) + }, + searchChange(param, done) { + this.searchForm = param + this.page.currentPage = 1 + this.getList(this.page, param) + done() + }, + sizeChange(pageSize) { + this.page.pageSize = pageSize + }, + currentChange(current) { + this.page.currentPage = current + }, + refreshChange() { + this.getList(this.page) + }, + handleOpenBefore(show, type) { + window.boxType = type + // 鏌ヨ閮ㄩ棬鏍� + fetchTree().then((response) => { + this.treeDeptData = response.data.data + }) + // 鏌ヨ瑙掕壊鍒楄〃 + deptRoleList().then((response) => { + this.rolesOptions = response.data.data + }) - if (['edit', 'views'].includes(type)) { - this.role = [] - for (let i = 0; i < this.form.roleList.length; i++) { - this.role[i] = this.form.roleList[i].roleId - } - } else if (type === 'add') { - this.role = [] - } - show() - }, - handleUpdate(row, index) { - this.$refs.crud.rowEdit(row, index) - this.form.password = undefined - }, + if (['edit', 'views'].includes(type)) { + this.role = [] + for (let i = 0; i < this.form.roleList.length; i++) { + this.role[i] = this.form.roleList[i].roleId + } + } else if (type === 'add') { + this.role = [] + } + show() + }, + handleUpdate(row, index) { + this.$refs.crud.rowEdit(row, index) + this.form.password = undefined + }, - create(row, done, loading) { - if (this.form.phone.indexOf('*') > 0) { - this.form.phone = undefined - } - addObj(this.form) - .then(() => { - this.getList(this.page) - done() - this.$notify.success('鍒涘缓鎴愬姛') - }) - .catch(() => { - loading() - }) - }, - update(row, index, done, loading) { - if (this.form.phone && this.form.phone.indexOf('*') > 0) { - this.form.phone = undefined - } - putObj(this.form) - .then(() => { - this.getList(this.page) - done() - this.$notify.success('淇敼鎴愬姛') - }) - .catch(() => { - loading() - }) - }, - deletes(row, index) { - this.$confirm( - '姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ョ敤鎴�(鐢ㄦ埛鍚�:' + row.username + '), 鏄惁缁х画?', - '鎻愮ず', - { - confirmButtonText: '纭畾', - cancelButtonText: '鍙栨秷', - closeOnClickModal: false, - type: 'warning' + create(row, done, loading) { + // 妫�鏌ュ瘑鐮佸己搴� + if (this.passwordLevel !== '寮�') { + this.$message.warning('瀵嗙爜寮哄害涓嶅锛岃纭繚瀵嗙爜鍖呭惈锛氬ぇ灏忓啓瀛楁瘝銆佹暟瀛楀拰鐗规畩瀛楃锛屼笖闀垮害涓嶅皯浜�8浣�') + loading() + return + } + + if (this.form.phone.indexOf('*') > 0) { + this.form.phone = undefined + } + + addObj(this.form) + .then(() => { + this.getList(this.page) + done() + this.$notify.success('鍒涘缓鎴愬姛') + }) + .catch(() => { + loading() + }) + }, + update(row, index, done, loading) { + // 濡傛灉淇敼浜嗗瘑鐮侊紝涔熻妫�鏌ュ瘑鐮佸己搴� + if (this.form.password && this.passwordLevel !== '寮�') { + this.$message.warning('瀵嗙爜寮哄害涓嶅锛岃纭繚瀵嗙爜鍖呭惈锛氬ぇ灏忓啓瀛楁瘝銆佹暟瀛楀拰鐗规畩瀛楃锛屼笖闀垮害涓嶅皯浜�8浣�') + loading() + return + } + + if (this.form.phone && this.form.phone.indexOf('*') > 0) { + this.form.phone = undefined + } + + putObj(this.form) + .then(() => { + this.getList(this.page) + done() + this.$notify.success('淇敼鎴愬姛') + }) + .catch(() => { + loading() + }) + }, + deletes(row, index) { + this.$confirm('姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ョ敤鎴�(鐢ㄦ埛鍚�:' + row.username + '), 鏄惁缁х画?', '鎻愮ず', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + closeOnClickModal: false, + type: 'warning' + }).then(() => { + delObj(row.userId) + .then(() => { + this.list.splice(index, 1) + this.$notify.success('鍒犻櫎鎴愬姛') + }) + .catch(() => { + this.$notify.error('鍒犻櫎澶辫触') + }) + }) + }, + unlock(row, index) { + unlock({ id: row.userId }).then((repsonse) => {}) + }, + // 妫�鏌ュ瘑鐮佸己搴� + checkPasswordStrength(password) { + let strength = 0 + + // 妫�鏌ラ暱搴� + if (password.length >= 8) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚暟瀛� + if (/\d/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚皬鍐欏瓧姣� + if (/[a-z]/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚ぇ鍐欏瓧姣� + if (/[A-Z]/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚壒娈婂瓧绗� + if (/[!@#$%^&*]/.test(password)) strength++ + + switch (strength) { + case 0: + case 1: + this.passwordLevel = '寮�' + this.passwordLevelClass = 'password-weak' + break + case 2: + case 3: + this.passwordLevel = '涓�' + this.passwordLevelClass = 'password-medium' + break + case 4: + case 5: + this.passwordLevel = '寮�' + this.passwordLevelClass = 'password-strong' + break + } } - ).then(() => { - delObj(row.userId) - .then(() => { - this.list.splice(index, 1) - this.$notify.success('鍒犻櫎鎴愬姛') - }) - .catch(() => { - this.$notify.error('鍒犻櫎澶辫触') - }) - }) - }, - unlock(row, index) { - unlock({ id: row.userId }).then((repsonse) => { - if(repsonse.data.data){ - this.$message.success("璇ョ敤鎴峰凡瑙i攣") - } - }) } - } } </script> <style lang="scss"> .user { - height: 100%; + height: 100%; - &__tree { - padding-top: 3px; - padding-right: 20px; - } - - &__main { - .el-card__body { - padding-top: 0; + &__tree { + padding-top: 3px; + padding-right: 20px; } - } + + &__main { + .el-card__body { + padding-top: 0; + } + } +} + +.password-input-container { + width: 100%; + .el-input { + width: 100%; + } +} + +.password-strength-indicator { + margin-top: 5px; + font-size: 12px; + color: #606266; +} + +.password-weak { + color: #f56c6c; +} + +.password-medium { + color: #e6a23c; +} + +.password-strong { + color: #67c23a; } </style> diff --git a/src/views/admin/user/info.vue b/src/views/admin/user/info.vue index b7fb0b1..f220390 100644 --- a/src/views/admin/user/info.vue +++ b/src/views/admin/user/info.vue @@ -16,269 +16,314 @@ --> <template> - <div class="app-container calendar-list-container"> - <basic-container> - <template> - <el-tabs v-model="switchStatus" @tab-click="switchTab" style="padding-left:20px"> - <el-tab-pane label="淇℃伅绠$悊" name="userManager"/> - <el-tab-pane label="瀵嗙爜绠$悊" name="passwordManager"/> - </el-tabs> - </template> - <el-row> - <el-col :span="12"> - <div class="grid-content bg-purple"> - <el-form - v-if="switchStatus==='userManager'" - ref="ruleForm2" - :model="ruleForm2" - :rules="rules2" - label-width="100px" - class="demo-ruleForm"> - <el-form-item - label="鐢ㄦ埛鍚�" - prop="username"> - <el-input - v-model="ruleForm2.username" - type="text" - disabled/> - </el-form-item> - <el-form-item label="鎵嬫満鍙�" prop="phone"> - <el-input v-model="ruleForm2.phone" placeholder="楠岃瘉鐮佺櫥褰曚娇鐢�"/> - </el-form-item> - <el-form-item label="澶村儚"> - <el-upload - :headers="headers" - :show-file-list="false" - :on-success="handleAvatarSuccess" - class="avatar-uploader" - action="/admin/sys-file/upload"> - <img v-if="ruleForm2.avatar" id="avatar" :src="avatarUrl" class="avatar"> - <i v-else class="el-icon-plus avatar-uploader-icon"/> - </el-upload> - </el-form-item> - <!-- <el-form-item - label="绀句氦鐧诲綍" - prop="social"> - <a - href="#" - style="color: blue" - @click="handleClick('wechat')">缁戝畾寰俊</a>锝� - <a - href="#" - style="color: blue" - @click="handleClick('gitee')">缁戝畾鐮佷簯</a> | - <a - href="#" - style="color: blue" - @click="handleClick('osc')">寮�婧愪腑鍥�</a> - </el-form-item> --> - <el-form-item> - <el-button - type="primary" - @click="submitForm('ruleForm2')">鎻愪氦 - </el-button> - <el-button @click="resetForm('ruleForm2')">閲嶇疆</el-button> - </el-form-item> - </el-form> - <el-form - v-if="switchStatus==='passwordManager'" - ref="ruleForm2" - :model="ruleForm2" - :rules="rules2" - label-width="100px" - class="demo-ruleForm"> - <el-form-item - label="鍘熷瘑鐮�" - prop="password"> - <el-input - v-model="ruleForm2.password" - type="password" - auto-complete="off"/> - </el-form-item> - <el-form-item - label="瀵嗙爜" - prop="newpassword1"> - <el-input - v-model="ruleForm2.newpassword1" - type="password" - auto-complete="off"/> - </el-form-item> - <el-form-item - label="纭瀵嗙爜" - prop="newpassword2"> - <el-input - v-model="ruleForm2.newpassword2" - type="password" - auto-complete="off"/> - </el-form-item> - <el-form-item> - <el-button - type="primary" - @click="submitForm('ruleForm2')">鎻愪氦 - </el-button> - <el-button @click="resetForm('ruleForm2')">閲嶇疆</el-button> - </el-form-item> - </el-form> - </div> - </el-col> - </el-row> - </basic-container> - </div> + <div class="app-container calendar-list-container"> + <basic-container> + <template> + <el-tabs @tab-click="switchTab"> + <el-tab-pane label="淇℃伅绠$悊" name="userManager" /> + <el-tab-pane label="瀵嗙爜绠$悊" name="passwordManager" /> + </el-tabs> + </template> + <el-row> + <el-col :span="12"> + <div class="grid-content bg-purple"> + <el-form v-if="switchStatus === 'userManager'" ref="ruleForm2" :model="ruleForm2" :rules="rules2" label-width="100px" class="demo-ruleForm"> + <el-form-item label="鐢ㄦ埛鍚�" prop="username"> + <el-input v-model="ruleForm2.username" type="text" disabled /> + </el-form-item> + <el-form-item label="鎵嬫満鍙�" prop="phone"> + <el-input v-model="ruleForm2.phone" placeholder="楠岃瘉鐮佺櫥褰曚娇鐢�" /> + </el-form-item> + <el-form-item label="澶村儚"> + <el-upload :headers="headers" :show-file-list="false" :on-success="handleAvatarSuccess" class="avatar-uploader" action="/admin/sys-file/upload"> + <img v-if="ruleForm2.avatar" id="avatar" :src="avatarUrl" class="avatar" /> + <i v-else class="el-icon-plus avatar-uploader-icon" /> + </el-upload> + </el-form-item> + <el-form-item label="绀句氦鐧诲綍" prop="social"> + <a href="#" style="color: blue" @click="handleClick('wechat')">缁戝畾寰俊</a>锝� <a href="#" style="color: blue" @click="handleClick('gitee')">缁戝畾鐮佷簯</a> | + <a href="#" style="color: blue" @click="handleClick('osc')">寮�婧愪腑鍥�</a> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="submitForm('ruleForm2')">鎻愪氦 </el-button> + <el-button @click="resetForm('ruleForm2')">閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-form v-if="switchStatus === 'passwordManager'" ref="ruleForm2" :model="ruleForm2" :rules="rules2" label-width="100px" class="demo-ruleForm"> + <el-form-item label="鍘熷瘑鐮�" prop="password"> + <el-input v-model="ruleForm2.password" type="password" auto-complete="off" /> + </el-form-item> + <el-form-item label="瀵嗙爜" prop="newpassword1"> + <div class="password-input-container"> + <el-input v-model="ruleForm2.newpassword1" type="password" @input="checkPasswordStrength" show-password auto-complete="off" /> + <div v-if="ruleForm2.newpassword1" class="password-strength-indicator"> + 瀵嗙爜寮哄害: <span :class="passwordLevelClass">{{ passwordLevel }}</span> + </div> + </div> + </el-form-item> + <el-form-item label="纭瀵嗙爜" prop="newpassword2"> + <el-input v-model="ruleForm2.newpassword2" type="password" auto-complete="off" /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="submitForm('ruleForm2')">鎻愪氦 </el-button> + <el-button @click="resetForm('ruleForm2')">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + </el-col> + </el-row> + </basic-container> + </div> </template> <script> - import {handleImg, openWindow} from '@/util/util' - import {mapState} from 'vuex' - import store from '@/store' - import {getStore, setStore} from '@/util/store' - import {editInfo} from '@/api/admin/user' +import { handleImg, openWindow } from '@/util/util' +import { mapState } from 'vuex' +import store from '@/store' +import { getStore, setStore } from '@/util/store' +import { editInfo } from '@/api/admin/user' - - export default { +export default { data() { - var validatePass = (rule, value, callback) => { - if (this.ruleForm2.password !== '') { - if (value !== this.ruleForm2.newpassword1) { - callback(new Error('涓ゆ杈撳叆瀵嗙爜涓嶄竴鑷�!')) - } else { - callback() - } - } else { - callback() + // 瀵嗙爜寮哄害鏍¢獙 + const validatePasswordStrength = (rule, value, callback) => { + let strength = 0 + + if (value.length >= 8) strength++ + if (/\d/.test(value)) strength++ + if (/[a-z]/.test(value)) strength++ + if (/[A-Z]/.test(value)) strength++ + if (/[!@#$%^&*]/.test(value)) strength++ + + if (strength < 4) { + callback(new Error('瀵嗙爜寮哄害涓嶅锛岃纭繚瀵嗙爜鍖呭惈锛氬ぇ灏忓啓瀛楁瘝銆佹暟瀛楀拰鐗规畩瀛楃锛屼笖闀垮害涓嶅皯浜�8浣�')) + } else { + callback() + } } - } - return { - switchStatus: 'userManager', - avatarUrl: '', - show: false, - headers: { - 'Authorization': 'Bearer ' + store.getters.access_token, - }, - ruleForm2: { - username: '', - password: '', - newpassword1: '', - newpassword2: '', - avatar: '', - phone: '' - }, - rules2: { - password: [{required: true, min: 6, message: '鍘熷瘑鐮佷笉鑳戒负绌轰笖涓嶅皯浜�6浣�', trigger: 'change'}], - newpassword1: [{required: false, min: 6, message: '涓嶅皯浜�6浣�', trigger: 'change'}], - newpassword2: [{required: false, validator: validatePass, trigger: 'blur'}] + + // 纭瀵嗙爜鏍¢獙 + var validatePass = (rule, value, callback) => { + if (this.ruleForm2.password !== '') { + if (value !== this.ruleForm2.newpassword1) { + callback(new Error('涓ゆ杈撳叆瀵嗙爜涓嶄竴鑷�!')) + } else { + callback() + } + } else { + callback() + } } - } + + return { + switchStatus: 'userManager', + avatarUrl: '', + show: false, + headers: { + Authorization: 'Bearer ' + store.getters.access_token + }, + ruleForm2: { + username: '', + password: '', + newpassword1: '', + newpassword2: '', + avatar: '', + phone: '' + }, + passwordLevel: '', // 瀵嗙爜寮哄害绾у埆 + passwordLevelClass: '', // 瀵嗙爜寮哄害鏍峰紡绫� + rules2: { + password: [ + { + required: true, + min: 8, + message: '鍘熷瘑鐮佷笉鑳戒负绌轰笖涓嶅皯浜�8浣�', + trigger: 'change' + } + ], + newpassword1: [{ required: true, message: '璇疯緭鍏ユ柊瀵嗙爜', trigger: 'blur' }, { validator: validatePasswordStrength, trigger: 'blur' }], + newpassword2: [ + { + required: true, + validator: validatePass, + trigger: 'blur' + } + ] + } + } }, created() { - this.resetForm() + this.resetForm() }, computed: { - ...mapState({ - userInfo: state => state.user.userInfo - }) + ...mapState({ + userInfo: (state) => state.user.userInfo + }) }, methods: { - switchTab(tab) { - if (tab.name === 'userManager') { - handleImg(this.ruleForm2.avatar, 'avatar') - } - this.switchStatus = tab.name - }, - submitForm(formName) { - this.$refs[formName].validate(valid => { - if (!valid) { - return false - } - editInfo(this.ruleForm2).then(response => { - this.handleLocalData(this.ruleForm2) - this.$notify.success('淇敼鎴愬姛') - // 淇敼瀵嗙爜涔嬪悗寮哄埗閲嶆柊鐧诲綍 - if (this.switchStatus === 'passwordManager') { - this.$store.dispatch('LogOut').then(() => { - location.reload() // 涓轰簡閲嶆柊瀹炰緥鍖杤ue-router瀵硅薄 閬垮厤bug - }) + switchTab(tab) { + if (tab.name === 'userManager') { + handleImg(this.ruleForm2.avatar, 'avatar') } - }) - }) - }, - resetForm() { - this.ruleForm2.password = undefined - this.ruleForm2.newpassword1 = undefined - this.ruleForm2.newpassword2 = undefined - this.ruleForm2.username = this.userInfo.username - this.ruleForm2.phone = this.userInfo.phone - this.ruleForm2.avatar = this.userInfo.avatar - handleImg(this.userInfo.avatar, 'avatar') - //鍒ゆ柇鏄惁閫夋嫨浜嗙鎴稩D - const TENANT_ID = getStore({name: 'tenantId'}) - if (TENANT_ID) { - this.headers['TENANT-ID'] = TENANT_ID // 绉熸埛ID - } - }, - handleClick(thirdpart) { - let appid, client_id, redirect_uri, url - redirect_uri = encodeURIComponent(window.location.origin + '/#/authredirect') - if (thirdpart === 'wechat') { - appid = 'wxd1678d3f83b1d83a' - url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&state=WX-BIND&response_type=code&scope=snsapi_login#wechat_redirect' - } else if (thirdpart === 'tencent') { - client_id = '101322838' - url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&state=QQ-BIND&client_id=' + client_id + '&redirect_uri=' + redirect_uri - } else if (thirdpart === 'gitee') { - client_id = '235ce26bbc59565b82c989aa3a407ce844cf59a7c5e0f9caa9bb3bf32cee5952' - url = 'https://gitee.com/oauth/authorize?response_type=code&state=GITEE-BIND&client_id=' + client_id + '&redirect_uri=' + redirect_uri - } else if (thirdpart === 'osc') { - client_id = 'neIIqlwGsjsfsA6uxNqD' - url = 'https://www.oschina.net/action/oauth2/authorize?response_type=code&client_id=' + client_id + '&state=OSC-BIND&redirect_uri=' + redirect_uri - } - openWindow(url, thirdpart, 540, 540) - }, - handleAvatarSuccess(res, file) { - this.avatarUrl = URL.createObjectURL(file.raw) - this.ruleForm2.avatar = res.data.url - }, - // 澶勭悊鏈湴鏁版嵁锛岄伩鍏嶅埛鏂颁笉鍚屾 - handleLocalData(form) { - let userInfo = getStore({name: 'userInfo'}) + this.switchStatus = tab.name + }, + submitForm(formName) { + this.$refs[formName].validate((valid) => { + if (!valid) { + return false + } + editInfo(this.ruleForm2).then((response) => { + this.handleLocalData(this.ruleForm2) + this.$notify.success('淇敼鎴愬姛') + // 淇敼瀵嗙爜涔嬪悗寮哄埗閲嶆柊鐧诲綍 + if (this.switchStatus === 'passwordManager') { + this.$store.dispatch('LogOut').then(() => { + location.reload() // 涓轰簡閲嶆柊瀹炰緥鍖杤ue-router瀵硅薄 閬垮厤bug + }) + } + }) + }) + }, + resetForm() { + this.ruleForm2.password = undefined + this.ruleForm2.newpassword1 = undefined + this.ruleForm2.newpassword2 = undefined + this.ruleForm2.username = this.userInfo.username + this.ruleForm2.phone = this.userInfo.phone + this.ruleForm2.avatar = this.userInfo.avatar + handleImg(this.userInfo.avatar, 'avatar') + //鍒ゆ柇鏄惁閫夋嫨浜嗙鎴稩D + const TENANT_ID = getStore({ name: 'tenantId' }) + if (TENANT_ID) { + this.headers['TENANT-ID'] = TENANT_ID // 绉熸埛ID + } + }, + handleClick(thirdpart) { + let appid, client_id, redirect_uri, url + redirect_uri = encodeURIComponent(window.location.origin + '/#/authredirect') + if (thirdpart === 'wechat') { + appid = 'wxd1678d3f83b1d83a' + url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&state=WX-BIND&response_type=code&scope=snsapi_login#wechat_redirect' + } else if (thirdpart === 'tencent') { + client_id = '101322838' + url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&state=QQ-BIND&client_id=' + client_id + '&redirect_uri=' + redirect_uri + } else if (thirdpart === 'gitee') { + client_id = '235ce26bbc59565b82c989aa3a407ce844cf59a7c5e0f9caa9bb3bf32cee5952' + url = 'https://gitee.com/oauth/authorize?response_type=code&state=GITEE-BIND&client_id=' + client_id + '&redirect_uri=' + redirect_uri + } else if (thirdpart === 'osc') { + client_id = 'neIIqlwGsjsfsA6uxNqD' + url = 'https://www.oschina.net/action/oauth2/authorize?response_type=code&client_id=' + client_id + '&state=OSC-BIND&redirect_uri=' + redirect_uri + } + openWindow(url, thirdpart, 540, 540) + }, + handleAvatarSuccess(res, file) { + this.avatarUrl = URL.createObjectURL(file.raw) + this.ruleForm2.avatar = res.data.url + }, + // 澶勭悊鏈湴鏁版嵁锛岄伩鍏嶅埛鏂颁笉鍚屾 + handleLocalData(form) { + let userInfo = getStore({ name: 'userInfo' }) - if (userInfo) { - userInfo.avatar = form.avatar - userInfo.phone = form.phone - setStore({ - name: 'userInfo', - content: userInfo, - type: 'session' - }) + if (userInfo) { + userInfo.avatar = form.avatar + userInfo.phone = form.phone + setStore({ + name: 'userInfo', + content: userInfo, + type: 'session' + }) + } + }, + // 妫�鏌ュ瘑鐮佸己搴� + checkPasswordStrength(password) { + let strength = 0 + + // 妫�鏌ラ暱搴� + if (password.length >= 8) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚暟瀛� + if (/\d/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚皬鍐欏瓧姣� + if (/[a-z]/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚ぇ鍐欏瓧姣� + if (/[A-Z]/.test(password)) strength++ + + // 妫�鏌ユ槸鍚﹀寘鍚壒娈婂瓧绗� + if (/[!@#$%^&*]/.test(password)) strength++ + + switch (strength) { + case 0: + case 1: + this.passwordLevel = '寮�' + this.passwordLevelClass = 'password-weak' + break + case 2: + case 3: + this.passwordLevel = '涓�' + this.passwordLevelClass = 'password-medium' + break + case 4: + case 5: + this.passwordLevel = '寮�' + this.passwordLevelClass = 'password-strong' + break + } } - } } - } +} </script> <style> - .avatar-uploader .el-upload { +.avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; - } +} - .avatar-uploader .el-upload:hover { - border-color: #409EFF; - } +.avatar-uploader .el-upload:hover { + border-color: #409eff; +} - .avatar-uploader-icon { +.avatar-uploader-icon { font-size: 28px !important; color: #8c939d !important; width: 178px !important; height: 178px !important; line-height: 178px !important; text-align: center !important; - } +} - .avatar { +.avatar { width: 178px; height: 178px; display: block; - } +} + +.password-input-container { + width: 100%; + .el-input { + width: 100%; + } +} + +.password-strength-indicator { + margin-top: 5px; + font-size: 12px; + color: #606266; +} + +.password-weak { + color: #f56c6c; +} + +.password-medium { + color: #e6a23c; +} + +.password-strong { + color: #67c23a; +} </style> -- Gitblit v1.9.3