Compare commits
2 Commits
577e524c6d
...
78fb8d2a78
Author | SHA1 | Date |
---|---|---|
|
78fb8d2a78 | 7 months ago |
|
9749dac3d3 | 7 months ago |
@ -0,0 +1,21 @@ |
||||
import request from "@/utils/request"; |
||||
import { AxiosPromise } from "axios"; |
||||
|
||||
export function updateIpv6Config(param1: string, param2: string) { |
||||
return request({ |
||||
url: `/api/ipv6-collection-config/v1/${param1}/${param2}`, |
||||
method: "PUT", |
||||
}); |
||||
} |
||||
export function uploadDevSnoApi(file: any): AxiosPromise<string> { |
||||
const formData = new FormData(); |
||||
formData.append("file", file); |
||||
return request({ |
||||
url: `/api/ipv6-collection-config/v1/upload`, |
||||
method: "POST", |
||||
data: formData, |
||||
headers: { |
||||
"Content-Type": "multipart/form-data", |
||||
}, |
||||
}); |
||||
} |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 571 KiB |
@ -0,0 +1,289 @@ |
||||
/** |
||||
* 数字 |
||||
*/ |
||||
const REG_NUMBER: string = ".*\\d+.*"; |
||||
/** |
||||
* 小写字母 |
||||
*/ |
||||
const REG_UPPERCASE: string = ".*[A-Z]+.*"; |
||||
/** |
||||
* 大写字母 |
||||
*/ |
||||
const REG_LOWERCASE: string = ".*[a-z]+.*"; |
||||
/** |
||||
* 特殊符号(~!@#$%^&*()_+|<>,.?/:;'[]{}\) |
||||
*/ |
||||
const REG_SYMBOL: string = ".*[~!@#$%^&*()_+|<>,.?/:;'\\[\\]{}\"]+.*"; |
||||
/** |
||||
* 键盘字符表(小写) |
||||
* 非shift键盘字符表 |
||||
*/ |
||||
const CHAR_TABLE1: string[][] = [ |
||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\0"], |
||||
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\"], |
||||
["a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "\0", "\0"], |
||||
["z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "\0", "\0", "\0"], |
||||
]; |
||||
/** |
||||
* shift键盘的字符表 |
||||
*/ |
||||
const CHAR_TABLE2: string[][] = [ |
||||
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\0"], |
||||
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "{", "}", "|"], |
||||
["a", "s", "d", "f", "g", "h", "j", "k", "l", ":", '"', "\0", "\0"], |
||||
["z", "x", "c", "v", "b", "n", "m", "<", ">", "?", "\0", "\0", "\0"], |
||||
]; |
||||
|
||||
/** |
||||
* 校验密码是否符合条件 |
||||
* @param password 密码 |
||||
* @param username 用户名 |
||||
*/ |
||||
export const checkPasswordRule = (password: string, username: string) => { |
||||
if (password === "" || password.length < 8 || password.length > 32) { |
||||
// console.log("长度小于8,或大于32");
|
||||
return "密码长度应大于8小于32"; |
||||
} |
||||
if (password.indexOf(username) !== -1) { |
||||
// console.log("包含用户名");
|
||||
return "请勿包含用户名"; |
||||
} |
||||
if (isContinuousChar(password)) { |
||||
// console.log("包含3个及以上相同或字典连续字符");
|
||||
return "请勿包含3个及以上相同或连续的字符"; |
||||
} |
||||
if (isKeyBoardContinuousChar(password)) { |
||||
// console.log("包含3个及以上键盘连续字符");
|
||||
return "请勿包含3个及以上键盘连续字符"; |
||||
} |
||||
let i: number = 0; |
||||
if (password.match(REG_NUMBER)) i++; |
||||
if (password.match(REG_LOWERCASE)) i++; |
||||
if (password.match(REG_UPPERCASE)) i++; |
||||
if (password.match(REG_SYMBOL)) i++; |
||||
if (i < 2) { |
||||
// console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
|
||||
return "数字、小写字母、大写字母、特殊字符,至少包含两种"; |
||||
} |
||||
// console.log(i);
|
||||
return "校验通过"; |
||||
}; |
||||
|
||||
/** |
||||
* 是否包含3个及以上相同或字典连续字符 |
||||
*/ |
||||
const isContinuousChar = (password: string) => { |
||||
const chars: string[] = password.split(""); |
||||
const charCode: number[] = []; |
||||
for (let i = 0; i < chars.length - 2; i++) { |
||||
charCode[i] = chars[i].charCodeAt(0); |
||||
} |
||||
for (let i = 0; i < charCode.length - 2; i++) { |
||||
const n1 = charCode[i]; |
||||
const n2 = charCode[i + 1]; |
||||
const n3 = charCode[i + 2]; |
||||
// 判断重复字符
|
||||
if (n1 == n2 && n1 == n3) { |
||||
return true; |
||||
} |
||||
// 判断连续字符: 正序 + 倒序
|
||||
if ((n1 + 1 == n2 && n1 + 2 == n3) || (n1 - 1 == n2 && n1 - 2 == n3)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
}; |
||||
/** |
||||
* 是否包含3个及以上键盘连续字符 |
||||
* @param password 待匹配的字符串 |
||||
*/ |
||||
const isKeyBoardContinuousChar = (password: string) => { |
||||
if (password === "") { |
||||
return false; |
||||
} |
||||
//考虑大小写,都转换成小写字母
|
||||
const lpStrChars: string[] = password.toLowerCase().split(""); |
||||
// 获取字符串长度
|
||||
const nStrLen: number = lpStrChars.length; |
||||
// 定义位置数组:row - 行,col - column 列
|
||||
const pRowCharPos: number[] = new Array(nStrLen).fill(""); |
||||
const pColCharPos: number[] = new Array(nStrLen).fill(""); |
||||
for (let i = 0; i < nStrLen; i++) { |
||||
const chLower: string = lpStrChars[i]; |
||||
pColCharPos[i] = -1; |
||||
// 检索在表1中的位置,构建位置数组
|
||||
for (let nRowTable1Idx = 0; nRowTable1Idx < 4; nRowTable1Idx++) { |
||||
for (let nColTable1Idx = 0; nColTable1Idx < 13; nColTable1Idx++) { |
||||
if (chLower == CHAR_TABLE1[nRowTable1Idx][nColTable1Idx]) { |
||||
pRowCharPos[i] = nRowTable1Idx; |
||||
pColCharPos[i] = nColTable1Idx; |
||||
} |
||||
} |
||||
} |
||||
// 在表1中没找到,到表二中去找,找到则continue
|
||||
if (pColCharPos[i] >= 0) { |
||||
continue; |
||||
} |
||||
// 检索在表2中的位置,构建位置数组
|
||||
for (let nRowTable2Idx = 0; nRowTable2Idx < 4; nRowTable2Idx++) { |
||||
for (let nColTable2Idx = 0; nColTable2Idx < 13; nColTable2Idx++) { |
||||
if (chLower == CHAR_TABLE2[nRowTable2Idx][nColTable2Idx]) { |
||||
pRowCharPos[i] = nRowTable2Idx; |
||||
pColCharPos[i] = nColTable2Idx; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
// 匹配坐标连线
|
||||
for (let j = 1; j <= nStrLen - 2; j++) { |
||||
//同一行
|
||||
if ( |
||||
pRowCharPos[j - 1] == pRowCharPos[j] && |
||||
pRowCharPos[j] == pRowCharPos[j + 1] |
||||
) { |
||||
// 键盘行正向连续(asd)或者键盘行反向连续(dsa)
|
||||
if ( |
||||
(pColCharPos[j - 1] + 1 == pColCharPos[j] && |
||||
pColCharPos[j] + 1 == pColCharPos[j + 1]) || |
||||
(pColCharPos[j + 1] + 1 == pColCharPos[j] && |
||||
pColCharPos[j] + 1 == pColCharPos[j - 1]) |
||||
) { |
||||
return true; |
||||
} |
||||
} |
||||
//同一列
|
||||
if ( |
||||
pColCharPos[j - 1] == pColCharPos[j] && |
||||
pColCharPos[j] == pColCharPos[j + 1] |
||||
) { |
||||
//键盘列连续(qaz)或者键盘列反向连续(zaq)
|
||||
if ( |
||||
(pRowCharPos[j - 1] + 1 == pRowCharPos[j] && |
||||
pRowCharPos[j] + 1 == pRowCharPos[j + 1]) || |
||||
(pRowCharPos[j - 1] - 1 == pRowCharPos[j] && |
||||
pRowCharPos[j] - 1 == pRowCharPos[j + 1]) |
||||
) { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
}; |
||||
|
||||
/** |
||||
* 密码强度校验 |
||||
*/ |
||||
/** |
||||
* 长度 |
||||
* @param str |
||||
*/ |
||||
const length = (str: string) => { |
||||
if (str.length < 5) { |
||||
return 5; |
||||
} else if (str.length < 8) { |
||||
return 10; |
||||
} else { |
||||
return 25; |
||||
} |
||||
}; |
||||
/** |
||||
* 字母 |
||||
* @param str |
||||
*/ |
||||
const letters = (str: string) => { |
||||
let count1 = 0, |
||||
count2 = 0; |
||||
for (let i = 0; i < str.length; i++) { |
||||
if (str.charAt(i) >= "a" && str.charAt(i) <= "z") { |
||||
count1++; |
||||
} |
||||
if (str.charAt(i) >= "A" && str.charAt(i) <= "Z") { |
||||
count2++; |
||||
} |
||||
} |
||||
if (count1 == 0 && count2 == 0) { |
||||
return 0; |
||||
} |
||||
if (count1 != 0 && count2 != 0) { |
||||
return 20; |
||||
} |
||||
return 10; |
||||
}; |
||||
|
||||
/** |
||||
* 数字 |
||||
* @param str |
||||
*/ |
||||
const numbers = (str: string) => { |
||||
let count = 0; |
||||
for (let i = 0; i < str.length; i++) { |
||||
if (str.charAt(i) >= "0" && str.charAt(i) <= "9") { |
||||
count++; |
||||
} |
||||
} |
||||
if (count == 0) { |
||||
return 0; |
||||
} |
||||
if (count == 1) { |
||||
return 10; |
||||
} |
||||
return 20; |
||||
}; |
||||
/** |
||||
* 符号 |
||||
* @param str |
||||
*/ |
||||
const symbols = (str: string) => { |
||||
let count = 0; |
||||
for (let i = 0; i < str.length; i++) { |
||||
if ( |
||||
(str.charCodeAt(i) >= 0x21 && str.charCodeAt(i) <= 0x2f) || |
||||
(str.charCodeAt(i) >= 0x3a && str.charCodeAt(i) <= 0x40) || |
||||
(str.charCodeAt(i) >= 0x5b && str.charCodeAt(i) <= 0x60) || |
||||
(str.charCodeAt(i) >= 0x7b && str.charCodeAt(i) <= 0x7e) |
||||
) { |
||||
count++; |
||||
} |
||||
} |
||||
if (count == 0) { |
||||
return 0; |
||||
} |
||||
if (count == 1) { |
||||
return 10; |
||||
} |
||||
return 25; |
||||
}; |
||||
/** |
||||
* 得分机制 |
||||
* @param str |
||||
*/ |
||||
const rewards = (str: string) => { |
||||
const letter = letters(str); //字母
|
||||
const number = numbers(str); //数字
|
||||
const symbol = symbols(str); //符号
|
||||
if (letter > 0 && number > 0 && symbol == 0) { |
||||
//字母和数字
|
||||
return 2; |
||||
} |
||||
if (letter == 10 && number > 0 && symbol > 0) { |
||||
//字母、数字和符号
|
||||
return 3; |
||||
} |
||||
if (letter == 20 && number > 0 && symbol > 0) { |
||||
//大小写字母、数字和符号
|
||||
return 5; |
||||
} |
||||
return 0; |
||||
}; |
||||
/** |
||||
* 最终评分 |
||||
* @param str |
||||
*/ |
||||
export const level = (str: string) => { |
||||
const lengths = length(str); //长度
|
||||
const letter = letters(str); //字母
|
||||
const number = numbers(str); //数字
|
||||
const symbol = symbols(str); //符号
|
||||
const reward = rewards(str); //奖励
|
||||
return lengths + letter + number + symbol + reward; |
||||
}; |
@ -0,0 +1,382 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-card shadow="never"> |
||||
<template #header> |
||||
<div style="display: flex; align-items: center"> |
||||
<el-icon size="15"> <Grid /> </el-icon> <span |
||||
style="font-weight: 700; font-size: 14px; line-height: 16px" |
||||
>IPV6采集配置信息</span |
||||
> |
||||
</div> |
||||
</template> |
||||
<el-descriptions :column="1" border v-loading="loading"> |
||||
<el-descriptions-item |
||||
label="分公司" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select v-model="area" clearable placeholder="请选择分公司"> |
||||
<el-option |
||||
v-for="item in areaOption" |
||||
:label="item.label" |
||||
:value="item.value" |
||||
:key="item.value" |
||||
/> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button type="primary" @click="updateIpv6ConfigParam('areaId')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="区局" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select v-model="subarea" placeholder="请选择区局" clearable> |
||||
<el-option |
||||
v-for="item in subareaOption" |
||||
:label="item.label" |
||||
:value="item.value" |
||||
:key="item.value" |
||||
/> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button |
||||
type="primary" |
||||
@click="updateIpv6ConfigParam('subAreaId')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="路由/桥接" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select |
||||
v-model="rgMode" |
||||
placeholder="请选择连接方式" |
||||
clearable |
||||
> |
||||
<el-option label="桥接" value="1,IP_Routed" /> |
||||
<el-option label="路由" value="2,PPPoE_Bridged" /> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button |
||||
type="primary" |
||||
@click="updateIpv6ConfigParam('routeId')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="设备供应商" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select |
||||
v-model="category" |
||||
placeholder="请选择设备供应商" |
||||
filterable |
||||
clearable |
||||
> |
||||
<el-option |
||||
v-for="item in vendorNameOption" |
||||
:label="item.label" |
||||
:value="item.value" |
||||
:key="item.value" |
||||
/> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button |
||||
type="primary" |
||||
disabled |
||||
@click="updateIpv6ConfigParam('devVendorName')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="设备型号" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select |
||||
v-model="typeName" |
||||
placeholder="请选择设备型号" |
||||
filterable |
||||
clearable |
||||
> |
||||
<el-option |
||||
v-for="item in typeNameOption" |
||||
:label="item.label" |
||||
:value="item.value" |
||||
:key="item.value" |
||||
/> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button |
||||
type="primary" |
||||
@click="updateIpv6ConfigParam('deviceTypeId')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="采集组件" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-select |
||||
v-model="acquisitionComponents" |
||||
placeholder="请选择采集组件" |
||||
clearable |
||||
> |
||||
<el-option label="queryIpv6Info" value="queryIpv6Info" /> |
||||
</el-select> |
||||
</el-col> |
||||
<el-col :span="8"> |
||||
<el-button |
||||
type="primary" |
||||
disabled |
||||
@click="updateIpv6ConfigParam('collectStrId')" |
||||
>修改 |
||||
</el-button> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="导入设备序列号" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
<el-row :gutter="20"> |
||||
<el-col :span="16"> |
||||
<el-upload |
||||
drag |
||||
action="#" |
||||
:limit="1" |
||||
:auto-upload="false" |
||||
accept="text/plain" |
||||
v-model:file-list="fileList" |
||||
> |
||||
<el-icon class="el-icon--upload"> |
||||
<upload-filled /> |
||||
</el-icon> |
||||
<div class="el-upload__text">拖拽上传 或 <em>点击上传</em></div> |
||||
<template #tip> |
||||
<div |
||||
style=" |
||||
margin-top: 7px; |
||||
color: var(--el-text-color-regular); |
||||
font-size: 12px; |
||||
" |
||||
> |
||||
(仅支持txt文件按列排序) |
||||
</div> |
||||
</template> |
||||
</el-upload> |
||||
</el-col> |
||||
<el-col :span="8" style="display: flex; margin: auto 0"> |
||||
<div> |
||||
<el-button type="primary" @click="handUpload" |
||||
>提交文件</el-button |
||||
> |
||||
</div> |
||||
</el-col> |
||||
</el-row> |
||||
</el-descriptions-item> |
||||
</el-descriptions> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { Grid, UploadFilled } from "@element-plus/icons-vue"; |
||||
import { reportArea } from "@/api/report"; |
||||
import { getTypeNameOption, getVendorNameOption } from "@/api/device-type"; |
||||
import { UploadUserFile } from "element-plus"; |
||||
import { confirm } from "@/utils/confirm"; |
||||
import { updateIpv6Config, uploadDevSnoApi } from "@/api/ipv6Config"; |
||||
|
||||
defineOptions({ |
||||
name: "Ipv6CollectingConfig", |
||||
inheritAttrs: false, |
||||
}); |
||||
const fileList = ref<UploadUserFile[]>([]); |
||||
const loading = ref<boolean>(false); |
||||
const area = ref<number>(); |
||||
const subarea = ref<number>(); |
||||
const rgMode = ref<string>(); |
||||
const category = ref<string>(); |
||||
const typeName = ref<string>(); |
||||
const acquisitionComponents = ref<string>(); |
||||
const areaOption = ref<OptionType[]>([]); |
||||
const subareaOption = ref<OptionType[]>([]); |
||||
const vendorNameOption = ref<OptionType[]>([]); |
||||
const typeNameOption = ref<OptionType[]>([]); |
||||
watch( |
||||
() => area.value, |
||||
() => { |
||||
if (area.value === undefined) { |
||||
return; |
||||
} |
||||
loadSubarea(area.value); |
||||
} |
||||
); |
||||
const loadSubarea = (groupId: number) => { |
||||
reportArea(groupId).then(({ data }) => { |
||||
subareaOption.value = data; |
||||
}); |
||||
}; |
||||
const loadArea = (groupId: number) => { |
||||
reportArea(groupId).then(({ data }) => { |
||||
areaOption.value.push(...data); |
||||
}); |
||||
}; |
||||
const loadVendorNameOption = () => { |
||||
getVendorNameOption().then(({ data }) => { |
||||
vendorNameOption.value.push(...data); |
||||
}); |
||||
}; |
||||
const loadTypeNameOption = () => { |
||||
getTypeNameOption("").then(({ data }) => { |
||||
typeNameOption.value.push(...data); |
||||
}); |
||||
}; |
||||
const updateIpv6ConfigParam = (param: string) => { |
||||
let param2 = ""; |
||||
switch (param) { |
||||
case "areaId": |
||||
param2 = area.value; |
||||
break; |
||||
case "subAreaId": |
||||
param2 = subarea.value; |
||||
break; |
||||
case "routeId": |
||||
param2 = rgMode.value; |
||||
break; |
||||
case "devVendorName": |
||||
param2 = category.value; |
||||
break; |
||||
case "deviceTypeId": |
||||
param2 = typeName.value; |
||||
break; |
||||
case "collectStrId": |
||||
param2 = acquisitionComponents.value; |
||||
break; |
||||
} |
||||
if (param2 === "" || param2 == undefined) { |
||||
param2 = "-1"; |
||||
} |
||||
confirm("确定修改吗", () => { |
||||
loading.value = true; |
||||
updateIpv6Config(param, param2) |
||||
.then(() => { |
||||
ElMessage({ |
||||
message: "修改成功", |
||||
type: "success", |
||||
duration: 1000, |
||||
}); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}); |
||||
}; |
||||
const handUpload = () => { |
||||
if (fileList.value.length === 0) { |
||||
ElMessage({ |
||||
message: "请选择需要上传的文件", |
||||
type: "error", |
||||
duration: 1000, |
||||
}); |
||||
return; |
||||
} |
||||
const file = fileList.value[0].raw; |
||||
let fileType = file?.type; |
||||
if (fileType != "text/plain") { |
||||
ElMessage({ |
||||
message: "文件类型错误", |
||||
type: "error", |
||||
duration: 1000, |
||||
}); |
||||
return; |
||||
} |
||||
confirm("确定提交文件吗", () => { |
||||
loading.value = true; |
||||
uploadDevSnoApi(file) |
||||
.then(() => { |
||||
ElMessage({ |
||||
message: "操作成功", |
||||
duration: 1000, |
||||
type: "success", |
||||
}); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}); |
||||
}; |
||||
onMounted(() => { |
||||
loadArea(0); |
||||
loadVendorNameOption(); |
||||
loadTypeNameOption(); |
||||
}); |
||||
</script> |
||||
|
||||
<style scoped> |
||||
:deep(.el-card__body) { |
||||
padding: 10px 0 0 0; |
||||
} |
||||
|
||||
:deep(.my-label) { |
||||
width: 10px !important; |
||||
background: var(--el-color-white) !important; |
||||
} |
||||
</style> |
@ -0,0 +1,110 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-card shadow="never"> |
||||
<template #header> |
||||
<div style="display: flex; align-items: center"> |
||||
<el-icon size="15"> <Grid /> </el-icon> <span |
||||
style="font-weight: 700; font-size: 14px; line-height: 16px" |
||||
>远程操作任务信息</span |
||||
> |
||||
</div> |
||||
</template> |
||||
<el-descriptions v-loading="loading" :column="1" border> |
||||
<el-descriptions-item |
||||
label="创建人" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.userName }} |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="创建时间" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.operTaskCreateTime }} |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="开始时间" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.operStartTime }} |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="结束时间" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.operEndTime }} |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="结果" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.resultState }} |
||||
</el-descriptions-item> |
||||
<el-descriptions-item |
||||
label="原因" |
||||
label-align="left" |
||||
align="left" |
||||
label-class-name="my-label" |
||||
class-name="my-content" |
||||
width="150px" |
||||
> |
||||
{{ result.errorDesc }} |
||||
</el-descriptions-item> |
||||
</el-descriptions> |
||||
</el-card> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { Grid } from "@element-plus/icons-vue"; |
||||
import { OperateTask2ResultVO } from "@/api/operate-task2/types"; |
||||
import { taskResult } from "@/api/operate-task2"; |
||||
const route = useRoute(); |
||||
let taskId: number = parseInt(<string>route.params.taskId); |
||||
const result = ref<OperateTask2ResultVO>({}); |
||||
const loading = ref<boolean>(false); |
||||
|
||||
const getData = () => { |
||||
loading.value = false; |
||||
taskResult(taskId) |
||||
.then(({ data }) => { |
||||
result.value = data; |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}; |
||||
onMounted(() => { |
||||
getData(); |
||||
}); |
||||
</script> |
||||
|
||||
<style scoped> |
||||
:deep(.my-label) { |
||||
width: 80px !important; |
||||
background: var(--el-color-white) !important; |
||||
} |
||||
:deep(.el-card__body) { |
||||
padding: 10px 0 0 0; |
||||
} |
||||
</style> |
@ -0,0 +1,254 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<div class="flex justify-between"> |
||||
<div class="card mr-1 ml-3"> |
||||
<div> |
||||
<el-image |
||||
class="parent-img" |
||||
style="border-radius: 10px 10px 0 0" |
||||
:src="personUrl" |
||||
/> |
||||
<el-avatar |
||||
:size="100" |
||||
shape="circle" |
||||
fit="cover" |
||||
class="child-avatar" |
||||
:src="avatarUrl" |
||||
/> |
||||
<div class="user-info"> |
||||
<div class="username">{{ userStore.user.username }}</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="wh-full"> |
||||
<el-card shadow="never"> |
||||
<template #header> |
||||
<div style="display: flex; align-items: center"> |
||||
<el-icon size="15"> <Grid /> </el-icon> <span |
||||
style="font-weight: 700; font-size: 14px; line-height: 16px" |
||||
>更改密码</span |
||||
> |
||||
</div> |
||||
</template> |
||||
<el-form |
||||
:model="formData" |
||||
ref="ruleFormRef" |
||||
:rules="rules" |
||||
size="large" |
||||
v-loading="loading" |
||||
label-position="top" |
||||
> |
||||
<el-form-item label="当前密码" prop="oldPwd"> |
||||
<el-input |
||||
placeholder="请输入当前密码" |
||||
v-model="formData.oldPwd" |
||||
type="password" |
||||
show-password |
||||
/> |
||||
</el-form-item> |
||||
<el-form-item label="新密码" prop="newPwd"> |
||||
<el-input |
||||
placeholder="请输入新密码密码" |
||||
v-model="formData.newPwd" |
||||
type="password" |
||||
show-password |
||||
/> |
||||
<div class="flex justify-end w-full mt-2"> |
||||
<div class="bar w-20" :style="{ backgroundColor: weakColor }"> |
||||
弱 |
||||
</div> |
||||
<div class="bar w-20" :style="{ backgroundColor: middleColor }"> |
||||
中 |
||||
</div> |
||||
<div class="bar w-20" :style="{ backgroundColor: strongColor }"> |
||||
强 |
||||
</div> |
||||
</div> |
||||
</el-form-item> |
||||
<el-form-item label="确认新密码密码" prop="lastPwd"> |
||||
<el-input |
||||
placeholder="请输入新密码密码" |
||||
v-model="formData.lastPwd" |
||||
type="password" |
||||
show-password |
||||
/> |
||||
</el-form-item> |
||||
</el-form> |
||||
<template #footer> |
||||
<div class="flex justify-end"> |
||||
<el-button type="primary" @click="submitForm(ruleFormRef)" |
||||
>保 存</el-button |
||||
> |
||||
</div> |
||||
</template> |
||||
</el-card> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { Grid } from "@element-plus/icons-vue"; |
||||
import { useUserStore } from "@/store"; |
||||
import { FormInstance, FormRules } from "element-plus"; |
||||
import { checkPasswordRule, level } from "@/utils/checkPwd"; |
||||
import { PasswordForm } from "@/api/user/types"; |
||||
import { confirm } from "@/utils/confirm"; |
||||
import { updateUserPwd } from "@/api/user"; |
||||
const userStore = useUserStore(); |
||||
const ruleFormRef = ref<FormInstance>(); |
||||
const loading = ref<boolean>(false); |
||||
const route = useRoute(); |
||||
const router = useRouter(); |
||||
const formData = ref<PasswordForm>({ |
||||
oldPwd: "", |
||||
newPwd: "", |
||||
lastPwd: "", |
||||
}); |
||||
const personUrl = ref( |
||||
new URL(`../../assets/images/person_bg.jpg`, import.meta.url).href |
||||
); |
||||
const avatarUrl = ref( |
||||
new URL(`../../assets/images/avatar.png`, import.meta.url).href |
||||
); |
||||
// 强度条颜色 |
||||
const weakColor = ref<string>(""); |
||||
const middleColor = ref<string>(""); |
||||
const strongColor = ref<string>(""); |
||||
watch( |
||||
() => formData.value.newPwd, |
||||
(newVal) => { |
||||
if (formData.value.newPwd?.length === 0) { |
||||
weakColor.value = ""; |
||||
return; |
||||
} |
||||
let scores = level(<string>newVal); |
||||
if (scores > 80) { |
||||
strongColor.value = "#00c6ee"; |
||||
} else { |
||||
strongColor.value = ""; |
||||
} |
||||
if (scores > 70) { |
||||
middleColor.value = "#00c6ee"; |
||||
} else { |
||||
middleColor.value = ""; |
||||
} |
||||
if (scores > 0) { |
||||
weakColor.value = "#00c6ee"; |
||||
} else { |
||||
weakColor.value = ""; |
||||
} |
||||
} |
||||
); |
||||
const rules = reactive<FormRules<PasswordForm>>({ |
||||
oldPwd: [{ required: true, message: "请输入当前密码", trigger: "blur" }], |
||||
newPwd: [ |
||||
{ |
||||
validator: (rule: any, value: any, callback: any) => { |
||||
if (value === undefined) { |
||||
callback(new Error("请输入密码")); |
||||
} else if (value.length < 12) { |
||||
callback(new Error("密码必须大于12位")); |
||||
} else { |
||||
const result: string = checkPasswordRule( |
||||
value, |
||||
<string>userStore.user.username |
||||
); |
||||
if (result === "校验通过") { |
||||
callback(); |
||||
} else { |
||||
callback(new Error(result)); |
||||
} |
||||
} |
||||
}, |
||||
required: true, |
||||
trigger: "blur", |
||||
}, |
||||
], |
||||
lastPwd: [ |
||||
{ |
||||
required: true, |
||||
validator: (rule: any, value: any, callback: any) => { |
||||
if (formData.value.newPwd === undefined) { |
||||
callback(new Error("请先输入新密码")); |
||||
} else if (value === undefined) { |
||||
callback(new Error("请输入确认密码")); |
||||
} else if (formData.value.newPwd != formData.value.lastPwd) { |
||||
callback(new Error("两次密码不一致")); |
||||
} else { |
||||
callback(); |
||||
} |
||||
}, |
||||
trigger: "blur", |
||||
}, |
||||
], |
||||
}); |
||||
const submitForm = async (formEl: FormInstance | undefined) => { |
||||
if (!formEl) return; |
||||
await formEl.validate((valid, fields) => { |
||||
if (valid) { |
||||
confirm("确认修改密码吗", () => { |
||||
loading.value = true; |
||||
updateUserPwd(formData.value) |
||||
.then(() => { |
||||
router.push(`/login?redirect=${route.fullPath}`); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}); |
||||
} else { |
||||
console.log("error submit!", fields); |
||||
} |
||||
}); |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.card { |
||||
/* 设置卡片的宽度 */ |
||||
width: 600px; |
||||
/* 设置卡片的高度(可选,根据需要设置) */ |
||||
/* height: 200px; */ |
||||
/* 设置卡片的背景颜色 */ |
||||
background-color: #fff; |
||||
/* 设置卡片的边框(可选) */ |
||||
border: 1px solid #ddd; |
||||
/* 设置卡片的圆角 */ |
||||
border-radius: 10px; /* 你可以根据需要调整这个值 */ |
||||
/* 设置卡片的盒子模型为border-box,这样padding和border就不会增加元素的宽度和高度 */ |
||||
box-sizing: border-box; |
||||
} |
||||
.parent-img { |
||||
position: relative; |
||||
} |
||||
.child-avatar { |
||||
position: relative; |
||||
top: -50px; |
||||
left: 38%; |
||||
} |
||||
.user-info { |
||||
position: relative; |
||||
top: -50px; |
||||
.username { |
||||
text-align: center; |
||||
font-size: 22px; |
||||
font-weight: 400; |
||||
color: #344767; |
||||
} |
||||
.other-info { |
||||
margin-left: 50px; |
||||
margin-right: 50px; |
||||
color: #677481; |
||||
} |
||||
} |
||||
.bar { |
||||
height: 8px; |
||||
background-color: #dcdfe6; |
||||
margin-right: 3px; |
||||
border-radius: 4px; |
||||
transition: 0.5s all ease; |
||||
text-align: center; |
||||
color: #606266; |
||||
} |
||||
</style> |
Loading…
Reference in new issue