云查询管理、新登陆页面
parent
f2b0837ead
commit
445ecde28f
Binary file not shown.
|
After Width: | Height: | Size: 415 B |
Binary file not shown.
|
After Width: | Height: | Size: 888 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 311 B |
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 439 B |
Binary file not shown.
|
After Width: | Height: | Size: 695 B |
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { defHttp } from '@/utils/http/axios';
|
||||||
|
enum Api{
|
||||||
|
getLeftTree = '/api/SysDataItemDetail/Load',
|
||||||
|
addLeftItem = '/api/SysDataItemDetail/Add',
|
||||||
|
delLeftItem = '/api/SysDataItemDetail/Delete',
|
||||||
|
editLeftItem = '/api/SysDataItemDetail/Update',
|
||||||
|
LoadPage = '/api/DroneCloudQueryContent/LoadPage',
|
||||||
|
Get = '/api/DroneCloudQueryContent/Get',
|
||||||
|
Add = '/api/DroneCloudQueryContent/Add',
|
||||||
|
Update = '/api/DroneCloudQueryContent/Update',
|
||||||
|
Delete = '/api/DroneCloudQueryContent/Delete',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分类
|
||||||
|
export function getLeftTree(params) {
|
||||||
|
return defHttp.get({ url: Api.getLeftTree, params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLeftItem(params){
|
||||||
|
return defHttp.post({ url: Api.addLeftItem + '?code=' + params.itemCode, params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delLeftItem(params){
|
||||||
|
return defHttp.post({ url: Api.delLeftItem + '?id=' + params.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 云查询管理
|
||||||
|
export function LoadPage(params) {
|
||||||
|
return defHttp.get({ url: Api.LoadPage, params });
|
||||||
|
}
|
||||||
|
export function Get(params) {
|
||||||
|
return defHttp.get({ url: Api.Get, params });
|
||||||
|
}
|
||||||
|
export function Add(params) {
|
||||||
|
return defHttp.post({ url: Api.Add, params });
|
||||||
|
}
|
||||||
|
export function Update(params) {
|
||||||
|
return defHttp.post({ url: Api.Update, params });
|
||||||
|
}
|
||||||
|
export function Delete(params) {
|
||||||
|
return defHttp.post({ url: Api.Delete + '?id=' + params.id , params });
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,8 @@ export const RootRoute: AppRouteRecordRaw = {
|
||||||
export const LoginRoute: AppRouteRecordRaw = {
|
export const LoginRoute: AppRouteRecordRaw = {
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
component: () => import('@/views/sys/login/Login.vue'),
|
// component: () => import('@/views/sys/login/Login.vue'),
|
||||||
|
component: () => import('@/views/sys/login_lindidiaocha/Login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: t('routes.basic.login'),
|
title: t('routes.basic.login'),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
<template>
|
||||||
|
<div class="categories-modal-container">
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="props.modalData.data"
|
||||||
|
:rules="props.modalData.type.indexOf('tree') > -1 ? treeRules : rightRules"
|
||||||
|
labelAlign="right"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
>
|
||||||
|
<template v-if="props.modalData.type.indexOf('tree') > -1">
|
||||||
|
<a-form-item label="名称" name="itemName">
|
||||||
|
<a-input v-model:value="props.modalData.data.itemName" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="类型" name="itemValue">
|
||||||
|
<a-select v-model:value="props.modalData.data.itemValue">
|
||||||
|
<a-select-option value="vector_data">矢量数据</a-select-option>
|
||||||
|
<a-select-option value="imgage_data">影像数据</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-form-item label="ID" name="id" v-if="false">
|
||||||
|
<a-input v-model:value="props.modalData.data.id" disabled />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="名称" name="name">
|
||||||
|
<a-input v-model:value="props.modalData.data.name" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="图层" name="layer">
|
||||||
|
<a-select v-model:value="props.modalData.data.layer">
|
||||||
|
<a-select-option value="1">图层1</a-select-option>
|
||||||
|
<a-select-option value="2">图层2</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="数据表" name="tableName">
|
||||||
|
<a-select v-model:value="props.modalData.data.tableName">
|
||||||
|
<a-select-option value="1">数据表1</a-select-option>
|
||||||
|
<a-select-option value="2">数据表2</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="叠加图层" name="overlayList">
|
||||||
|
<a-select v-model:value="props.modalData.data.overlayList" mode="multiple">
|
||||||
|
<a-select-option value="1" checked>天地图</a-select-option>
|
||||||
|
<a-select-option value="2">图层2</a-select-option>
|
||||||
|
<a-select-option value="3">图层3</a-select-option>
|
||||||
|
<a-select-option value="4">图层4</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="是否分类" name="isClass">
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="props.modalData.data.isClass"
|
||||||
|
checked-children="是"
|
||||||
|
un-checked-children="否"
|
||||||
|
checked-value="1"
|
||||||
|
un-checked-value="0"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="分类字段" name="classField" v-if="props.modalData.data.isClass">
|
||||||
|
<a-select v-model:value="props.modalData.data.classField">
|
||||||
|
<a-select-option value="1">字段1</a-select-option>
|
||||||
|
<a-select-option value="2">字段2</a-select-option>
|
||||||
|
<a-select-option value="3">字段3</a-select-option>
|
||||||
|
<a-select-option value="4">字段4</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="分类名称" name="className" v-if="props.modalData.data.isClass">
|
||||||
|
<a-input v-model:value="props.modalData.data.className" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="选择面积计算字段" name="areaField">
|
||||||
|
<a-select v-model:value="props.modalData.data.areaField">
|
||||||
|
<a-select-option value="1">面积1</a-select-option>
|
||||||
|
<a-select-option value="2">面积2</a-select-option>
|
||||||
|
<a-select-option value="3">面积3</a-select-option>
|
||||||
|
<a-select-option value="4">面积4</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="是否合计" name="isSum">
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="props.modalData.data.isSum"
|
||||||
|
checked-children="是"
|
||||||
|
un-checked-children="否"
|
||||||
|
checked-value="1"
|
||||||
|
un-checked-value="0"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a-button style="margin-right: 10px" @click="closeModal">取消</a-button>
|
||||||
|
<a-button type="primary" @click="submit">确定</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits, ref } from 'vue';
|
||||||
|
const props = defineProps(['modalData', 'modalForm']);
|
||||||
|
const emit = defineEmits(['closeModal', 'submit']);
|
||||||
|
const formRef = ref();
|
||||||
|
const treeRules = {
|
||||||
|
itemName: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||||
|
itemValue: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
|
||||||
|
};
|
||||||
|
const rightRules = {
|
||||||
|
itemName: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
|
||||||
|
itemCode: [{ required: true, message: '所属分类不能为空', trigger: 'blur' }],
|
||||||
|
};
|
||||||
|
const submit = () => {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
emit('submit');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log('error', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const closeModal = () => {
|
||||||
|
emit('closeModal');
|
||||||
|
formRef.value.resetFields();
|
||||||
|
};
|
||||||
|
const clearModalData = () => {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
};
|
||||||
|
defineExpose({
|
||||||
|
clearModalData,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.categories-modal-container {
|
||||||
|
padding: 10px 25px;
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
<template>
|
||||||
|
<div class="page-div categories-page">
|
||||||
|
<div class="tree-div w-1/5 xl:w-1/6 m-4 mr-0">
|
||||||
|
<div class="header">
|
||||||
|
<div class="buttons-div">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
color="info"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
@click="buttonClick('treeAdd')"
|
||||||
|
>
|
||||||
|
添加分类
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" color="error" @click="buttonClick('treeDel')">
|
||||||
|
删除分类
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="showTree">
|
||||||
|
<BasicTree
|
||||||
|
ref="treeRef"
|
||||||
|
:treeData="showLTree"
|
||||||
|
:loading="lLoading"
|
||||||
|
:fieldNames="{ key: 'itemValue', title: 'itemName' }"
|
||||||
|
@select="
|
||||||
|
(selectedKeys, { node }) => {
|
||||||
|
getLeftSelectId(node.itemDetailId);
|
||||||
|
changeTypeId(node.key);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-div w-4/5 xl:w-5/6">
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<template #toolbar>
|
||||||
|
<PermissionBtn @btnEvent="buttonClick"></PermissionBtn>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'enabledMark'">
|
||||||
|
<a-tag v-if="record.enabledMark" color="success">正常</a-tag>
|
||||||
|
<a-tag v-else color="error">停用</a-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
<a-modal
|
||||||
|
class="categories-modal"
|
||||||
|
v-model:open="openModal"
|
||||||
|
:title="modalData.title"
|
||||||
|
:afterClose="clearModal"
|
||||||
|
>
|
||||||
|
<UseModal ref="modalForm" :modalData="modalData" @closeModal="closeModal" @submit="submit" />
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||||
|
import UseModal from './Modal/index.vue';
|
||||||
|
import { BasicTree } from '@/components/Tree';
|
||||||
|
import { BasicTable, useTable } from '@/components/Table';
|
||||||
|
import { PermissionBtn } from '@/components/PermissionBtn/index';
|
||||||
|
import { ref, onMounted, reactive, watch, createVNode, unref } from 'vue';
|
||||||
|
import {
|
||||||
|
getLeftTree,
|
||||||
|
addLeftItem,
|
||||||
|
delLeftItem,
|
||||||
|
LoadPage,
|
||||||
|
Add,
|
||||||
|
Update,
|
||||||
|
Delete,
|
||||||
|
} from '@/api/sys/cloud';
|
||||||
|
import { columns, emptyRightItem, emptyLeftItem, searchFormSchema } from './util';
|
||||||
|
import { Modal, message } from 'ant-design-vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const modalForm = ref();
|
||||||
|
let lLoading = ref(false);
|
||||||
|
let typeId = ref('');
|
||||||
|
let selectTreeId = ref('');
|
||||||
|
let showLTree = ref([]);
|
||||||
|
let openModal = ref(false);
|
||||||
|
const modalData = reactive({
|
||||||
|
title: '',
|
||||||
|
data: {},
|
||||||
|
type: '',
|
||||||
|
});
|
||||||
|
const treeRef = ref();
|
||||||
|
const firstRequestCode = ref('');
|
||||||
|
|
||||||
|
const [registerTable, { reload, getSelectRows }] = useTable({
|
||||||
|
// beforeFetch: (params) => {
|
||||||
|
// console.log(firstRequestCode.value, 'before');
|
||||||
|
// if (firstRequestCode.value !== '') {
|
||||||
|
// params = { ...params, code: firstRequestCode.value };
|
||||||
|
// } else {
|
||||||
|
// params = { ...params, code: typeId.value };
|
||||||
|
// }
|
||||||
|
// return params;
|
||||||
|
// },
|
||||||
|
api: LoadPage,
|
||||||
|
columns,
|
||||||
|
formConfig: {
|
||||||
|
labelWidth: 120,
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
},
|
||||||
|
showIndexColumn: false,
|
||||||
|
rowSelection: {
|
||||||
|
type: 'radio',
|
||||||
|
},
|
||||||
|
useSearchForm: true,
|
||||||
|
bordered: true,
|
||||||
|
showTableSetting: true,
|
||||||
|
handleSearchInfoFn(info) {
|
||||||
|
return info;
|
||||||
|
},
|
||||||
|
immediate: false,
|
||||||
|
afterFetch(res) {
|
||||||
|
firstRequestCode.value = '';
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const getLeftTreeData = (isMounted) => {
|
||||||
|
lLoading.value = true;
|
||||||
|
getLeftTree({ code: 'cloudQueryManagement' })
|
||||||
|
.then((res) => {
|
||||||
|
showLTree.value = res;
|
||||||
|
lLoading.value = false;
|
||||||
|
if (isMounted && res.length > 0) {
|
||||||
|
firstRequestCode.value = res[0].key;
|
||||||
|
typeId.value = res[0].key;
|
||||||
|
unref(treeRef).setSelectedKeys([res[0].key]);
|
||||||
|
}
|
||||||
|
reload();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
lLoading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
getLeftTreeData(true);
|
||||||
|
});
|
||||||
|
const changeTypeId = (value) => {
|
||||||
|
typeId.value = value;
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
const getLeftSelectId = (value) => {
|
||||||
|
selectTreeId.value = value;
|
||||||
|
};
|
||||||
|
const submit = () => {
|
||||||
|
let userName = localStorage.getItem('fireUserLoginName');
|
||||||
|
switch (modalData.type) {
|
||||||
|
case 'treeAdd':
|
||||||
|
addLeftItem({
|
||||||
|
...modalData.data,
|
||||||
|
createUserName: userName,
|
||||||
|
modifyUserName: userName,
|
||||||
|
createDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
modifyDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
enabledMark: 1,
|
||||||
|
sortCode: showLTree.value.length,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
getLeftTreeData(false);
|
||||||
|
openModal.value = false;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'btnAdd':
|
||||||
|
Add({
|
||||||
|
...modalData.data,
|
||||||
|
createUserName: userName,
|
||||||
|
modifyUserName: userName,
|
||||||
|
createDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
modifyDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
reload();
|
||||||
|
openModal.value = false;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'btnEdit':
|
||||||
|
Update({
|
||||||
|
...modalData.data,
|
||||||
|
modifyUserName: userName,
|
||||||
|
modifyDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
reload();
|
||||||
|
openModal.value = false;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const buttonClick = (diffType) => {
|
||||||
|
let check = false;
|
||||||
|
switch (diffType) {
|
||||||
|
case 'treeAdd':
|
||||||
|
openModal.value = true;
|
||||||
|
modalData.title = '添加分组';
|
||||||
|
modalData.data = emptyLeftItem;
|
||||||
|
modalData.type = diffType;
|
||||||
|
break;
|
||||||
|
case 'treeDel':
|
||||||
|
check = selectTreeId.value === '' ? true : false;
|
||||||
|
if (check) {
|
||||||
|
message.warning('请选择一条分组数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: '是否确认删除?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
onCancel() {},
|
||||||
|
onOk() {
|
||||||
|
return delLeftItem({ id: selectTreeId.value })
|
||||||
|
.then((res) => {
|
||||||
|
getLeftTreeData(false);
|
||||||
|
typeId.value = '';
|
||||||
|
reload();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'btnAdd':
|
||||||
|
openModal.value = true;
|
||||||
|
modalData.title = '添加';
|
||||||
|
modalData.data = emptyRightItem;
|
||||||
|
modalData.type = diffType;
|
||||||
|
break;
|
||||||
|
case 'btnDelete':
|
||||||
|
check = getSelectRows().length !== 1 ? true : false;
|
||||||
|
if (check) {
|
||||||
|
message.warning('请选择一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: '是否确认删除?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
onCancel() {},
|
||||||
|
onOk() {
|
||||||
|
console.log(getSelectRows()[0]);
|
||||||
|
|
||||||
|
let id = getSelectRows()[0].id;
|
||||||
|
return Delete({ id: id })
|
||||||
|
.then((res) => {
|
||||||
|
reload();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'btnEdit':
|
||||||
|
if (getSelectRows().length !== 1) {
|
||||||
|
message.warning('请选择一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let editData = getSelectRows()[0];
|
||||||
|
console.log(editData);
|
||||||
|
modalData.title = '编辑';
|
||||||
|
modalData.data = { ...editData };
|
||||||
|
modalData.type = diffType;
|
||||||
|
openModal.value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const closeModal = () => {
|
||||||
|
openModal.value = false;
|
||||||
|
};
|
||||||
|
const clearModal = () => {
|
||||||
|
modalForm.value.clearModalData();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-div {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: calc(100% - 20px);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.tree-div {
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
.buttons-div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
.search-div {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
.showTree {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-div {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.categories-page {
|
||||||
|
.tree-div {
|
||||||
|
.h-full {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.search-div {
|
||||||
|
.ant-btn-primary {
|
||||||
|
background-color: #2a7dc9;
|
||||||
|
}
|
||||||
|
.ant-input-search-button {
|
||||||
|
width: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.categories-modal {
|
||||||
|
.ant-modal-footer > button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ant-modal-footer {
|
||||||
|
margin-top: 0px;
|
||||||
|
border-top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ant-table-header
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
resizable: true,
|
||||||
|
ifShow: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '图层',
|
||||||
|
dataIndex: 'layer',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数据表',
|
||||||
|
dataIndex: 'tableName',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '叠加图层',
|
||||||
|
dataIndex: 'overlayList',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否分类',
|
||||||
|
dataIndex: 'isClass',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类字段',
|
||||||
|
dataIndex: 'classField',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类名称',
|
||||||
|
dataIndex: 'className',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '选择面积计算字段',
|
||||||
|
dataIndex: 'areaField',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否合计',
|
||||||
|
dataIndex: 'isSum',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
export const emptyLeftItem = {
|
||||||
|
itemName: '',
|
||||||
|
itemValue: '',
|
||||||
|
};
|
||||||
|
export const emptyRightItem = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
layer: '',
|
||||||
|
tableName: '',
|
||||||
|
overlayList: ['1'],
|
||||||
|
isClass: 0,
|
||||||
|
classField: '',
|
||||||
|
className: '',
|
||||||
|
areaField:'',
|
||||||
|
isSum: 0,
|
||||||
|
};
|
||||||
|
export const searchFormSchema = [
|
||||||
|
{
|
||||||
|
field: 'key',
|
||||||
|
label: '关键字',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 8 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,405 @@
|
||||||
|
<template>
|
||||||
|
<div style="padding: 24px; overflow-y: hidden">
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="imageBoundary" tab="影像边界shp">
|
||||||
|
<div class="flex-column">
|
||||||
|
<a-row style="overflow-y: hidden">
|
||||||
|
<a-col :span="12" style="padding: 20px; border: 1px silver solid; margin-left: 10px">
|
||||||
|
<a-form
|
||||||
|
ref="form"
|
||||||
|
v-modal:model="currentAppForm"
|
||||||
|
labelAlign="right"
|
||||||
|
label-width="80px"
|
||||||
|
:label-col="labelCol"
|
||||||
|
:wrapper-col="wrapperCol"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<a-form-item label="当前版本">
|
||||||
|
<span style="font-size: 15px; font-weight: bold; color: #000">
|
||||||
|
<a-input v-model:value="currentAppInfo.edition" :bordered="false" />
|
||||||
|
</span>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="版本号">
|
||||||
|
<a-input-number v-model:value="currentAppForm.edition" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="项目名称">
|
||||||
|
<a-input
|
||||||
|
v-model:value="currentAppForm.project_name"
|
||||||
|
defaultValue="drone_enforcement"
|
||||||
|
disabled="true"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="上传">
|
||||||
|
<a-upload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
:name="imageBoundary_name"
|
||||||
|
:multiple="true"
|
||||||
|
:customRequest="handleCustomRequest"
|
||||||
|
:headers="headers"
|
||||||
|
:progress="progress"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
>
|
||||||
|
<a-button :loading="imageBoundary_loading"> 点击上传 </a-button>
|
||||||
|
</a-upload>
|
||||||
|
{{ fileName }}
|
||||||
|
<DeleteOutlined v-if="fileName != ''" @click="deleteFileName" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="描述信息">
|
||||||
|
<a-textarea
|
||||||
|
type="textarea"
|
||||||
|
v-model:value="currentAppForm.description"
|
||||||
|
:rows="4"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="是否必须更新" label-width="120px">
|
||||||
|
<a-radio-group v-model:value="currentAppForm.must_update" size="medium">
|
||||||
|
<a-radio :bordered="false" :value="1">是</a-radio>
|
||||||
|
<a-radio :bordered="false" :value="0">否</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :wrapper-col="{ offset: 10, span: 14 }">
|
||||||
|
<a-button type="primary" @click="onSubmit">立即创建</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="imageRelease" tab="影像发布事件">
|
||||||
|
<div class="flex-column">
|
||||||
|
<a-row style="overflow-y: hidden">
|
||||||
|
<a-col :span="12" style="padding: 20px; border: 1px silver solid; margin-left: 10px">
|
||||||
|
<a-form
|
||||||
|
ref="fly_form"
|
||||||
|
v-model:model="fly_currentAppForm"
|
||||||
|
labelAlign="right"
|
||||||
|
label-width="80px"
|
||||||
|
:label-col="labelCol"
|
||||||
|
:wrapper-col="wrapperCol"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<a-form-item label="当前版本">
|
||||||
|
<span style="font-size: 15px; font-weight: bold; color: #000">
|
||||||
|
<a-input :bordered="false" />
|
||||||
|
</span>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="版本号">
|
||||||
|
<a-input-number v-model:value="fly_currentAppForm.edition" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="项目名称">
|
||||||
|
<a-input v-model:value="fly_currentAppForm.project_name" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="上传">
|
||||||
|
<a-upload
|
||||||
|
v-model:file-list="fly_fileList"
|
||||||
|
:name="imageRelease_name"
|
||||||
|
:multiple="true"
|
||||||
|
:customRequest="handleCustomRequest"
|
||||||
|
:headers="headers"
|
||||||
|
:progress="progress"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
>
|
||||||
|
<a-button :loading="imageRelease_loading"> 点击上传 </a-button>
|
||||||
|
</a-upload>
|
||||||
|
{{ fly_fileName }}
|
||||||
|
<DeleteOutlined v-if="fileName != ''" @click="deleteFileName" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="描述信息">
|
||||||
|
<a-textarea
|
||||||
|
type="textarea"
|
||||||
|
v-model:value="fly_currentAppForm.description"
|
||||||
|
:rows="4"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="是否必须更新" label-width="120px">
|
||||||
|
<a-radio-group v-model:value="fly_currentAppForm.must_update" size="medium">
|
||||||
|
<a-radio :bordered="false" :value="1">是</a-radio>
|
||||||
|
<a-radio :bordered="false" :value="0">否</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :wrapper-col="{ offset: 10, span: 14 }">
|
||||||
|
<a-button type="primary" @click="onFlyControlSubmit">立即创建</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="history" tab="历史记录">
|
||||||
|
<div>
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<template #toolbar>
|
||||||
|
<a-button type="primary" @click="updateimage">更新最新影像</a-button>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'enabledMark'">
|
||||||
|
<a-tag v-if="record.enabledMark" color="success">正常</a-tag>
|
||||||
|
<a-tag v-else color="error">停用</a-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// vue
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
// vben
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage';
|
||||||
|
import { DeleteOutlined, UploadOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { message, Upload } from 'ant-design-vue';
|
||||||
|
import type { UploadProps } from 'ant-design-vue';
|
||||||
|
import { BasicTable, useTable } from '@/components/Table';
|
||||||
|
import { useGlobSetting } from '@/hooks/setting';
|
||||||
|
import { columns, searchFormSchema } from './util';
|
||||||
|
// api
|
||||||
|
import { fun_GetUpdateFiles, fun_AddAppFiles } from '@/api/demo/version';
|
||||||
|
import { fun_Load, fun_Upload } from '@/api/demo/files';
|
||||||
|
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const { apiUrl } = useGlobSetting();
|
||||||
|
|
||||||
|
// 表单栅格
|
||||||
|
const labelCol = { span: 6 };
|
||||||
|
const wrapperCol = { span: 18 };
|
||||||
|
// 影像边界shp-当前版本信息
|
||||||
|
let currentAppInfo = ref({});
|
||||||
|
// 标签页
|
||||||
|
let activeKey = ref('imageBoundary');
|
||||||
|
// 上传文件列表
|
||||||
|
let fileList: any = ref([]);
|
||||||
|
let fly_fileList: any = ref([]);
|
||||||
|
const imageBoundary_loading = ref<boolean>(false);
|
||||||
|
const imageRelease_loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const imageBoundary_name = ref<string>('');
|
||||||
|
const imageRelease_name = ref<string>('');
|
||||||
|
// 上传的文件
|
||||||
|
const fileName = ref('');
|
||||||
|
const fly_fileName = ref('');
|
||||||
|
let filePath = '';
|
||||||
|
let fly_filePath = '';
|
||||||
|
|
||||||
|
// 影像边界shp
|
||||||
|
const currentAppForm = ref({
|
||||||
|
edition: '',
|
||||||
|
description: '',
|
||||||
|
filepath: '',
|
||||||
|
must_update: '',
|
||||||
|
project_name: '',
|
||||||
|
createuser: '',
|
||||||
|
createtime: '',
|
||||||
|
});
|
||||||
|
// 影像发布事件
|
||||||
|
const fly_currentAppForm = ref({
|
||||||
|
edition: '',
|
||||||
|
description: '',
|
||||||
|
filepath: '',
|
||||||
|
must_update: '',
|
||||||
|
project_name: '',
|
||||||
|
createuser: '',
|
||||||
|
createtime: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 版本
|
||||||
|
async function getAppInfo() {
|
||||||
|
fun_GetUpdateFiles({ project: 'drone_enforcement' }).then(async (res) => {
|
||||||
|
if (res) {
|
||||||
|
currentAppForm.value.project_name = res.project_name;
|
||||||
|
currentAppForm.value.description = res.description;
|
||||||
|
currentAppInfo.value = res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件接口
|
||||||
|
const handleCustomRequest = (file, progress) => {
|
||||||
|
if (activeKey.value === 'imageBoundary') {
|
||||||
|
fileList.value = [];
|
||||||
|
fun_Upload(file, progress)
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.data.result.length > 0) {
|
||||||
|
fileName.value = res.data.result[0].fileName;
|
||||||
|
filePath = res.data.result[0].filePath;
|
||||||
|
imageBoundary_loading.value = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
imageBoundary_loading.value = false;
|
||||||
|
file.onError(err);
|
||||||
|
createMessage.error(err.message);
|
||||||
|
});
|
||||||
|
} else if (activeKey.value === 'imageRelease') {
|
||||||
|
fly_fileList.value = [];
|
||||||
|
fun_Upload(file, progress)
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.data.result.length > 0) {
|
||||||
|
fly_fileName.value = res.data.result[0].fileName;
|
||||||
|
fly_filePath = res.data.result[0].filePath;
|
||||||
|
imageRelease_loading.value = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
imageRelease_loading.value = false;
|
||||||
|
file.onError(err);
|
||||||
|
createMessage.error(err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上传之前
|
||||||
|
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||||
|
let extension = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||||
|
let size = file.size / 1024 / 1024;
|
||||||
|
const isApk = extension === 'apk';
|
||||||
|
const suitableSize = size < 100;
|
||||||
|
|
||||||
|
if (activeKey.value === 'imageBoundary') {
|
||||||
|
imageBoundary_name.value = file.name;
|
||||||
|
} else {
|
||||||
|
imageRelease_name.value = file.name;
|
||||||
|
}
|
||||||
|
if (!isApk) {
|
||||||
|
createMessage.error('只能上传后缀是.apk的文件');
|
||||||
|
}
|
||||||
|
if (!suitableSize) {
|
||||||
|
createMessage.error('文件大小不得超过100M');
|
||||||
|
}
|
||||||
|
if (isApk && suitableSize) {
|
||||||
|
if (activeKey.value === 'imageBoundary') {
|
||||||
|
imageBoundary_loading.value = true;
|
||||||
|
} else {
|
||||||
|
imageRelease_loading.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (isApk && suitableSize) || Upload.LIST_IGNORE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
function deleteFileName() {
|
||||||
|
// 影像边界shp-删除文件
|
||||||
|
if (activeKey.value === 'imageBoundary') {
|
||||||
|
fileName.value = '';
|
||||||
|
filePath = '';
|
||||||
|
fileList.value = [];
|
||||||
|
} else {
|
||||||
|
// 影像发布事件-删除文件
|
||||||
|
fly_fileName.value = '';
|
||||||
|
fly_filePath = '';
|
||||||
|
fly_fileList.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 影像边界shp-立即创建
|
||||||
|
function onSubmit() {
|
||||||
|
if (fileName.value == '' || filePath == '') {
|
||||||
|
createMessage.warn(`请选择上传文件!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentAppForm.value.edition == '') {
|
||||||
|
createMessage.warn(`请先填写版本号!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentAppForm.value.project_name != 'drone_enforcement') {
|
||||||
|
createMessage.warn(`请不要修改项目名称!`);
|
||||||
|
currentAppForm.value.project_name = 'drone_enforcement';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fun_Load({ key: fileName.value }).then((res) => {
|
||||||
|
if (res.items.length > 0) {
|
||||||
|
currentAppForm.value.filepath = res.items[0].filePath;
|
||||||
|
fun_AddAppFiles(currentAppForm.value).then((res2) => {
|
||||||
|
if (res2) {
|
||||||
|
createMessage.success(`上传成功!`);
|
||||||
|
currentAppForm.value = {
|
||||||
|
createtime: '',
|
||||||
|
createuser: '',
|
||||||
|
edition: '',
|
||||||
|
description: '',
|
||||||
|
filepath: '',
|
||||||
|
must_update: '',
|
||||||
|
project_name: '',
|
||||||
|
};
|
||||||
|
fileList.value = [];
|
||||||
|
getAppInfo();
|
||||||
|
} else {
|
||||||
|
createMessage.warn(`接口错误!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createMessage.warn(`上传文件出现错误!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 影像发布事件-立即创建
|
||||||
|
function onFlyControlSubmit() {
|
||||||
|
if (fly_fileName.value == '' || fly_filePath == '') {
|
||||||
|
createMessage.warn(`请选择上传文件!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fly_currentAppForm.value.edition == '') {
|
||||||
|
createMessage.warn(`请先填写版本号!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fly_currentAppForm.value.project_name == '') {
|
||||||
|
createMessage.warn(`请先填写项目名称!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fun_Load({ key: fly_fileName.value }).then((res) => {
|
||||||
|
if (res.items.length > 0) {
|
||||||
|
fly_currentAppForm.value.filepath = res.items[0].filePath;
|
||||||
|
fun_AddAppFiles(fly_currentAppForm.value).then((res2) => {
|
||||||
|
if (res2) {
|
||||||
|
createMessage.success(`上传成功!`);
|
||||||
|
fly_currentAppForm.value = {
|
||||||
|
createtime: '',
|
||||||
|
createuser: '',
|
||||||
|
edition: '',
|
||||||
|
description: '',
|
||||||
|
filepath: '',
|
||||||
|
must_update: '',
|
||||||
|
project_name: '',
|
||||||
|
};
|
||||||
|
fly_fileList.value = [];
|
||||||
|
getAppInfo();
|
||||||
|
} else {
|
||||||
|
createMessage.warn(`接口错误!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createMessage.warn(`上传文件出现错误!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 历史记录
|
||||||
|
const [registerTable, { reload, getSelectRows }] = useTable({
|
||||||
|
// api: getRightTable,
|
||||||
|
columns,
|
||||||
|
formConfig: {
|
||||||
|
labelWidth: 120,
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
},
|
||||||
|
showIndexColumn: false,
|
||||||
|
rowSelection: {
|
||||||
|
type: 'radio',
|
||||||
|
},
|
||||||
|
useSearchForm: true,
|
||||||
|
bordered: true,
|
||||||
|
showTableSetting: true,
|
||||||
|
handleSearchInfoFn(info) {
|
||||||
|
return info;
|
||||||
|
},
|
||||||
|
immediate: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 版本
|
||||||
|
getAppInfo();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
resizable: true,
|
||||||
|
ifShow: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时间',
|
||||||
|
dataIndex: 'time',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '地址',
|
||||||
|
dataIndex: 'geoserver',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema = [
|
||||||
|
{
|
||||||
|
field: '[startTime, endTime]',
|
||||||
|
label: '日期范围',
|
||||||
|
component: 'RangePicker',
|
||||||
|
colProps: { span: 8 },
|
||||||
|
componentProps: {
|
||||||
|
format: 'YYYY-MM',
|
||||||
|
picker: 'month',
|
||||||
|
placeholder: ['开始日期', '结束日期'],
|
||||||
|
defaultValue: [dayjs(dayjs(), 'YYYY-MM-DD'), dayjs(dayjs(), 'YYYY-MM-DD')]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<template v-if="getShow">
|
||||||
|
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||||
|
<FormItem name="account" class="enter-x">
|
||||||
|
<Input
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.account"
|
||||||
|
:placeholder="t('sys.login.userName')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem name="mobile" class="enter-x">
|
||||||
|
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="sms" class="enter-x">
|
||||||
|
<CountdownInput
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.sms"
|
||||||
|
:placeholder="t('sys.login.smsCode')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem>
|
||||||
|
<Button class="logon" size="large" block @click="handleReset" :loading="loading">
|
||||||
|
{{ t('common.resetText') }}
|
||||||
|
</Button>
|
||||||
|
<Button size="large" block class="mt-4" @click="handleBackLogin">
|
||||||
|
{{ t('sys.login.backSignIn') }}
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, computed, unref } from 'vue';
|
||||||
|
import LoginFormTitle from './LoginFormTitle.vue';
|
||||||
|
import { Form, Input, Button } from 'ant-design-vue';
|
||||||
|
import { CountdownInput } from '@/components/CountDown';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useLoginState, useFormRules, LoginStateEnum } from './useLogin';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { handleBackLogin, getLoginState } = useLoginState();
|
||||||
|
const { getFormRules } = useFormRules();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
account: '',
|
||||||
|
mobile: '',
|
||||||
|
sms: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD);
|
||||||
|
|
||||||
|
async function handleReset() {
|
||||||
|
const form = unref(formRef);
|
||||||
|
if (!form) return;
|
||||||
|
await form.resetFields();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.logon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background: #0f915f;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 21px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<div class="login" style="background-color: #f0f0f0; width: 100%; height: 100%">
|
||||||
|
<div class="title">
|
||||||
|
<img src="/login_lindidiaocha/logo.png" />
|
||||||
|
<div class="title_span">
|
||||||
|
<a-col>
|
||||||
|
<a-row><span class="title_CN">临沂市自然资源综合监管平台</span></a-row>
|
||||||
|
<a-row>
|
||||||
|
<span class="title_EN">
|
||||||
|
Linyi City Natural Resources Comprehensive Supervision Platform
|
||||||
|
</span>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg">
|
||||||
|
<div
|
||||||
|
class="bg_login"
|
||||||
|
:style="{
|
||||||
|
height: `${isRegister ? 600 : 400}px`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<LoginForm />
|
||||||
|
<ForgetPasswordForm />
|
||||||
|
<RegisterForm />
|
||||||
|
<MobileForm />
|
||||||
|
<QrCodeForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
版权所有:临沂市自然资源和规划局 技术支持:山东慧创信息科技有限公司
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { unref } from 'vue';
|
||||||
|
import { AppDarkModeToggle, AppLocalePicker, AppLogo } from '@/components/Application';
|
||||||
|
import { useGlobSetting } from '@/hooks/setting';
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useLocaleStore } from '@/store/modules/locale';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import ForgetPasswordForm from './ForgetPasswordForm.vue';
|
||||||
|
import LoginForm from './LoginForm.vue';
|
||||||
|
import MobileForm from './MobileForm.vue';
|
||||||
|
import QrCodeForm from './QrCodeForm.vue';
|
||||||
|
import RegisterForm from './RegisterForm.vue';
|
||||||
|
import { LoginStateEnum, useLoginState } from './useLogin';
|
||||||
|
const { getLoginState } = useLoginState();
|
||||||
|
|
||||||
|
const isRegister = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER);
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
sessionTimeout: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const globSetting = useGlobSetting();
|
||||||
|
const { prefixCls } = useDesign('login');
|
||||||
|
const { t } = useI18n();
|
||||||
|
const localeStore = useLocaleStore();
|
||||||
|
const showLocale = localeStore.getShowPicker;
|
||||||
|
const title = computed(() => globSetting?.title ?? '');
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.login {
|
||||||
|
// 页面不能被选中
|
||||||
|
-webkit-user-select: none; /* Safari */
|
||||||
|
-moz-user-select: none; /* Firefox */
|
||||||
|
-ms-user-select: none; /* IE/Edge */
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
top: calc((25% - 230px));
|
||||||
|
left: 143px;
|
||||||
|
height: 99px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 99px;
|
||||||
|
margin-right: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_span {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_CN {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 50px;
|
||||||
|
color: #131313;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
&_EN {
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
height: 22px;
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #131313;
|
||||||
|
text-shadow: 0px 3px 7px rgba(0, 0, 0, 0.35);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
position: relative;
|
||||||
|
top: 50px;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 280px);
|
||||||
|
background-image: url('/login_lindidiaocha/bg.png');
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&_login {
|
||||||
|
position: relative;
|
||||||
|
left: 1170px;
|
||||||
|
width: 294.1px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
width: 628px;
|
||||||
|
height: 18px;
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
bottom: 26px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
left: calc(50% - 628px / 2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
<template>
|
||||||
|
<LoginFormTitle class="login_title" />
|
||||||
|
<Form
|
||||||
|
class="p-4 enter-x"
|
||||||
|
:model="formData"
|
||||||
|
:rules="getFormRules"
|
||||||
|
ref="formRef"
|
||||||
|
v-show="getShow"
|
||||||
|
@keypress.enter="handleLogin"
|
||||||
|
>
|
||||||
|
<FormItem name="account">
|
||||||
|
<Input
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.account"
|
||||||
|
placeholder="请输入账户名"
|
||||||
|
class="fix-auto-fill"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<img src="/public/login_lindidiaocha/account.png" />
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="password">
|
||||||
|
<InputPassword
|
||||||
|
size="large"
|
||||||
|
visibilityToggle
|
||||||
|
v-model:value="formData.password"
|
||||||
|
placeholder="密码"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<img src="/public/login_lindidiaocha/password.png" />
|
||||||
|
</template>
|
||||||
|
</InputPassword>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<ARow>
|
||||||
|
<ACol :span="12">
|
||||||
|
<FormItem>
|
||||||
|
<Checkbox v-model:checked="rememberMe" size="small">
|
||||||
|
{{ t('sys.login.rememberMe') }}
|
||||||
|
</Checkbox>
|
||||||
|
</FormItem>
|
||||||
|
</ACol>
|
||||||
|
<ACol :span="12">
|
||||||
|
<FormItem
|
||||||
|
:style="{
|
||||||
|
'text-align': 'right',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
|
||||||
|
{{ t('sys.login.forgetPassword') }}
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
</ACol>
|
||||||
|
</ARow>
|
||||||
|
|
||||||
|
<FormItem>
|
||||||
|
<Button size="large" block @click="handleLogin" :loading="loading" class="logon">
|
||||||
|
立即登录
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
<div class="bottom" v-if="getShow">
|
||||||
|
<Button type="link" block size="small" @click="setLoginState(LoginStateEnum.REGISTER)">
|
||||||
|
<template #icon>
|
||||||
|
<img src="/public/login_lindidiaocha/register.png" />
|
||||||
|
</template>
|
||||||
|
<span> 用户注册</span>
|
||||||
|
</Button>
|
||||||
|
|
|
||||||
|
<Button type="link" block size="small">
|
||||||
|
<template #icon>
|
||||||
|
<img src="/public/login_lindidiaocha/download.png" />
|
||||||
|
</template>
|
||||||
|
<span> 下载APP</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, unref, computed } from 'vue';
|
||||||
|
|
||||||
|
import { Checkbox, Form, Input, Row, Col, Button, Divider } from 'ant-design-vue';
|
||||||
|
// import {
|
||||||
|
// GithubFilled,
|
||||||
|
// WechatFilled,
|
||||||
|
// AlipayCircleFilled,
|
||||||
|
// GoogleCircleFilled,
|
||||||
|
// TwitterCircleFilled,
|
||||||
|
// } from '@ant-design/icons-vue';
|
||||||
|
import LoginFormTitle from './LoginFormTitle.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage';
|
||||||
|
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign';
|
||||||
|
//import { onKeyStroke } from '@vueuse/core';
|
||||||
|
|
||||||
|
const ACol = Col;
|
||||||
|
const ARow = Row;
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const InputPassword = Input.Password;
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notification, createErrorModal } = useMessage();
|
||||||
|
const { prefixCls } = useDesign('login');
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const { setLoginState, getLoginState } = useLoginState();
|
||||||
|
const { getFormRules } = useFormRules();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
const rememberMe = ref(false);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
// account: 'vben',
|
||||||
|
// password: '123456',
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { validForm } = useFormValid(formRef);
|
||||||
|
|
||||||
|
//onKeyStroke('Enter', handleLogin);
|
||||||
|
|
||||||
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
|
||||||
|
|
||||||
|
async function handleLogin() {
|
||||||
|
const data = await validForm();
|
||||||
|
if (!data) return;
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const userInfo = await userStore.login({
|
||||||
|
password: data.password,
|
||||||
|
account: data.account,
|
||||||
|
mode: 'none', //不要默认的错误提示
|
||||||
|
});
|
||||||
|
console.log(userInfo);
|
||||||
|
localStorage.setItem('fireUserLoginName', userInfo.name);
|
||||||
|
if (userInfo) {
|
||||||
|
notification.success({
|
||||||
|
message: t('sys.login.loginSuccessTitle'),
|
||||||
|
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.name}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
createErrorModal({
|
||||||
|
title: t('sys.api.errorTip'),
|
||||||
|
content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'),
|
||||||
|
getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.login_title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 68px;
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #131313;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background: #0f915f;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 21px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
background: #ebf3f1;
|
||||||
|
|
||||||
|
Button {
|
||||||
|
font-family: Microsoft YaHei;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div class="login_title">
|
||||||
|
{{ getFormTitle }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, unref } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { LoginStateEnum, useLoginState } from './useLogin';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const { getLoginState } = useLoginState();
|
||||||
|
|
||||||
|
const getFormTitle = computed(() => {
|
||||||
|
const titleObj = {
|
||||||
|
[LoginStateEnum.RESET_PASSWORD]: '重置密码',
|
||||||
|
[LoginStateEnum.LOGIN]: '用户登录',
|
||||||
|
[LoginStateEnum.REGISTER]: t('sys.login.signUpFormTitle'),
|
||||||
|
[LoginStateEnum.MOBILE]: t('sys.login.mobileSignInFormTitle'),
|
||||||
|
[LoginStateEnum.QR_CODE]: t('sys.login.qrSignInFormTitle'),
|
||||||
|
};
|
||||||
|
return titleObj[unref(getLoginState)];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.login_title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 68px;
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #131313;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="getShow">
|
||||||
|
<LoginFormTitle class="enter-x" />
|
||||||
|
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||||
|
<FormItem name="mobile" class="enter-x">
|
||||||
|
<Input
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.mobile"
|
||||||
|
:placeholder="t('sys.login.mobile')"
|
||||||
|
class="fix-auto-fill"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="sms" class="enter-x">
|
||||||
|
<CountdownInput
|
||||||
|
size="large"
|
||||||
|
class="fix-auto-fill"
|
||||||
|
v-model:value="formData.sms"
|
||||||
|
:placeholder="t('sys.login.smsCode')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem class="enter-x">
|
||||||
|
<Button type="primary" size="large" block @click="handleLogin" :loading="loading">
|
||||||
|
{{ t('sys.login.loginButton') }}
|
||||||
|
</Button>
|
||||||
|
<Button size="large" block class="mt-4" @click="handleBackLogin">
|
||||||
|
{{ t('sys.login.backSignIn') }}
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, computed, unref } from 'vue';
|
||||||
|
import { Form, Input, Button } from 'ant-design-vue';
|
||||||
|
import { CountdownInput } from '@/components/CountDown';
|
||||||
|
import LoginFormTitle from './LoginFormTitle.vue';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { handleBackLogin, getLoginState } = useLoginState();
|
||||||
|
const { getFormRules } = useFormRules();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
mobile: '',
|
||||||
|
sms: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { validForm } = useFormValid(formRef);
|
||||||
|
|
||||||
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.MOBILE);
|
||||||
|
|
||||||
|
async function handleLogin() {
|
||||||
|
const data = await validForm();
|
||||||
|
if (!data) return;
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="getShow">
|
||||||
|
<LoginFormTitle class="enter-x" />
|
||||||
|
<div class="enter-x min-w-64 min-h-64">
|
||||||
|
<QrCode
|
||||||
|
:value="qrCodeUrl"
|
||||||
|
class="enter-x flex justify-center xl:justify-start"
|
||||||
|
:width="280"
|
||||||
|
/>
|
||||||
|
<Divider class="enter-x">{{ t('sys.login.scanSign') }}</Divider>
|
||||||
|
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
|
||||||
|
{{ t('sys.login.backSignIn') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, unref } from 'vue';
|
||||||
|
import LoginFormTitle from './LoginFormTitle.vue';
|
||||||
|
import { Button, Divider } from 'ant-design-vue';
|
||||||
|
import { QrCode } from '@/components/Qrcode';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useLoginState, LoginStateEnum } from './useLogin';
|
||||||
|
|
||||||
|
const qrCodeUrl = 'https://vben.vvbin.cn/login';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { handleBackLogin, getLoginState } = useLoginState();
|
||||||
|
|
||||||
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.QR_CODE);
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="getShow">
|
||||||
|
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||||
|
<FormItem name="account" class="enter-x">
|
||||||
|
<Input
|
||||||
|
class="fix-auto-fill"
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.account"
|
||||||
|
:placeholder="t('sys.login.userName')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="mobile" class="enter-x">
|
||||||
|
<Input
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.mobile"
|
||||||
|
:placeholder="t('sys.login.mobile')"
|
||||||
|
class="fix-auto-fill"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="sms" class="enter-x">
|
||||||
|
<CountdownInput
|
||||||
|
size="large"
|
||||||
|
class="fix-auto-fill"
|
||||||
|
v-model:value="formData.sms"
|
||||||
|
:placeholder="t('sys.login.smsCode')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="password" class="enter-x">
|
||||||
|
<StrengthMeter
|
||||||
|
size="large"
|
||||||
|
v-model:value="formData.password"
|
||||||
|
:placeholder="t('sys.login.password')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem name="confirmPassword" class="enter-x">
|
||||||
|
<InputPassword
|
||||||
|
size="large"
|
||||||
|
visibilityToggle
|
||||||
|
v-model:value="formData.confirmPassword"
|
||||||
|
:placeholder="t('sys.login.confirmPassword')"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem class="enter-x" name="policy">
|
||||||
|
<!-- No logic, you need to deal with it yourself -->
|
||||||
|
<Checkbox v-model:checked="formData.policy" size="small">
|
||||||
|
{{ t('sys.login.policy') }}
|
||||||
|
</Checkbox>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<Button class="logon" size="large" block @click="handleRegister" :loading="loading">
|
||||||
|
{{ t('sys.login.registerButton') }}
|
||||||
|
</Button>
|
||||||
|
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
|
||||||
|
{{ t('sys.login.backSignIn') }}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, unref, computed } from 'vue';
|
||||||
|
import LoginFormTitle from './LoginFormTitle.vue';
|
||||||
|
import { Form, Input, Button, Checkbox } from 'ant-design-vue';
|
||||||
|
import { StrengthMeter } from '@/components/StrengthMeter';
|
||||||
|
import { CountdownInput } from '@/components/CountDown';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const InputPassword = Input.Password;
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { handleBackLogin, getLoginState } = useLoginState();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
mobile: '',
|
||||||
|
sms: '',
|
||||||
|
policy: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getFormRules } = useFormRules(formData);
|
||||||
|
const { validForm } = useFormValid(formRef);
|
||||||
|
|
||||||
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER);
|
||||||
|
|
||||||
|
async function handleRegister() {
|
||||||
|
const data = await validForm();
|
||||||
|
if (!data) return;
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.logon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background: #0f915f;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
font-family: Alibaba PuHuiTi;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 21px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<transition>
|
||||||
|
<div :class="prefixCls" v-if="true">
|
||||||
|
<Login sessionTimeout />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
|
import Login from './Login.vue';
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import { usePermissionStore } from '@/store/modules/permission';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { PermissionModeEnum } from '@/enums/appEnum';
|
||||||
|
import { type Nullable } from '@vben/types';
|
||||||
|
|
||||||
|
const { prefixCls } = useDesign('st-login');
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const userId = ref<Nullable<number | string>>(0);
|
||||||
|
|
||||||
|
const isBackMode = () => {
|
||||||
|
return appStore.getProjectConfig.permissionMode === PermissionModeEnum.BACK;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 记录当前的UserId
|
||||||
|
userId.value = userStore.getUserInfo?.userId;
|
||||||
|
console.log('Mounted', userStore.getUserInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (userId.value && userId.value !== userStore.getUserInfo.userId) {
|
||||||
|
// 登录的不是同一个用户,刷新整个页面以便丢弃之前用户的页面状态
|
||||||
|
document.location.reload();
|
||||||
|
} else if (isBackMode() && permissionStore.getLastBuildMenuTime === 0) {
|
||||||
|
// 后台权限模式下,没有成功加载过菜单,就重新加载整个页面。这通常发生在会话过期后按F5刷新整个页面后载入了本模块这种场景
|
||||||
|
document.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@prefix-cls: ~'@{namespace}-st-login';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9999999;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: @component-background;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
import type { FormInstance } from 'ant-design-vue/lib/form/Form';
|
||||||
|
import type {
|
||||||
|
RuleObject,
|
||||||
|
NamePath,
|
||||||
|
Rule as ValidationRule,
|
||||||
|
} from 'ant-design-vue/lib/form/interface';
|
||||||
|
import { ref, computed, unref, Ref } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
|
||||||
|
export enum LoginStateEnum {
|
||||||
|
LOGIN,
|
||||||
|
REGISTER,
|
||||||
|
RESET_PASSWORD,
|
||||||
|
MOBILE,
|
||||||
|
QR_CODE,
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = ref(LoginStateEnum.LOGIN);
|
||||||
|
|
||||||
|
// 这里也可以优化
|
||||||
|
// import { createGlobalState } from '@vueuse/core'
|
||||||
|
|
||||||
|
export function useLoginState() {
|
||||||
|
function setLoginState(state: LoginStateEnum) {
|
||||||
|
currentState.value = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLoginState = computed(() => currentState.value);
|
||||||
|
|
||||||
|
function handleBackLogin() {
|
||||||
|
setLoginState(LoginStateEnum.LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { setLoginState, getLoginState, handleBackLogin };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFormValid<T extends Object = any>(formRef: Ref<FormInstance>) {
|
||||||
|
const validate = computed(() => {
|
||||||
|
const form = unref(formRef);
|
||||||
|
return form?.validate ?? ((_nameList?: NamePath) => Promise.resolve());
|
||||||
|
});
|
||||||
|
|
||||||
|
async function validForm() {
|
||||||
|
const form = unref(formRef);
|
||||||
|
if (!form) return;
|
||||||
|
const data = await form.validate();
|
||||||
|
return data as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { validate, validForm };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFormRules(formData?: Recordable) {
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const getAccountFormRule = computed(() => createRule(t('sys.login.accountPlaceholder')));
|
||||||
|
const getPasswordFormRule = computed(() => createRule(t('sys.login.passwordPlaceholder')));
|
||||||
|
const getSmsFormRule = computed(() => createRule(t('sys.login.smsPlaceholder')));
|
||||||
|
const getMobileFormRule = computed(() => createRule(t('sys.login.mobilePlaceholder')));
|
||||||
|
|
||||||
|
const validatePolicy = async (_: RuleObject, value: boolean) => {
|
||||||
|
return !value ? Promise.reject(t('sys.login.policyPlaceholder')) : Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateConfirmPassword = (password: string) => {
|
||||||
|
return async (_: RuleObject, value: string) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.reject(t('sys.login.passwordPlaceholder'));
|
||||||
|
}
|
||||||
|
if (value !== password) {
|
||||||
|
return Promise.reject(t('sys.login.diffPwd'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFormRules = computed((): { [k: string]: ValidationRule | ValidationRule[] } => {
|
||||||
|
const accountFormRule = unref(getAccountFormRule);
|
||||||
|
const passwordFormRule = unref(getPasswordFormRule);
|
||||||
|
const smsFormRule = unref(getSmsFormRule);
|
||||||
|
const mobileFormRule = unref(getMobileFormRule);
|
||||||
|
|
||||||
|
const mobileRule = {
|
||||||
|
sms: smsFormRule,
|
||||||
|
mobile: mobileFormRule,
|
||||||
|
};
|
||||||
|
switch (unref(currentState)) {
|
||||||
|
// register form rules
|
||||||
|
case LoginStateEnum.REGISTER:
|
||||||
|
return {
|
||||||
|
account: accountFormRule,
|
||||||
|
password: passwordFormRule,
|
||||||
|
confirmPassword: [
|
||||||
|
{ validator: validateConfirmPassword(formData?.password), trigger: 'change' },
|
||||||
|
],
|
||||||
|
policy: [{ validator: validatePolicy, trigger: 'change' }],
|
||||||
|
...mobileRule,
|
||||||
|
};
|
||||||
|
|
||||||
|
// reset password form rules
|
||||||
|
case LoginStateEnum.RESET_PASSWORD:
|
||||||
|
return {
|
||||||
|
account: accountFormRule,
|
||||||
|
...mobileRule,
|
||||||
|
};
|
||||||
|
|
||||||
|
// mobile form rules
|
||||||
|
case LoginStateEnum.MOBILE:
|
||||||
|
return mobileRule;
|
||||||
|
|
||||||
|
// login form rules
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
account: accountFormRule,
|
||||||
|
password: passwordFormRule,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { getFormRules };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRule(message: string): ValidationRule[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message,
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue