feat: 登录页,及Log页接口对接

This commit is contained in:
Xu Zhimeng 2023-05-25 19:42:39 +08:00
parent ac8acd3d38
commit 2f8104d4c9
15 changed files with 385 additions and 222 deletions

5
public/index.html vendored
View File

@ -139,7 +139,7 @@
top: 0; top: 0;
width: 51%; width: 51%;
height: 100%; height: 100%;
background: #49a9ee; background: #051314;
/* Old browsers */ /* Old browsers */
z-index: 1000; z-index: 1000;
-webkit-transform: translateX(0); -webkit-transform: translateX(0);
@ -249,9 +249,6 @@
<div id="loader"></div> <div id="loader"></div>
<div class="loader-section section-left"></div> <div class="loader-section section-left"></div>
<div class="loader-section section-right"></div> <div class="loader-section section-right"></div>
<div class="load_title">正在加载 核素监测数据自动处理与交互分析系统,请耐心等待
</div>
</div> </div>
</div> </div>

View File

@ -152,8 +152,8 @@ export function downFile(url,parameter, method='get'){
* @param parameter * @param parameter
* @returns {*} * @returns {*}
*/ */
export function downloadFile(url, fileName, parameter) { export function downloadFile(url, fileName, parameter, method) {
return downFile(url, parameter).then((data) => { return downFile(url, parameter, method).then((data) => {
if (!data || data.size === 0) { if (!data || data.size === 0) {
Vue.prototype['$message'].warning('文件下载失败') Vue.prototype['$message'].warning('文件下载失败')
return return

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="search-form"> <div class="search-form">
<a-form-model ref="form" :model="formModel" v-bind="$attrs" @keyup.enter.native="onSearch"> <a-form-model ref="form" :colon="false" :model="formModel" v-bind="$attrs" @keyup.enter.native="onSearch">
<a-row v-if="type == 'single-line'" :gutter="20" style="margin-right: 0 !important;"> <a-row v-if="type == 'single-line'" :gutter="20" style="margin-right: 0 !important;">
<a-col <a-col
class="search-form-item" class="search-form-item"
@ -101,6 +101,13 @@ export default {
} }
.ant-form-item-label { .ant-form-item-label {
flex-shrink: 0; flex-shrink: 0;
margin-right: 10px;
label {
font-size: 16px;
&::after {
display: none;
}
}
} }
.ant-form-item-control-wrapper { .ant-form-item-control-wrapper {
width: 100%; width: 100%;

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="tree-with-line"> <div class="tree-with-line">
<a-tree v-bind="$attrs" :defaultExpandedKeys="defaultExpandedKeys" :selected-keys="selectedKeys" @select="onSelect"> <a-tree v-bind="$attrs" :selected-keys="selectedKeys" :expandedKeys="expandedKeys" @select="onSelect" @expand="onExpand">
<template slot="switcherIcon"> <template slot="switcherIcon">
<div></div> <div></div>
</template> </template>
@ -13,7 +13,7 @@ export default {
selectedKeys: { selectedKeys: {
type: Array type: Array
}, },
defaultExpandedKeys: { expandedKeys: {
type: Array type: Array
} }
}, },
@ -23,6 +23,9 @@ export default {
this.$emit('update:selectedKeys', selectedKeys) this.$emit('update:selectedKeys', selectedKeys)
this.$emit('select') this.$emit('select')
} }
},
onExpand(expandedKeys) {
this.$emit('update:expandedKeys', expandedKeys)
} }
} }
} }

View File

@ -680,7 +680,8 @@
margin-right: 10px; margin-right: 10px;
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
padding: 0 15px; padding: 0 16px;
letter-spacing: 2px;
position: relative; position: relative;
> a { > a {
color: #c9f6f6; color: #c9f6f6;

View File

@ -23,7 +23,7 @@ export const JeecgListMixin = {
pageSizeOptions: ['10', '20', '30'], pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => { showTotal: (total, range) => {
const { current, pageSize } = this.ipagination const { current, pageSize } = this.ipagination
return `${total} 条记录 第 ${current} / ${Math.ceil(total / pageSize)}` return `Total ${total} items Page ${current} / ${Math.ceil(total / pageSize)}`
}, },
showQuickJumper: true, showQuickJumper: true,
showSizeChanger: true, showSizeChanger: true,
@ -228,12 +228,12 @@ export const JeecgListMixin = {
}, },
handleEdit: function (record) { handleEdit: function (record) {
this.$refs.modalForm.edit(record); this.$refs.modalForm.edit(record);
this.$refs.modalForm.title = "编辑"; this.$refs.modalForm.title = "Edit";
this.$refs.modalForm.disableSubmit = false; this.$refs.modalForm.disableSubmit = false;
}, },
handleAdd: function () { handleAdd: function () {
this.$refs.modalForm.add(); this.$refs.modalForm.add();
this.$refs.modalForm.title = "新增"; this.$refs.modalForm.title = "Add";
this.$refs.modalForm.disableSubmit = false; this.$refs.modalForm.disableSubmit = false;
}, },
handleTableChange(pagination, filters, sorter) { handleTableChange(pagination, filters, sorter) {
@ -262,7 +262,7 @@ export const JeecgListMixin = {
}, },
handleDetail:function(record){ handleDetail:function(record){
this.$refs.modalForm.edit(record); this.$refs.modalForm.edit(record);
this.$refs.modalForm.title="详情"; this.$refs.modalForm.title="Detail";
this.$refs.modalForm.disableSubmit = true; this.$refs.modalForm.disableSubmit = true;
}, },
/* 导出 */ /* 导出 */

View File

@ -17,9 +17,18 @@
</template> </template>
<!-- 标题结束 --> <!-- 标题结束 -->
<!-- 内容 --> <!-- 内容 -->
<tree-with-line :treeData="treeData" :selected-keys.sync="selectedKeys" :expanded-keys.sync="expandedKeys" @select="onSelect"> </tree-with-line> <a-spin :spinning="isGettingTreeData" style="min-height: 200px">
<tree-with-line
:treeData="treeData"
:selected-keys.sync="selectedKeys"
:expanded-keys.sync="expandedKeys"
@select="onSelect"
>
</tree-with-line>
</a-spin>
<!-- 内容结束 --> <!-- 内容结束 -->
</a-card> </a-card>
<!-- 日志列表 -->
<div class="log-list"> <div class="log-list">
<custom-table <custom-table
size="middle" size="middle"
@ -27,10 +36,10 @@
:columns="columns" :columns="columns"
:list="dataSource" :list="dataSource"
:pagination="false" :pagination="false"
:loading="loading" :loading="isGettingTreeData || loading"
:can-select="false" :can-select="false"
@change="handleTableChange" @change="handleTableChange"
:scroll="{ y: 'calc(100vh - 365px)' }" :scroll="{ y: 'calc(100vh - 175px)' }"
> >
<template slot="operate" slot-scope="{ record }"> <template slot="operate" slot-scope="{ record }">
<a-space :size="20"> <a-space :size="20">
@ -45,37 +54,42 @@
</template> </template>
</custom-table> </custom-table>
</div> </div>
<!-- 日志列表结束 -->
<custom-modal title="Log" v-model="visible" :show-footer="false"> <custom-modal title="Log" v-model="visible" :show-footer="false">
这里是Log内容 <a-spin :spinning="isGettingDetail" style="min-height: 100px">
{{ logInfo }}
</a-spin>
</custom-modal> </custom-modal>
</div> </div>
</template> </template>
<script> <script>
import { JeecgListMixin } from '@/mixins/JeecgListMixin' import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import TreeWithLine from '@/components/TreeWithLine/index.vue' import TreeWithLine from '@/components/TreeWithLine/index.vue'
import { downloadFile, getAction } from '../../api/manage' import { downloadFile, getAction, postAction } from '../../api/manage'
import TreeJson from './tree.json'
const columns = [ const columns = [
{ {
title: 'Name', title: 'NAME',
align: 'center', align: 'center',
dataIndex: 'name' width: 320,
dataIndex: 'fileName'
}, },
{ {
title: 'date', title: 'DATE',
align: 'center', align: 'center',
dataIndex: 'date' width: 200,
dataIndex: 'fileDate'
}, },
{ {
title: 'size', title: 'SIZE',
align: 'center', align: 'center',
dataIndex: 'size' width: 220,
dataIndex: 'fileSize'
}, },
{ {
title: 'Operate', title: 'OPERATE',
align: 'center', align: 'center',
width: 200,
scopedSlots: { scopedSlots: {
customRender: 'operate' customRender: 'operate'
} }
@ -92,22 +106,17 @@ export default {
return { return {
disableMixinCreated: true, disableMixinCreated: true,
url: { url: {
list: '/test' list: '/logManage/findFiles'
}, },
isGettingTreeData: false, // isGettingTreeData: false, //
treeData: [], treeData: [],
selectedKeys: [], selectedKeys: [],
expandedKeys: [], expandedKeys: [],
dataSource: [ dataSource: [],
{
id: 1, visible: false,
name: 'DEX33_002-20220512_0723_S_FULL_9011.9.log', isGettingDetail: false,
date: '2022-05-13', logInfo: ''
size: '105KB',
fileName: 'test.txt'
}
],
visible: false
} }
}, },
created() { created() {
@ -117,17 +126,15 @@ export default {
async getTreeData() { async getTreeData() {
try { try {
this.isGettingTreeData = true this.isGettingTreeData = true
const res = await getAction('/logManage/findFtpFolders') const res = await getAction('/logManage/findFtpFolders', { workPath: 'log' })
console.log('%c [ res ]-159', 'font-size:13px; background:pink; color:#bf2c9f;', res) this.treeData = this.buildTreeData(res)
} catch (error) {
console.error(error)
this.$message.error('Get Tree Data Failed')
this.treeData = this.buildTreeData(TreeJson)
console.log('%c [ this.treeData ]-125', 'font-size:13px; background:pink; color:#bf2c9f;', this.treeData)
const firstNode = this.treeData[0] const firstNode = this.treeData[0]
this.selectedKeys = [firstNode.key] this.selectedKeys = [firstNode.key]
this.expandedKeys = [firstNode.key] this.expandedKeys = [firstNode.key]
this.onSelect() this.onSelect()
} catch (error) {
console.error(error)
this.$message.error('Get Tree Data Failed')
} finally { } finally {
this.isGettingTreeData = false this.isGettingTreeData = false
} }
@ -152,15 +159,60 @@ export default {
return tree return tree
}, },
onSelect() { onSelect() {
this.queryParam.query = this.selectedKeys[0] this.queryParam.path = this.selectedKeys[0]
this.loadData() this.loadData()
}, },
onOperate(record) {
console.log('%c [ ]-147', 'font-size:13px; background:pink; color:#bf2c9f;', record) loadData(arg) {
this.visible = true if (!this.url.list) {
this.$message.error('请设置url.list属性!')
return
}
// 1
if (arg === 1) {
this.ipagination.current = 1
}
this.onClearSelected()
var params = this.getQueryParams() //
this.loading = true
getAction(this.url.list, params)
.then(res => {
this.dataSource = res
})
.finally(() => {
this.loading = false
})
}, },
onDownload({ fileName }) {
downloadFile('/logManage/downloadFile', fileName, { fileName }) async onOperate({ fileName, filePath, fileSize }) {
if (parseFloat(fileSize) == 0) {
this.$message.warn('This Log Is Empty')
return
}
this.visible = true
const formData = new FormData()
formData.append('fileName', fileName)
formData.append('localPath', filePath)
try {
this.isGettingDetail = true
const res = await postAction('/logManage/downloadFile', formData)
this.logInfo = res
} catch (error) {
console.error(error)
} finally {
this.isGettingDetail = false
}
},
onDownload({ fileName, filePath, fileSize }) {
if (parseFloat(fileSize) == 0) {
this.$message.warn('This Log Is Empty')
return
}
const formData = new FormData()
formData.append('fileName', fileName)
formData.append('localPath', filePath)
downloadFile('/logManage/downloadFile', fileName, formData, 'post')
} }
} }
} }

View File

@ -1,75 +1,83 @@
<template> <template>
<div class="main"> <div class="login-page">
<a-form-model class="user-layout-login" @keyup.enter.native="handleSubmit"> <div class="login-form">
<a-tabs :activeKey="customActiveKey" :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }" @change="handleTabClick"> <div class="logo">
<a-tab-pane key="tab1" tab="账号密码登录"> <img src="@/assets/images/login/logo.png" alt="" />
<login-account ref="alogin" @validateFail="validateFail" @success="requestSuccess" @fail="requestFailed"></login-account> </div>
</a-tab-pane> <a-form-model layout="vertical" ref="formRef" :model="model" :rules="rules" @keyup.enter.native="handleSubmit">
<a-form-model-item class="username" label="Username" prop="username">
<a-input v-model="model.username"></a-input>
</a-form-model-item>
<a-form-model-item class="password" label="Password" prop="password">
<a-input type="password" v-model="model.password"></a-input>
</a-form-model-item>
<!-- 验证码 -->
<a-tab-pane key="tab2" tab="手机号登录"> <a-form-model-item class="validate-code" label="Captcha" prop="inputCode">
<login-phone ref="plogin" @validateFail="validateFail" @success="requestSuccess" @fail="requestFailed"></login-phone> <a-input v-model="model.inputCode"> </a-input>
</a-tab-pane> <div class="validate-image">
</a-tabs> <img v-if="requestCodeSuccess" :src="randCodeImage" @click="handleChangeCheckCode" />
<img v-else src="@/assets/checkcode.png" @click="handleChangeCheckCode" />
<a-form-model-item> </div>
<a-checkbox @change="handleRememberMeChange" default-checked>自动登录</a-checkbox>
</a-form-model-item> </a-form-model-item>
<a-form-item style="margin-top:24px"> <!-- 验证码结束 -->
<a-button size="large" type="primary" htmlType="submit" class="login-button" :loading="loginBtn" @click.stop.prevent="handleSubmit" :disabled="loginBtn">确定 <!-- 记住密码 -->
</a-button> <div class="remember-pwd">
</a-form-item> <a-checkbox v-model="model.rememberPwd">Remember password</a-checkbox>
</div>
<!-- 记住密码结束 -->
<a-button class="login-button" :loading="isSubmitting" @click="handleSubmit"> SIGN IN </a-button>
</a-form-model> </a-form-model>
<login-select-tenant ref="loginSelect" @success="loginSelectOk"></login-select-tenant> </div>
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import { ACCESS_TOKEN, ENCRYPTED_STRING } from '@/store/mutation-types' import { ACCESS_TOKEN } from '@/store/mutation-types'
import ThirdLogin from './third/ThirdLogin'
import LoginSelectTenant from './LoginSelectTenant'
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { getEncryptedString } from '@/utils/encryption/aesEncrypt'
import { timeFix } from '@/utils/util' import { timeFix } from '@/utils/util'
import { getAction } from '@/api/manage'
import LoginAccount from './LoginAccount' import { mapActions } from 'vuex'
import LoginPhone from './LoginPhone'
export default { export default {
components: {
LoginSelectTenant,
TwoStepCaptcha,
ThirdLogin,
LoginAccount,
LoginPhone
},
data() { data() {
return { return {
customActiveKey: 'tab1', isSubmitting: false,
rememberMe: true, randCodeImage: '',
loginBtn: false, requestCodeSuccess: false,
requiredTwoStepCaptcha: false, model: {
stepCaptchaVisible: false, username: '',
encryptedString:{ password: '',
key:"", inputCode: '',
iv:"", rememberPwd: true
}, },
rules: {
username: [{ required: true, message: 'Username Required' }],
password: [
{
required: true,
message: 'Password Required'
},
],
inputCode: [
{
required: true,
message: 'Captchar Required',
},
],
},
currdatetime: '',
} }
}, },
created() { created() {
Vue.ls.remove(ACCESS_TOKEN) Vue.ls.remove(ACCESS_TOKEN)
this.getRouterData(); this.getRouterData()
this.rememberMe = true this.handleChangeCheckCode()
}, },
methods: { methods: {
handleTabClick(key){ ...mapActions(['Login']),
this.customActiveKey = key
},
handleRememberMeChange(e){
this.rememberMe = e.target.checked
},
/**跳转到登录页面的参数-账号获取*/ /**跳转到登录页面的参数-账号获取*/
getRouterData() { getRouterData() {
this.$nextTick(() => { this.$nextTick(() => {
@ -80,126 +88,221 @@ export default {
}) })
}, },
// /**刷新验证码*/
handleSubmit () { handleChangeCheckCode() {
this.loginBtn = true; this.currdatetime = new Date().getTime()
if (this.customActiveKey === 'tab1') { this.model.inputCode = ''
// 使 getAction(`/sys/randomImage/${this.currdatetime}`)
this.$refs.alogin.handleLogin(this.rememberMe) .then((res) => {
if (res.success) {
this.randCodeImage = res.result
this.requestCodeSuccess = true
} else { } else {
// this.$message.error(res.message)
this.$refs.plogin.handleLogin(this.rememberMe) this.requestCodeSuccess = false
}
})
.catch(() => {
this.requestCodeSuccess = false
})
},
//
async handleSubmit() {
if (this.isSubmitting) {
return
}
try {
await this.$refs.formRef.validate()
try {
const loginParams = {
username: this.model.username,
password: this.model.password,
captcha: this.model.inputCode,
checkKey: this.currdatetime,
}
this.isSubmitting = true
const res = await this.Login(loginParams)
this.loginSuccess()
} catch (error) {
if (error && error.code === 412) {
this.handleChangeCheckCode()
}
this.requestFailed(error)
}
} catch (error) {
console.error(error)
} }
}, },
//
validateFail(){ //
this.loginBtn = false;
},
//
requestSuccess(loginResult){
this.$refs.loginSelect.show(loginResult)
},
//
requestFailed(err) { requestFailed(err) {
let description = ((err.response || {}).data || {}).message || err.message || "请求出现错误,请稍后再试" let description = ((err.response || {}).data || {}).message || err.message || 'Request Error'
this.$notification['error']({ this.$notification['error']({
message: '登录失败', message: 'Login Failed',
description: description, description: description,
duration: 4, duration: 4,
}); })
// //
if(this.customActiveKey === 'tab1' && description.indexOf('密码错误')>0){ this.handleChangeCheckCode()
this.$refs.alogin.handleChangeCheckCode() this.isSubmitting = false
}
this.loginBtn = false;
},
loginSelectOk(){
this.loginSuccess()
}, },
// //
loginSuccess() { loginSuccess() {
this.$router.push({ path: "/station-operation" }).catch(()=>{ this.$router.push({ path: '/station-operation' }).catch(() => {
console.log('登录跳转首页出错,这个错误从哪里来的') console.log('登录跳转首页出错,这个错误从哪里来的')
}) })
this.$notification.success({ this.$notification.success({
message: '欢迎', message: 'Welcome',
description: `${timeFix()},欢迎回来`, description: `${timeFix()}Welcome Back`,
});
},
stepCaptchaSuccess () {
this.loginSuccess()
},
stepCaptchaCancel () {
this.Logout().then(() => {
this.loginBtn = false
this.stepCaptchaVisible = false
}) })
}, },
// },
getEncrypte(){
var encryptedString = Vue.ls.get(ENCRYPTED_STRING);
if(encryptedString == null){
getEncryptedString().then((data) => {
this.encryptedString = data
});
}else{
this.encryptedString = encryptedString;
}
}
}
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.user-layout-login { .login-page {
label { height: 100%;
font-size: 14px; background: url(~@/assets/images/login/bg.jpg) center no-repeat;
} background-size: cover;
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.forge-password { .login-form {
font-size: 14px; position: fixed;
} top: 50%;
right: 295px;
transform: translateY(-50%);
button.login-button { .logo {
padding: 0 15px; img {
font-size: 16px; width: 578px;
height: 40px; height: 233px;
width: 100%;
}
.user-login-other {
text-align: left;
margin-top: 24px;
line-height: 22px;
.item-icon {
font-size: 24px;
color: rgba(0,0,0,.2);
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
transition: color .3s;
&:hover {
color: #1890ff;
} }
} }
.register { .ant-form {
margin-right: 24px;
float: right; float: right;
&-item {
margin-bottom: 20px !important;
::v-deep {
.ant-form-item-label {
line-height: 17px;
label {
font-size: 22px;
color: #fff;
padding-left: 6px;
&::before {
display: none;
}
}
}
.ant-form-explain {
position: absolute;
right: 0;
top: -36px;
font-size: 18px;
}
.ant-form-item-children {
width: 444px;
height: 60px;
.ant-input {
padding-left: 70px;
padding-right: 15px;
height: 100%;
font-size: 26px;
background-color: transparent !important;
border: none;
}
}
}
&.username {
::v-deep {
.ant-form-item-children {
background: url(~@/assets/images/login/username-bg.png) center no-repeat;
background-size: contain;
}
}
}
&.password {
::v-deep {
.ant-form-item-children {
background: url(~@/assets/images/login/pwd-bg.png) center no-repeat;
background-size: contain;
}
}
}
&.validate-code {
width: 444px;
::v-deep {
.ant-form-item-children {
display: flex;
justify-content: space-between;
.ant-input {
width: 294px;
background: url(~@/assets/images/login/validate-bg.png) center no-repeat;
background-size: contain;
}
.validate-image {
width: 135px;
height: 60px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
}
.remember-pwd {
user-select: none;
margin-top: -20px;
margin-left: 5px;
.ant-checkbox-wrapper {
color: #6ebad0;
}
}
.login-button {
width: 444px;
height: 76px;
margin-top: 44px;
background: url(~@/assets/images/login/login-btn.png) center no-repeat;
border: 0;
padding-bottom: 15px;
::v-deep {
.anticon-loading {
font-size: 22px;
}
span {
font-family: MicrogrammaD-MediExte;
font-size: 22px;
font-weight: bold;
color: #fff;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.61);
}
}
&:focus,
&.ant-btn-loading {
background-image: url(~@/assets/images/login/login-btn-active.png);
}
&::before,
&::after {
display: none;
}
}
} }
} }
} }
</style> </style>
<style>
.valid-error .ant-select-selection__placeholder{
color: #f5222d;
}
</style>