修改 数据填报,打包报错修改,框架迁移遗留问题;修改401/404显示逻辑;去除页面中的moment改用dayjs;
@ -24,6 +24,7 @@ module.exports = {
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告
|
||||
|
||||
9
frontend-sjtb/src/api/changePassWord/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request';
|
||||
//修改密码
|
||||
export function updatePassword(params:any) {
|
||||
return request({
|
||||
url: '/user/updatePassword',
|
||||
method: 'post',
|
||||
data: params
|
||||
});
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取 Kendo 列表数据(支持聚合和分组)
|
||||
* @param params 过滤条件
|
||||
* @param sortConfig 排序配置
|
||||
*/
|
||||
export function getKendoList(params: any, sortConfig?: any[]) {
|
||||
return request({
|
||||
url: '/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: {
|
||||
filters: params,
|
||||
sorts: sortConfig || []
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义 Kendo 列表数据(用于全基地模式)
|
||||
* @param params 过滤条件
|
||||
* @param sortConfig 排序配置
|
||||
*/
|
||||
export function getKendoListCust(params: any, sortConfig?: any[]) {
|
||||
return request({
|
||||
url: '/dec-lygk-base-server/base/vmsstbprpt/GetKendoListCust',
|
||||
method: 'post',
|
||||
data: {
|
||||
filters: params,
|
||||
sorts: sortConfig || []
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取介绍信息(用于 other 类型排序)
|
||||
* @param filters 过滤条件
|
||||
* @param sorts 排序配置
|
||||
* @param needTotal 是否需要总数
|
||||
* @param fields 返回字段
|
||||
* @param defaultSort 默认排序
|
||||
*/
|
||||
export function getIntroduce(
|
||||
filters: any[],
|
||||
sorts?: any[],
|
||||
needTotal?: boolean,
|
||||
fields?: string[],
|
||||
defaultSort?: any
|
||||
) {
|
||||
return request({
|
||||
url: '/dec-lygk-base-server/base/introduce/getIntroduce',
|
||||
method: 'post',
|
||||
data: {
|
||||
logic: 'and',
|
||||
filters: filters,
|
||||
sorts: sorts || [],
|
||||
needTotal: needTotal || false,
|
||||
fields: fields || [],
|
||||
defaultSort: defaultSort
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取所有沿程配置数据
|
||||
export function getAllConfigTree(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/msalongb/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 保存沿程配置
|
||||
export function saveBaseWbsb(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/wbsb/addOrUpdate',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 删除沿程配置
|
||||
export function deleteBaseWbsb(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/wbsb/delete',
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取所有地图 图层树
|
||||
export function getAllMapLayerTree(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLayer/getAllMapLayerTree',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 保存地图 图层
|
||||
export function saveMapLayer(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLayer/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 删除地图 图层
|
||||
export function deleteMapLayer(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLayer/delete',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 获取所有地图 图层树
|
||||
export function getAllMapLayerParentIdTree(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLayer/getAllMapLayerTree',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取所有地图 图例树
|
||||
export function getAllMapLegendTree(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLegend/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 保存地图 图例
|
||||
export function saveMapLegend(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLegend/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 删除地图 图例
|
||||
export function deleteMapLegend(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLegend/delete',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取所有地图 图例树
|
||||
export function getAllMapLegendParentIdTree(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/mapLegend/getAllMapLegendTree',
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取所有倾斜摄影
|
||||
export function getAllTiltPhotoTree(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/sys/oblique/GetKendoListCust',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 保存倾斜摄影
|
||||
export function saveTiltPhoto(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/sys/oblique/addOrUpdate',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
// 删除倾斜摄影
|
||||
export function deleteTiltPhoto(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-sys-server/sys/oblique/delete',
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 553 B |
@ -1,18 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onBeforeUnmount } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Modal } from "ant-design-vue";
|
||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons-vue";
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { UserOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
||||
// 国际化
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
// import LangSelect from '@/components/LangSelect/index.vue';
|
||||
import Sidebar from "./Sidebar/index.vue";
|
||||
import Sidebar from './Sidebar/index.vue';
|
||||
import ChangePassword from './changePassword.vue';
|
||||
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
// const url = import.meta.env.VITE_APP_BASE_API;
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
const userStore = useUserStore();
|
||||
@ -21,10 +20,10 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
function logout() {
|
||||
Modal.confirm({
|
||||
title: "提示?",
|
||||
content: "确定注销并退出系统吗?",
|
||||
okText: "确定",
|
||||
type: "warning",
|
||||
title: '提示?',
|
||||
content: '确定注销并退出系统吗?',
|
||||
okText: '确定',
|
||||
type: 'warning',
|
||||
onOk: async () => {
|
||||
userStore
|
||||
.logout()
|
||||
@ -34,14 +33,13 @@ function logout() {
|
||||
.then(() => {
|
||||
router.push(`/login`);
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
const changePasswordVisible = ref(false);
|
||||
function changePassword() {
|
||||
router.push("/changePassword");
|
||||
changePasswordVisible.value = true;
|
||||
}
|
||||
onMounted(() => {});
|
||||
onBeforeUnmount(() => {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -52,8 +50,9 @@ onBeforeUnmount(() => {});
|
||||
href="/"
|
||||
class="h-[50px] min-w-[350px] flex items-center justify-center text-white"
|
||||
>
|
||||
<!-- <h1 class="text-blank font-bold text-[16px]">{{ t("login.title") }}</h1> -->
|
||||
<h1 class="text-blank font-bold text-[16px]">{{ t("login.titleSjtb") }}</h1>
|
||||
<h1 class="text-blank font-bold text-[16px]">
|
||||
{{ t('login.titleSjtb') }}
|
||||
</h1>
|
||||
</a>
|
||||
</transition>
|
||||
<Sidebar />
|
||||
@ -82,11 +81,12 @@ onBeforeUnmount(() => {});
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-layout-header>
|
||||
<ChangePassword v-model:open="changePasswordVisible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/styles/variables.module.scss" as *;
|
||||
@use '@/styles/variables.module.scss' as *;
|
||||
.navbar {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
394
frontend-sjtb/src/layout/components/changePassword.vue
Normal file
@ -0,0 +1,394 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { encrypt } from '@/utils/rsaEncrypt';
|
||||
import { updatePassword } from '@/api/changePassWord';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps<{ open: boolean }>();
|
||||
const emit = defineEmits<{ 'update:open': [value: boolean] }>();
|
||||
|
||||
const closeModal = () => emit('update:open', false);
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
// 提交状态
|
||||
const submitting = ref(false);
|
||||
|
||||
// 表单数据
|
||||
interface ChangePasswordForm {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
const formState = reactive<ChangePasswordForm>({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
// ==================== 密码校验函数 ====================
|
||||
|
||||
/**
|
||||
* 校验密码复杂度:至少包含四类字符中的三类
|
||||
*/
|
||||
const checkComplexity = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
let count = 0;
|
||||
const hasUpper = /[A-Z]/.test(password);
|
||||
const hasLower = /[a-z]/.test(password);
|
||||
const hasNumber = /[0-9]/.test(password);
|
||||
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
||||
|
||||
if (hasUpper) count++;
|
||||
if (hasLower) count++;
|
||||
if (hasNumber) count++;
|
||||
if (hasSpecial) count++;
|
||||
|
||||
if (count >= 3) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类'
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续重复字符
|
||||
*/
|
||||
const checkConsecutiveRepeat = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
if (password[i] === password[i + 1]) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上相同字符的连续重复(如11、aa)'
|
||||
};
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减数字
|
||||
*/
|
||||
const checkConsecutiveNumbers = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是数字
|
||||
if (curr >= 48 && curr <= 57 && next >= 48 && next <= 57) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的数字(如12、21)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减字母
|
||||
*/
|
||||
const checkConsecutiveLetters = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是字母(大写或小写)
|
||||
const isCurrLetter =
|
||||
(curr >= 65 && curr <= 90) || (curr >= 97 && curr <= 122);
|
||||
const isNextLetter =
|
||||
(next >= 65 && next <= 90) || (next >= 97 && next <= 122);
|
||||
|
||||
if (isCurrLetter && isNextLetter) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的字母(如ab、ba)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验用户名关联
|
||||
*/
|
||||
const checkUsernameRelation = (
|
||||
password: string,
|
||||
username: string
|
||||
): { valid: boolean; message: string } => {
|
||||
if (!username) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
|
||||
const lowerPassword = password.toLowerCase();
|
||||
const lowerUsername = username.toLowerCase();
|
||||
|
||||
if (lowerPassword.includes(lowerUsername)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含用户名或其核心部分'
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
// ==================== 表单验证规则 ====================
|
||||
|
||||
const rules = {
|
||||
oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 10, message: '密码长度不能少于10位', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 校验复杂度
|
||||
const complexity = checkComplexity(value);
|
||||
if (!complexity.valid) {
|
||||
return Promise.reject(new Error(complexity.message));
|
||||
}
|
||||
|
||||
// 校验连续重复
|
||||
const repeat = checkConsecutiveRepeat(value);
|
||||
if (!repeat.valid) {
|
||||
return Promise.reject(new Error(repeat.message));
|
||||
}
|
||||
|
||||
// 校验连续数字
|
||||
const numbers = checkConsecutiveNumbers(value);
|
||||
if (!numbers.valid) {
|
||||
return Promise.reject(new Error(numbers.message));
|
||||
}
|
||||
|
||||
// 校验连续字母
|
||||
const letters = checkConsecutiveLetters(value);
|
||||
if (!letters.valid) {
|
||||
return Promise.reject(new Error(letters.message));
|
||||
}
|
||||
|
||||
// 校验用户名关联
|
||||
const username = userStore.username || '';
|
||||
const usernameCheck = checkUsernameRelation(value, username);
|
||||
if (!usernameCheck.valid) {
|
||||
return Promise.reject(new Error(usernameCheck.message));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value !== formState.newPassword) {
|
||||
return Promise.reject(new Error('两次输入的密码不一致'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
await formRef.value?.validate();
|
||||
|
||||
submitting.value = true;
|
||||
|
||||
// RSA 加密密码
|
||||
const encryptedOldPassword = encrypt(formState.oldPassword);
|
||||
const encryptedNewPassword = encrypt(formState.newPassword);
|
||||
|
||||
if (!encryptedOldPassword || !encryptedNewPassword) {
|
||||
message.error('密码加密失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用接口
|
||||
const res: any = await updatePassword({
|
||||
id: userStore.userid,
|
||||
password: encryptedNewPassword,
|
||||
oldPassword: encryptedOldPassword
|
||||
});
|
||||
|
||||
// 兼容字符串和数字类型的状态码
|
||||
if (res.code === 0 || res.code === '0') {
|
||||
message.success('密码修改成功,请重新登录');
|
||||
|
||||
// 清空 token 和用户信息
|
||||
userStore.resetToken();
|
||||
closeModal();
|
||||
|
||||
// 跳转到登录页
|
||||
setTimeout(() => {
|
||||
router.push('/login');
|
||||
}, 1500);
|
||||
} else {
|
||||
message.error(res.msg || '密码修改失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.errorFields) {
|
||||
// 表单验证错误
|
||||
message.error('请检查表单填写是否正确');
|
||||
} else {
|
||||
// 其他错误
|
||||
message.error(error.message || '密码修改失败,请稍后重试');
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
const handleReset = () => {
|
||||
formRef.value?.resetFields();
|
||||
formState.oldPassword = '';
|
||||
formState.newPassword = '';
|
||||
formState.confirmPassword = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleCancel = () => {
|
||||
formRef.value?.resetFields();
|
||||
formState.oldPassword = '';
|
||||
formState.newPassword = '';
|
||||
formState.confirmPassword = '';
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:open="props.open"
|
||||
title="修改密码"
|
||||
:footer="null"
|
||||
:mask-closable="false"
|
||||
width="480px"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
class="password-form"
|
||||
>
|
||||
<a-form-item label="旧密码" name="oldPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.oldPassword"
|
||||
placeholder="请输入旧密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="新密码" name="newPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="确认新密码" name="confirmPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.confirmPassword"
|
||||
placeholder="请再次输入新密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item class="button-group">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="submitting"
|
||||
class="submit-btn"
|
||||
>
|
||||
提交
|
||||
</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.password-form {
|
||||
:deep(.ant-form-item-label) {
|
||||
label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-password) {
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
border-color: #005293;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: #005293;
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 147, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 0;
|
||||
|
||||
.submit-btn {
|
||||
width: 100px;
|
||||
background-color: #005293;
|
||||
border-color: #005293;
|
||||
|
||||
&:hover {
|
||||
background-color: #003d6e;
|
||||
border-color: #003d6e;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -14,17 +14,11 @@ import dayjs from 'dayjs'; // ant 中文语言
|
||||
import 'dayjs/locale/zh-cn';
|
||||
// @ts-ignore
|
||||
import 'virtual:svg-icons-register';
|
||||
// 3d地图
|
||||
import * as Cesium from 'cesium';
|
||||
import 'cesium/Build/Cesium/Widgets/widgets.css';
|
||||
|
||||
// 国际化
|
||||
import i18n from '@/lang/index';
|
||||
|
||||
import '@/styles/index.scss';
|
||||
import 'element-plus/theme-chalk/index.css';
|
||||
//import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
import WujieVue from 'wujie-vue3';
|
||||
|
||||
const app = createApp(App);
|
||||
// 自定义指令
|
||||
@ -48,7 +42,5 @@ app
|
||||
.use(router)
|
||||
.use(ElementPlus)
|
||||
.use(Antd)
|
||||
.use(Antd)
|
||||
.use(WujieVue)
|
||||
.use(i18n)
|
||||
.mount('#app');
|
||||
|
||||
@ -52,7 +52,6 @@ function findFirstAvailableRoute(routes: any[]): string | undefined {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start();
|
||||
const userStore = useUserStoreHook();
|
||||
|
||||
if (userStore.Token) {
|
||||
// 登录成功,跳转到首页
|
||||
if (to.path === '/login') {
|
||||
@ -60,6 +59,15 @@ router.beforeEach(async (to, from, next) => {
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
return;
|
||||
} else if (
|
||||
(to.path === '/401' || to.path === '/404') &&
|
||||
from.matched.length === 0 &&
|
||||
!from.name
|
||||
) {
|
||||
// 拦截直接 URL 访问错误页面,仅允许内部重定向触发
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
return;
|
||||
} else {
|
||||
const hasGetUserInfo = userStore.roles.length > 0;
|
||||
if (hasGetUserInfo) {
|
||||
|
||||
@ -35,12 +35,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: '/401',
|
||||
component: () => import('@/views/error-page/401.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/changePassword',
|
||||
component: () => import('@/views/changePassword/index.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
// 创建路由
|
||||
|
||||
@ -20,27 +20,10 @@ service.interceptors.request.use(
|
||||
);
|
||||
}
|
||||
config.headers.tenant_id = '4';
|
||||
if (
|
||||
config.url.includes('/dec-lygk-base-server') ||
|
||||
config.url.includes('/wmp-env-server') ||
|
||||
config.url.includes('/wmp-sys-server') ||
|
||||
config.url.includes('/wmp-eng-server') ||
|
||||
config.url.includes('/dec-modules-usm-springcloud-starter') ||
|
||||
config.url.includes('/wmp-swqx-server')
|
||||
) {
|
||||
config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD';
|
||||
config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388';
|
||||
config.headers._platformid = '31e6d13f-c359-4a19-8e67-b6f868f2401b';
|
||||
config.headers._sysid = '10EC2E0B-AEA9-4757-83A2-201BA1BC54E9';
|
||||
config.headers.authorization =
|
||||
'bearer aa5be16b-d1a3-4b93-9886-b2490025c7d6';
|
||||
config.baseURL = '/';
|
||||
} else {
|
||||
const user = useUserStoreHook();
|
||||
if (user.Token) {
|
||||
config.headers.token = getToken();
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error: any) => {
|
||||
|
||||
@ -1,114 +1,48 @@
|
||||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page401'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRefs } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const state = reactive({
|
||||
errGif: new URL(`../../assets/401_images/401.gif`, import.meta.url).href,
|
||||
|
||||
ewizardClap:
|
||||
'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
|
||||
dialogVisible: false
|
||||
});
|
||||
|
||||
const { errGif, ewizardClap, dialogVisible } = toRefs(state);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function back() {
|
||||
router.back();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
||||
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
|
||||
<h2>你没有权限去该页面</h2>
|
||||
<h6>如有不满请联系你领导</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>或者你可以去:</li>
|
||||
<li class="link-type">
|
||||
<router-link to="/"> 回首页 </router-link>
|
||||
</li>
|
||||
<li class="link-type">
|
||||
<a href="https://www.taobao.com/">随便看看</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="dialogVisible = true">点我看图</a>
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img
|
||||
:src="errGif"
|
||||
width="313"
|
||||
height="428"
|
||||
alt="Girl has dropped her ice cream."
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog v-model="dialogVisible" title="随便看">
|
||||
<img :src="ewizardClap" class="pan-img" />
|
||||
</el-dialog>
|
||||
<div class="error-page-container">
|
||||
<a-result
|
||||
status="403"
|
||||
title="401"
|
||||
sub-title="抱歉,您没有权限访问该页面,请尝试重新登录或联系管理员"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="goHome">
|
||||
<template #icon><home-outlined /></template>
|
||||
回首页
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
max-width: 100%;
|
||||
margin: 100px auto;
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none !important;
|
||||
const router = useRouter();
|
||||
|
||||
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 调整 Result 组件样式,使其更美观 */
|
||||
.error-page-container :deep(.ant-result) {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,280 +1,47 @@
|
||||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page404'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
function message() {
|
||||
return 'The webmaster said that you can not enter this page...';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img
|
||||
class="pic-404__parent"
|
||||
src="@/assets/404_images/404.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child left"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child mid"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child right"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__info">
|
||||
All rights reserved
|
||||
<a
|
||||
style="color: #20a0ff"
|
||||
href="https://wallstreetcn.com"
|
||||
target="_blank"
|
||||
>wallstreetcn</a
|
||||
<div class="error-page-container">
|
||||
<a-result
|
||||
status="404"
|
||||
title="404"
|
||||
sub-title="抱歉,您访问的页面不存在,请检查网址或返回首页"
|
||||
>
|
||||
</div>
|
||||
<div class="bullshit__headline">{{ message }}</div>
|
||||
<div class="bullshit__info">
|
||||
Please check that the URL you entered is correct, or click the button
|
||||
below to return to the homepage.
|
||||
</div>
|
||||
<a href="" class="bullshit__return-home">Back to home</a>
|
||||
</div>
|
||||
</div>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="goHome">
|
||||
<template #icon><home-outlined /></template>
|
||||
回首页
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wscn-http404-container {
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__child {
|
||||
position: absolute;
|
||||
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.error-page-container :deep(.ant-result) {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
15
frontend-sjtb/src/views/redirect/index.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const { params, query } = route;
|
||||
const { path } = params;
|
||||
|
||||
router.replace({ path: '/' + path, query });
|
||||
</script>
|
||||
@ -25,8 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moment from 'moment';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { useShuJuTianBaoStore } from '@/store/modules/shuJuTianBao';
|
||||
@ -50,7 +49,7 @@ const btnLoading = ref<boolean>(false);
|
||||
const initSearchData = {
|
||||
rvcd: 'all',
|
||||
stcd: null,
|
||||
reportMonth: moment().subtract(1, 'month').format('YYYY-MM')
|
||||
reportMonth: dayjs().subtract(1, 'month').format('YYYY-MM')
|
||||
};
|
||||
|
||||
const searchData = ref<any>({ ...initSearchData });
|
||||
|
||||
@ -338,7 +338,6 @@ const videoFileList = ref<any[]>([]);
|
||||
|
||||
// 禁用今天之后的日期
|
||||
const disabledDate = (current: any) => {
|
||||
// current 是 moment/dayjs 对象
|
||||
// 返回 true 表示禁用
|
||||
return current && current > dayjs().endOf('day');
|
||||
};
|
||||
@ -370,7 +369,7 @@ const getHbrvcdDropdownSelect = async () => {
|
||||
const res = await getSelectForDropdown({});
|
||||
let list = res.data || [];
|
||||
if (list && list.length > 0) {
|
||||
list = list.filter((item: any) => item.rvcd !== "all");
|
||||
list = list.filter((item: any) => item.rvcd !== 'all');
|
||||
}
|
||||
hbrvcdOption.value = list;
|
||||
} catch (error) {
|
||||
@ -469,13 +468,13 @@ const filterOption = (inputValue: string, option: any) => {
|
||||
};
|
||||
// 验证规则
|
||||
const rules: Record<string, Rule[]> = {
|
||||
rvcd: [{ required: true, message: "请选择流域", trigger: "change" }],
|
||||
rstcd: [{ required: true, message: "请选择电站", trigger: "change" }],
|
||||
stcd: [{ required: true, message: "请选择过鱼设施", trigger: "change" }],
|
||||
strdt: [{ required: true, message: "请选择过鱼时间", trigger: "change" }],
|
||||
ftp: [{ required: true, message: "请选择鱼种类", trigger: "change" }],
|
||||
direction: [{ required: true, message: "请选择游向", trigger: "change" }],
|
||||
fcnt: [{ required: true, message: "请输入过鱼数量", trigger: "change" }],
|
||||
rvcd: [{ required: true, message: '请选择流域', trigger: 'change' }],
|
||||
rstcd: [{ required: true, message: '请选择电站', trigger: 'change' }],
|
||||
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||
strdt: [{ required: true, message: '请选择过鱼时间', trigger: 'change' }],
|
||||
ftp: [{ required: true, message: '请选择鱼种类', trigger: 'change' }],
|
||||
direction: [{ required: true, message: '请选择游向', trigger: 'change' }],
|
||||
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 计算是否为编辑模式
|
||||
|
||||
@ -103,11 +103,6 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import {
|
||||
SaveOutlined,
|
||||
CheckSquareOutlined,
|
||||
QuestionOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue'; // 确保路径正确
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import BasicSearch from "@/components/BasicSearch/index.vue";
|
||||
import { DateSetting } from "@/utils/enumeration";
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import BasicSearch from "@/components/BasicSearch/index.vue"; // 确保路径正确
|
||||
import { getDictItemsByCode } from "@/api/dict";
|
||||
import { CheckSquareOutlined, CloseCircleOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
@ -33,79 +33,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
||||
changeOrigin: true,
|
||||
rewrite: path =>
|
||||
path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},
|
||||
'/process': {
|
||||
target: 'http://localhost:5174',
|
||||
changeOrigin: true
|
||||
},
|
||||
'/api/dec-lygk-base-server': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/dec-lygk-base-server'),
|
||||
'/api/dec-lygk-base-server'
|
||||
)
|
||||
},
|
||||
'/wmp-env-server': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(new RegExp('^/wmp-env-server'), '/api/wmp-env-server')
|
||||
},
|
||||
'/api/wmp-env-server': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/wmp-env-server'),
|
||||
'/api/wmp-env-server'
|
||||
)
|
||||
},
|
||||
'/api/wmp-eng-server': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/wmp-eng-server'),
|
||||
'/api/wmp-eng-server'
|
||||
)
|
||||
},
|
||||
'/api/wmp-sys-server': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/wmp-sys-server'),
|
||||
'/api/wmp-sys-server'
|
||||
)
|
||||
},
|
||||
'/api/dec-modules-usm-springcloud-starter': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/dec-modules-usm-springcloud-starter'),
|
||||
'/api/dec-modules-usm-springcloud-starter'
|
||||
)
|
||||
},
|
||||
'/api/wmp-swqx-server/': {
|
||||
target: 'https://211.99.26.225:12122',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path =>
|
||||
path.replace(
|
||||
new RegExp('^/api/dec-modules-usm-springcloud-starter'),
|
||||
'/api/dec-modules-usm-springcloud-starter'
|
||||
)
|
||||
}
|
||||
// /
|
||||
}
|
||||
},
|
||||
build: {
|
||||
|
||||
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 553 B |
@ -99,7 +99,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
||||
@ -122,7 +122,7 @@ const formModel = ref({
|
||||
anchorPointSelect: null,
|
||||
fishSurveyZhuanZhi: false
|
||||
});
|
||||
const currentDate = moment();
|
||||
const currentDate = dayjs();
|
||||
const defaultYear =
|
||||
currentDate.month() < 6 ? currentDate.year() - 1 : currentDate.year();
|
||||
|
||||
@ -132,8 +132,8 @@ const fishSurveyZhuanZhiParams = ref({
|
||||
});
|
||||
|
||||
const searchTimeRange = ref([
|
||||
moment(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
moment(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
dayjs(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
]);
|
||||
|
||||
// 备注:筛选组件只负责根据当前路由决定展示哪些输入项。
|
||||
@ -148,8 +148,8 @@ watch(
|
||||
() => {
|
||||
mapOrchestrator.resetFilterState();
|
||||
searchTimeRange.value = [
|
||||
moment(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
moment(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
dayjs(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
];
|
||||
formModel.value = {
|
||||
siteRangePicker: null,
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onBeforeUnmount } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Modal } from "ant-design-vue";
|
||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons-vue";
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { UserOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
||||
// 国际化
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
// import LangSelect from '@/components/LangSelect/index.vue';
|
||||
import Sidebar from "./Sidebar/index.vue";
|
||||
import Sidebar from './Sidebar/index.vue';
|
||||
import ChangePassword from './changePassword.vue';
|
||||
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
// const url = import.meta.env.VITE_APP_BASE_API;
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
const userStore = useUserStore();
|
||||
@ -21,10 +20,10 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
function logout() {
|
||||
Modal.confirm({
|
||||
title: "提示?",
|
||||
content: "确定注销并退出系统吗?",
|
||||
okText: "确定",
|
||||
type: "warning",
|
||||
title: '提示?',
|
||||
content: '确定注销并退出系统吗?',
|
||||
okText: '确定',
|
||||
type: 'warning',
|
||||
onOk: async () => {
|
||||
userStore
|
||||
.logout()
|
||||
@ -34,14 +33,13 @@ function logout() {
|
||||
.then(() => {
|
||||
router.push(`/login`);
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
const changePasswordVisible = ref(false);
|
||||
function changePassword() {
|
||||
router.push("/changePassword");
|
||||
changePasswordVisible.value = true;
|
||||
}
|
||||
onMounted(() => {});
|
||||
onBeforeUnmount(() => {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -52,8 +50,9 @@ onBeforeUnmount(() => {});
|
||||
href="/"
|
||||
class="h-[50px] min-w-[350px] flex items-center justify-center text-white"
|
||||
>
|
||||
<!-- <h1 class="text-blank font-bold text-[16px]">{{ t("login.title") }}</h1> -->
|
||||
<h1 class="text-blank font-bold text-[16px]">{{ t("login.title") }}</h1>
|
||||
<h1 class="text-blank font-bold text-[16px]">
|
||||
{{ t('login.title') }}
|
||||
</h1>
|
||||
</a>
|
||||
</transition>
|
||||
<Sidebar />
|
||||
@ -82,11 +81,12 @@ onBeforeUnmount(() => {});
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-layout-header>
|
||||
<ChangePassword v-model:open="changePasswordVisible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/styles/variables.module.scss" as *;
|
||||
@use '@/styles/variables.module.scss' as *;
|
||||
.navbar {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
394
frontend/src/layout/components/changePassword.vue
Normal file
@ -0,0 +1,394 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { encrypt } from '@/utils/rsaEncrypt';
|
||||
import { updatePassword } from '@/api/changePassWord';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps<{ open: boolean }>();
|
||||
const emit = defineEmits<{ 'update:open': [value: boolean] }>();
|
||||
|
||||
const closeModal = () => emit('update:open', false);
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
// 提交状态
|
||||
const submitting = ref(false);
|
||||
|
||||
// 表单数据
|
||||
interface ChangePasswordForm {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
const formState = reactive<ChangePasswordForm>({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
// ==================== 密码校验函数 ====================
|
||||
|
||||
/**
|
||||
* 校验密码复杂度:至少包含四类字符中的三类
|
||||
*/
|
||||
const checkComplexity = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
let count = 0;
|
||||
const hasUpper = /[A-Z]/.test(password);
|
||||
const hasLower = /[a-z]/.test(password);
|
||||
const hasNumber = /[0-9]/.test(password);
|
||||
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
||||
|
||||
if (hasUpper) count++;
|
||||
if (hasLower) count++;
|
||||
if (hasNumber) count++;
|
||||
if (hasSpecial) count++;
|
||||
|
||||
if (count >= 3) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类'
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续重复字符
|
||||
*/
|
||||
const checkConsecutiveRepeat = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
if (password[i] === password[i + 1]) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上相同字符的连续重复(如11、aa)'
|
||||
};
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减数字
|
||||
*/
|
||||
const checkConsecutiveNumbers = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是数字
|
||||
if (curr >= 48 && curr <= 57 && next >= 48 && next <= 57) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的数字(如12、21)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减字母
|
||||
*/
|
||||
const checkConsecutiveLetters = (
|
||||
password: string
|
||||
): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是字母(大写或小写)
|
||||
const isCurrLetter =
|
||||
(curr >= 65 && curr <= 90) || (curr >= 97 && curr <= 122);
|
||||
const isNextLetter =
|
||||
(next >= 65 && next <= 90) || (next >= 97 && next <= 122);
|
||||
|
||||
if (isCurrLetter && isNextLetter) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的字母(如ab、ba)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验用户名关联
|
||||
*/
|
||||
const checkUsernameRelation = (
|
||||
password: string,
|
||||
username: string
|
||||
): { valid: boolean; message: string } => {
|
||||
if (!username) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
|
||||
const lowerPassword = password.toLowerCase();
|
||||
const lowerUsername = username.toLowerCase();
|
||||
|
||||
if (lowerPassword.includes(lowerUsername)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含用户名或其核心部分'
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
// ==================== 表单验证规则 ====================
|
||||
|
||||
const rules = {
|
||||
oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 10, message: '密码长度不能少于10位', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 校验复杂度
|
||||
const complexity = checkComplexity(value);
|
||||
if (!complexity.valid) {
|
||||
return Promise.reject(new Error(complexity.message));
|
||||
}
|
||||
|
||||
// 校验连续重复
|
||||
const repeat = checkConsecutiveRepeat(value);
|
||||
if (!repeat.valid) {
|
||||
return Promise.reject(new Error(repeat.message));
|
||||
}
|
||||
|
||||
// 校验连续数字
|
||||
const numbers = checkConsecutiveNumbers(value);
|
||||
if (!numbers.valid) {
|
||||
return Promise.reject(new Error(numbers.message));
|
||||
}
|
||||
|
||||
// 校验连续字母
|
||||
const letters = checkConsecutiveLetters(value);
|
||||
if (!letters.valid) {
|
||||
return Promise.reject(new Error(letters.message));
|
||||
}
|
||||
|
||||
// 校验用户名关联
|
||||
const username = userStore.username || '';
|
||||
const usernameCheck = checkUsernameRelation(value, username);
|
||||
if (!usernameCheck.valid) {
|
||||
return Promise.reject(new Error(usernameCheck.message));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value !== formState.newPassword) {
|
||||
return Promise.reject(new Error('两次输入的密码不一致'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
await formRef.value?.validate();
|
||||
|
||||
submitting.value = true;
|
||||
|
||||
// RSA 加密密码
|
||||
const encryptedOldPassword = encrypt(formState.oldPassword);
|
||||
const encryptedNewPassword = encrypt(formState.newPassword);
|
||||
|
||||
if (!encryptedOldPassword || !encryptedNewPassword) {
|
||||
message.error('密码加密失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用接口
|
||||
const res: any = await updatePassword({
|
||||
id: userStore.userid,
|
||||
password: encryptedNewPassword,
|
||||
oldPassword: encryptedOldPassword
|
||||
});
|
||||
|
||||
// 兼容字符串和数字类型的状态码
|
||||
if (res.code === 0 || res.code === '0') {
|
||||
message.success('密码修改成功,请重新登录');
|
||||
|
||||
// 清空 token 和用户信息
|
||||
userStore.resetToken();
|
||||
closeModal();
|
||||
|
||||
// 跳转到登录页
|
||||
setTimeout(() => {
|
||||
router.push('/login');
|
||||
}, 1500);
|
||||
} else {
|
||||
message.error(res.msg || '密码修改失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.errorFields) {
|
||||
// 表单验证错误
|
||||
message.error('请检查表单填写是否正确');
|
||||
} else {
|
||||
// 其他错误
|
||||
message.error(error.message || '密码修改失败,请稍后重试');
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
const handleReset = () => {
|
||||
formRef.value?.resetFields();
|
||||
formState.oldPassword = '';
|
||||
formState.newPassword = '';
|
||||
formState.confirmPassword = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleCancel = () => {
|
||||
formRef.value?.resetFields();
|
||||
formState.oldPassword = '';
|
||||
formState.newPassword = '';
|
||||
formState.confirmPassword = '';
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:open="props.open"
|
||||
title="修改密码"
|
||||
:footer="null"
|
||||
:mask-closable="false"
|
||||
width="480px"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
class="password-form"
|
||||
>
|
||||
<a-form-item label="旧密码" name="oldPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.oldPassword"
|
||||
placeholder="请输入旧密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="新密码" name="newPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="确认新密码" name="confirmPassword">
|
||||
<a-input-password
|
||||
v-model:value="formState.confirmPassword"
|
||||
placeholder="请再次输入新密码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item class="button-group">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="submitting"
|
||||
class="submit-btn"
|
||||
>
|
||||
提交
|
||||
</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.password-form {
|
||||
:deep(.ant-form-item-label) {
|
||||
label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-password) {
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
border-color: #005293;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: #005293;
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 147, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 0;
|
||||
|
||||
.submit-btn {
|
||||
width: 100px;
|
||||
background-color: #005293;
|
||||
border-color: #005293;
|
||||
|
||||
&:hover {
|
||||
background-color: #003d6e;
|
||||
border-color: #003d6e;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -23,8 +23,6 @@ import i18n from '@/lang/index';
|
||||
|
||||
import '@/styles/index.scss';
|
||||
import 'element-plus/theme-chalk/index.css';
|
||||
//import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
import WujieVue from 'wujie-vue3';
|
||||
|
||||
const app = createApp(App);
|
||||
// 自定义指令
|
||||
@ -49,6 +47,5 @@ app
|
||||
.use(ElementPlus)
|
||||
.use(Antd)
|
||||
.use(Antd)
|
||||
.use(WujieVue)
|
||||
.use(i18n)
|
||||
.mount('#app');
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { watch, type WatchStopHandle } from 'vue';
|
||||
import { unByKey } from 'ol/Observable';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { MapClass } from '@/components/gis/map.class';
|
||||
import { getMapConfig, layerConfig2Flat } from '@/components/gis/gisUtils';
|
||||
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
||||
@ -393,7 +393,7 @@ export const useMapOrchestrator = () => {
|
||||
|
||||
// 备注:统一重置地图筛选表单依赖的时间范围,供筛选组件在切页时复位默认输入。
|
||||
const resetFilterState = () => {
|
||||
mapViewStore.setSearchTimeRange([moment().subtract(1, 'M'), moment()]);
|
||||
mapViewStore.setSearchTimeRange([dayjs().subtract(1, 'M'), dayjs()]);
|
||||
};
|
||||
|
||||
// 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const useMapViewStore = defineStore('map-view', () => {
|
||||
const checkedLayerKeys = ref<string[]>([]);
|
||||
const legendCheckedState = ref<Record<string, number>>({});
|
||||
const searchTimeRange = ref<[any, any]>([moment().subtract(1, 'M'), moment()]);
|
||||
const searchTimeRange = ref<[any, any]>([dayjs().subtract(1, 'M'), dayjs()]);
|
||||
const selectedBaseId = ref('');
|
||||
const currentZoomLevel = ref(4.5);
|
||||
|
||||
@ -67,7 +67,7 @@ export const useMapViewStore = defineStore('map-view', () => {
|
||||
const resetViewState = () => {
|
||||
checkedLayerKeys.value = [];
|
||||
legendCheckedState.value = {};
|
||||
searchTimeRange.value = [moment().subtract(1, 'M'), moment()];
|
||||
searchTimeRange.value = [dayjs().subtract(1, 'M'), dayjs()];
|
||||
selectedBaseId.value = '';
|
||||
currentZoomLevel.value = 4.5;
|
||||
};
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
<div v-if="modalData" class="modal-content">
|
||||
<MonthlyAverage
|
||||
:tm="modalData.date ? moment(modalData.date).format('YYYY-MM') + '-01 00:00:00' : moment().format('YYYY-MM') + '-01 23:59:59'"
|
||||
:tm="modalData.date ? dayjs(modalData.date).format('YYYY-MM') + '-01 00:00:00' : dayjs().format('YYYY-MM') + '-01 23:59:59'"
|
||||
:dataDimensionVal="modalData.baseid" :rvcd='modalData.rvcd' :stcd="modalData.stcd" :jdList="JidiSelectEventStore.jidiData" />
|
||||
</div>
|
||||
</a-modal>
|
||||
@ -37,7 +37,7 @@ import { wbsbGetKendoList, avgMonGetKendoListCust } from "@/api/sw";
|
||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||
import MonthlyAverage from './monthlyAverage.vue'
|
||||
import MonthlyAverageMaxModal from './TwoLayers/monthlyAverageMaxModal.vue'
|
||||
import moment from 'moment'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
ZoomInOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
|
||||
@ -104,7 +104,7 @@ import { message } from 'ant-design-vue';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import * as echarts from 'echarts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { omit } from 'lodash';
|
||||
import { wbsbGetKendoList, getVmsstbprpt, DetGetKendoListCust } from '@/api/sw';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
@ -220,7 +220,7 @@ const getColorByCodeAndType = (code: string[], typeKey: string[]): string[] => {
|
||||
/**
|
||||
* 日期格式化
|
||||
*/
|
||||
const tmFmt = (d: string) => moment(d).format('YYYY-MM-DD');
|
||||
const tmFmt = (d: string) => dayjs(d).format('YYYY-MM-DD');
|
||||
|
||||
/**
|
||||
* 格式化数值:保留一位小数,四舍五入,空值显示 -
|
||||
@ -675,7 +675,7 @@ const handleSearch = async () => {
|
||||
|
||||
const tmValue = dayjs.isDayjs(formValue.tm)
|
||||
? formValue.tm.format('YYYY-MM-DD 00:00:00')
|
||||
: moment(formValue.tm).format('YYYY-MM-DD 00:00:00');
|
||||
: dayjs(formValue.tm).format('YYYY-MM-DD 00:00:00');
|
||||
|
||||
const values = {
|
||||
...formValue,
|
||||
@ -694,12 +694,12 @@ const handleExport = async () => {
|
||||
try {
|
||||
const tmValue = dayjs.isDayjs(formValue.tm)
|
||||
? formValue.tm.format('YYYY-MM-DD 00:00:00')
|
||||
: moment(formValue.tm).format('YYYY-MM-DD 00:00:00');
|
||||
: dayjs(formValue.tm).format('YYYY-MM-DD 00:00:00');
|
||||
|
||||
const params = {
|
||||
...buildFilterParams({ ...formValue, tm: tmValue }),
|
||||
exportType: 'excel',
|
||||
exportFileName: `月平均水温历史对比 ${moment().format(
|
||||
exportFileName: `月平均水温历史对比 ${dayjs().format(
|
||||
'YYYY-MM-DD HH-mm-ss'
|
||||
)}`
|
||||
};
|
||||
|
||||
@ -60,6 +60,15 @@ router.beforeEach(async (to, from, next) => {
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
return;
|
||||
} else if (
|
||||
(to.path === '/401' || to.path === '/404') &&
|
||||
from.matched.length === 0 &&
|
||||
!from.name
|
||||
) {
|
||||
// 拦截直接 URL 访问错误页面,仅允许内部重定向触发
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
return;
|
||||
} else {
|
||||
const hasGetUserInfo = userStore.roles.length > 0;
|
||||
if (hasGetUserInfo) {
|
||||
|
||||
@ -35,12 +35,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: '/401',
|
||||
component: () => import('@/views/error-page/401.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/changePassword',
|
||||
component: () => import('@/views/changePassword/index.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
// 创建路由
|
||||
|
||||
136
frontend/src/store/composables/useTimeScale.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
export interface UseTimeScaleOptions {
|
||||
defaultTimeScale?: string;
|
||||
onSearchFinish?: (values: any) => void;
|
||||
onReset?: (values: any) => void;
|
||||
}
|
||||
|
||||
export function useTimeScale(options: UseTimeScaleOptions = {}) {
|
||||
const defaultTimeScale = options.defaultTimeScale || 'tm';
|
||||
const currentTimeScale = ref(defaultTimeScale);
|
||||
|
||||
const jcdt = ref({
|
||||
min: null as string | null,
|
||||
max: null as string | null
|
||||
});
|
||||
|
||||
// 根据时间尺度动态切换 picker 类型
|
||||
const timePicker = computed(() => {
|
||||
if (currentTimeScale.value === 'tm') return 'date';
|
||||
if (currentTimeScale.value === 'dt') return 'date';
|
||||
return 'month';
|
||||
});
|
||||
|
||||
// 根据时间尺度动态切换 format
|
||||
const timeFormat = computed(() => {
|
||||
if (currentTimeScale.value === 'tm') return 'YYYY-MM-DD HH:mm';
|
||||
if (currentTimeScale.value === 'dt') return 'YYYY-MM-DD';
|
||||
return 'YYYY-MM';
|
||||
});
|
||||
|
||||
// 小时模式需要 showTime 配置
|
||||
const timeShowTime = computed(() => {
|
||||
if (currentTimeScale.value === 'tm') return { format: 'HH:mm' };
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// 设置默认时间范围
|
||||
const setDefaultTimeRange = (timeScale: string) => {
|
||||
const now = dayjs();
|
||||
const today = now.format('YYYY-MM-DD');
|
||||
|
||||
if (timeScale === 'tm') {
|
||||
// 小时:默认前七天,使用当前时分秒
|
||||
jcdt.value = {
|
||||
min: `${dayjs().subtract(6, 'day').format('YYYY-MM-DD')} ${now.format(
|
||||
'HH:mm:ss'
|
||||
)}`,
|
||||
max: `${today} ${now.format('HH:mm:ss')}`
|
||||
};
|
||||
} else if (timeScale === 'dt') {
|
||||
// 日:默认前一个月
|
||||
jcdt.value = {
|
||||
min: `${dayjs().subtract(1, 'month').format('YYYY-MM-DD')} 00:00:00`,
|
||||
max: `${today} 23:59:59`
|
||||
};
|
||||
} else {
|
||||
// 月:默认前4个月
|
||||
jcdt.value = {
|
||||
min: `${dayjs().subtract(4, 'month').format('YYYY-MM')}-01 00:00:00`,
|
||||
max: `${today} 23:59:59`
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleRangeClick = (value: any) => {
|
||||
jcdt.value = {
|
||||
min: dayjs(value.value[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
max: dayjs(value.value[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
};
|
||||
};
|
||||
|
||||
// 禁用起始时间之前的日期
|
||||
const disabledEndDate = (current: Dayjs) => {
|
||||
if (!jcdt.value.min) return false;
|
||||
return current && current < dayjs(jcdt.value.min).endOf('day');
|
||||
};
|
||||
|
||||
// 处理搜索完成
|
||||
const handleSearchFinish = (values: any) => {
|
||||
values.jcdt = {
|
||||
min: values.jcdt?.min || jcdt.value.min,
|
||||
max: values.jcdt?.max || jcdt.value.max
|
||||
};
|
||||
if (jcdt.value.min) values.jcdt.min = dayjs(jcdt.value.min);
|
||||
if (jcdt.value.max) values.jcdt.max = dayjs(jcdt.value.max);
|
||||
// 强制使用当前 timeScale,不被传入值覆盖
|
||||
values.timeScale = currentTimeScale.value;
|
||||
options.onSearchFinish?.(values);
|
||||
};
|
||||
|
||||
// 处理值变化
|
||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
||||
if (
|
||||
changedValues.timeScale &&
|
||||
changedValues.timeScale !== currentTimeScale.value
|
||||
) {
|
||||
currentTimeScale.value = changedValues.timeScale;
|
||||
setDefaultTimeRange(changedValues.timeScale);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
jcdt.value = {
|
||||
min: null,
|
||||
max: null
|
||||
};
|
||||
currentTimeScale.value = defaultTimeScale;
|
||||
setDefaultTimeRange(defaultTimeScale);
|
||||
options.onReset?.({
|
||||
timeScale: defaultTimeScale
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化
|
||||
const init = () => {
|
||||
setDefaultTimeRange(defaultTimeScale);
|
||||
};
|
||||
|
||||
return {
|
||||
jcdt,
|
||||
currentTimeScale,
|
||||
timePicker,
|
||||
timeFormat,
|
||||
timeShowTime,
|
||||
handleRangeClick,
|
||||
disabledEndDate,
|
||||
handleSearchFinish,
|
||||
handleValuesChange,
|
||||
handleReset,
|
||||
init,
|
||||
setDefaultTimeRange
|
||||
};
|
||||
}
|
||||
@ -1,449 +0,0 @@
|
||||
import moment from 'moment';
|
||||
|
||||
export const urlList = [
|
||||
{
|
||||
url: '/wmp-eng-server/eng/point/GetKendoListCust',
|
||||
title: '常规水电',
|
||||
params: {
|
||||
anchoPointState: [{ field: 'anchoPointState', operator: 'isnotnull' }]
|
||||
},
|
||||
orders:
|
||||
'{"baseId":"asc","rvcdStepSort":"asc","siteStepSort":"asc","ennm":"asc"}'
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getFacilityPointList/GetKendoListCust',
|
||||
title: '低温水减缓设施',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-eng-server/eng/eq/interval/GetKendoListCust',
|
||||
title: '生态流量达标率',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] },
|
||||
orders:
|
||||
'{"baseId":"asc","rvcdStepSort":"asc","siteStepSort":"asc","ennm":"asc"}'
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/reach/GetKendoListCust',
|
||||
title: '实际水质',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/reach/GetKendoListCust',
|
||||
title: '目标水质',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '自建站',
|
||||
params: {
|
||||
dtinType: [{ field: 'dtinType', operator: 'eq', value: '0' }],
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
},
|
||||
orders: '{"siteStepSort":"asc"}'
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '国家站',
|
||||
params: {
|
||||
dtinType: [{ field: 'dtinType', operator: 'eq', value: '1' }],
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '人工站',
|
||||
params: {
|
||||
dtinType: [{ field: 'dtinType', operator: 'eq', value: '2' }],
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '栖息地水质站',
|
||||
params: {
|
||||
fhstcd: [{ field: 'fhstcd', operator: 'isnotnull' }],
|
||||
fhFlag: [{ field: 'fhFlag', operator: 'eq', value: '1' }],
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fp/point/qgc/GetKendoListCust',
|
||||
title: '过鱼设施',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-eng-server/eng/eq/eqds/GetKendoListCust',
|
||||
title: '生态流量泄放设施',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/fb/point/GetKendoListCust',
|
||||
title: '鱼类增殖站',
|
||||
params: {
|
||||
sttp: [{ field: 'sttp', operator: 'eq', value: 'FB' }],
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/GetKendoListCust',
|
||||
title: '珍稀植物园',
|
||||
params: { sttpCode: 'VP', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/GetKendoListCust',
|
||||
title: '动物救助站',
|
||||
params: { sttpCode: 'VA', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/fb/point/GetKendoListCust',
|
||||
title: '人工产卵场',
|
||||
params: { sttp: 'SG', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getEngTempPointList/GetKendoListCust',
|
||||
title: '水温监测断面',
|
||||
keyType: 'wt_point',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getEngTempPointList/GetKendoListCust',
|
||||
title: '自建水温站',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }], flag: '0' }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getEngTempPointList/GetKendoListCust',
|
||||
title: '人工水温站',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }], flag: '2' }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getEngTempPointList/GetKendoListCust',
|
||||
title: '栖息地水温站',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
fhstcd: [{ field: 'fhstcd', operator: 'isnotnull' }],
|
||||
fhFlag: '1'
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/GetKendoListCust',
|
||||
title: '栖息地',
|
||||
params: { sttpCode: 'FH', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/fhPoint/GetKendoListCust',
|
||||
title: '水质监测站',
|
||||
params: { sttpCode: 'WQ', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/fhPoint/GetKendoListCust',
|
||||
title: '水温监测站',
|
||||
params: {
|
||||
sttpCode: 'WTRV',
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/fhPoint/GetKendoListCust',
|
||||
title: '流量监测站',
|
||||
params: { sttpCode: 'ZQ', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/fhPoint/GetKendoListCust',
|
||||
title: '视频监控',
|
||||
params: { sttpCode: 'VD', lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/vd/aiPoint/GetKendoListCust',
|
||||
title: 'AI视频监控站',
|
||||
params: { sttpCode: 'AIVD' }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '水电站监控视频',
|
||||
keyType: 'video_fbfm_point',
|
||||
params: {
|
||||
sttp: 'VD_FBFM',
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '生态流量监测断面视频',
|
||||
keyType: 'video_eqs_point',
|
||||
params: {
|
||||
sttp: 'VD_EQS',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '水质站运行视频',
|
||||
keyType: 'video_wq_point',
|
||||
params: {
|
||||
sttp: 'VD_WQ',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '过鱼设施视频',
|
||||
keyType: 'video_fp_point',
|
||||
params: {
|
||||
sttp: 'VD_FP',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '鱼类增殖站视频',
|
||||
keyType: 'video_fb_point',
|
||||
params: {
|
||||
sttp: 'VD_FB',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '栖息地视频',
|
||||
keyType: 'video_fh_point',
|
||||
params: {
|
||||
sttp: 'VD_FH',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '低温水减缓设施视频',
|
||||
keyType: 'video_dw_point',
|
||||
params: {
|
||||
sttp: 'VD_DW',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '动物救助站视频',
|
||||
keyType: 'video_va_point',
|
||||
params: {
|
||||
sttp: 'VD_VA',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '珍稀植物园视频',
|
||||
keyType: 'video_vp_point',
|
||||
params: {
|
||||
sttp: 'VD_VP',
|
||||
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
url: '/wmp-env-server/env/we/point/qgc/GetKendoListCust',
|
||||
title: '水生生态调查断面',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] },
|
||||
orders: '{"siteStepSort":"asc"}'
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/we/fishPoint/qgc/GetKendoListCust',
|
||||
title: '鱼类分布',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-eng-server/eng/alarmPoint/GetKendoListCust',
|
||||
title: '水电站告警情况',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/fb/point/discharge/GetKendoListCust',
|
||||
title: '水电站',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/zqpoint/GetKendoListCust',
|
||||
title: '国家水文站',
|
||||
params: { dtinType: 1, lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/zqpoint/GetKendoListCust',
|
||||
title: '自建水文站',
|
||||
params: { dtinType: 0, lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '增殖站水质站',
|
||||
params: {
|
||||
dtinType: 0,
|
||||
sttpCode: 'WQFB',
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fh/vdpoint/GetKendoListCust',
|
||||
title: '视频监控站',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wb/point/GetKendoListCust',
|
||||
title: '气象站',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sw/getEngTempPointList/getLastData',
|
||||
title: '水温站点',
|
||||
params: {
|
||||
logic: 'and',
|
||||
filters: []
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wq/anchorPoint/GetKendoListCust',
|
||||
title: '水质站点',
|
||||
params: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'lgtd',
|
||||
operator: 'isnotnull',
|
||||
dataType: 'string'
|
||||
},
|
||||
{
|
||||
field: 'orderIndex',
|
||||
operator: 'isnotnull',
|
||||
dataType: 'string'
|
||||
}
|
||||
]
|
||||
},
|
||||
orders: '{"orderIndex":"asc"}'
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/warn/stcd/point/GetKendoListCust',
|
||||
title: '水质告警',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'WQ' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/warn/stcd/point/GetKendoListCust',
|
||||
title: '水温告警',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'WTRV' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/warn/stcd/point/GetKendoListCust',
|
||||
title: '水位告警',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'ENG' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/warn/stcd/operatePoint/GetKendoListCust',
|
||||
title: '环保设施告警',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
yr: moment().format('YYYY')
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fp/point/built/GetKendoListCust',
|
||||
title: '在建过鱼设施-地图锚点',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
bldsttCcode: [{ field: 'bldsttCcode', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fb/point/built/GetKendoListCust',
|
||||
title: '在建鱼类增殖站',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'FB' }],
|
||||
bldsttCcode: [{ field: 'bldsttCcode', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fb/point/built/GetKendoListCust',
|
||||
title: '在建人工产卵场',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'SG' }],
|
||||
bldsttCcode: [{ field: 'bldsttCcode', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/built/GetKendoListCust',
|
||||
title: '珍稀植物园-在建',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'VP' }],
|
||||
bldsttCcode: [{ field: 'bldsttCcode', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/fhvap/built/GetKendoListCust',
|
||||
title: '动物救助站-在建',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'VA' }],
|
||||
bldsttCcode: [{ field: 'bldsttCcode', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/eng/eq/eqds/built/GetKendoListCust',
|
||||
title: '在建生态流量泄放设施',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
BLDSTT_CCODE: [{ field: 'BLDSTT_CCODE', operator: 'eq', value: '1' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/we/fishList/point/GetKendoList',
|
||||
title: '鱼类沿程',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/wva/point/GetKendoListCust',
|
||||
title: '野生动物监测',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
sttpCode: [{ field: 'sttpCode', operator: 'eq', value: 'WVA' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/env/we/fishList/point/GetNativeRareFish',
|
||||
title: '土著珍稀鱼类',
|
||||
params: {
|
||||
rare: '1',
|
||||
specOrigin: '1',
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/te/tet/point/GetTerrestrialAnimal',
|
||||
title: '陆生动物分布',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
url: '/wmp-env-server/sdFprdR/point/getFprdPointList',
|
||||
title: '鱼类调查装置',
|
||||
params: { lgtd: [{ field: 'lgtd', operator: 'isnotnull' }] }
|
||||
}
|
||||
];
|
||||
@ -1,7 +1,6 @@
|
||||
import { defineStore, storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { omit } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { MapClass } from '@/components/gis/map.class';
|
||||
import { applyLayerMutualExclusionRules } from '@/modules/map/domain/map-layer-rules';
|
||||
import {
|
||||
@ -13,7 +12,7 @@ import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
||||
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
import request from '@/utils/request';
|
||||
import { urlList } from './GisUrlList';
|
||||
import { urlList } from '@/utils/GisUrlList';
|
||||
const mapClass = MapClass.getInstance();
|
||||
const DEFAULT_LAYER_REQUEST_CONCURRENCY = 4;
|
||||
|
||||
@ -707,19 +706,19 @@ export const useMapStore = defineStore('map', () => {
|
||||
'wq_rive_gradient_line'
|
||||
]; // 需要时间搜索的图层key
|
||||
|
||||
let yearTime = moment().subtract(1, 'years'); // 鱼类分布年份
|
||||
let yearTime = dayjs().subtract(1, 'years'); // 鱼类分布年份
|
||||
if (timeRangeLayerKeys.includes(layerKey)) {
|
||||
requestParams.filters.push({
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: moment(searchTimeRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
|
||||
value: dayjs(searchTimeRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
|
||||
});
|
||||
requestParams.filters.push({
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: moment(searchTimeRange.value[1]).format('YYYY-MM-DD 23:59:59')
|
||||
value: dayjs(searchTimeRange.value[1]).format('YYYY-MM-DD 23:59:59')
|
||||
});
|
||||
}
|
||||
if (spjkz.includes(layerKey)) {
|
||||
@ -737,7 +736,7 @@ export const useMapStore = defineStore('map', () => {
|
||||
field: 'startTime',
|
||||
operator: 'eq',
|
||||
dataType: 'date',
|
||||
value: moment(yearTime)
|
||||
value: dayjs(yearTime)
|
||||
.startOf('year')
|
||||
.format('YYYY-MM-DD 00:00:00')
|
||||
});
|
||||
@ -745,7 +744,7 @@ export const useMapStore = defineStore('map', () => {
|
||||
field: 'endTime',
|
||||
operator: 'eq',
|
||||
dataType: 'date',
|
||||
value: moment(yearTime).endOf('year').format('YYYY-MM-DD 23:59:59')
|
||||
value: dayjs(yearTime).endOf('year').format('YYYY-MM-DD 23:59:59')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
// const page =location.href.split('page=');
|
||||
// const zaiJianParams = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{bldsttCcode:1}:{}
|
||||
// const zaiJianParams2 = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{BLDSTT_CCODE:1}:{}
|
||||
@ -262,7 +262,7 @@ export const urlList = [
|
||||
title: '环保设施告警',
|
||||
params: {
|
||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||
yr: moment().format('YYYY')
|
||||
yr: dayjs().format('YYYY')
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -17,8 +17,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moment from 'moment';
|
||||
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ import { ref, computed, onMounted } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
import { getFishReleaseMonitorSectionList } from '@/api/DataQueryMenuModule';
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -70,7 +70,7 @@ import { ref, computed, onMounted } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'export-btn'): void;
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'export-btn'): void;
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
import { getFlowStationSectionList } from '@/api/DataQueryMenuModule';
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { useTimeScale } from '@/composables/useTimeScale';
|
||||
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||
import { getWaterDataSectionList } from '@/api/DataQueryMenuModule';
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -1,491 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons-vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { encrypt } from '@/utils/rsaEncrypt';
|
||||
import {
|
||||
updatePassword,
|
||||
} from "@/api/changePassWord";
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
// 提交状态
|
||||
const submitting = ref(false);
|
||||
|
||||
// 表单数据
|
||||
interface ChangePasswordForm {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
const formState = reactive<ChangePasswordForm>({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
// ==================== 密码校验函数 ====================
|
||||
|
||||
/**
|
||||
* 校验密码复杂度:至少包含四类字符中的三类
|
||||
*/
|
||||
const checkComplexity = (password: string): { valid: boolean; message: string } => {
|
||||
let count = 0;
|
||||
const hasUpper = /[A-Z]/.test(password);
|
||||
const hasLower = /[a-z]/.test(password);
|
||||
const hasNumber = /[0-9]/.test(password);
|
||||
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
||||
|
||||
if (hasUpper) count++;
|
||||
if (hasLower) count++;
|
||||
if (hasNumber) count++;
|
||||
if (hasSpecial) count++;
|
||||
|
||||
if (count >= 3) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类'
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续重复字符
|
||||
*/
|
||||
const checkConsecutiveRepeat = (password: string): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
if (password[i] === password[i + 1]) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上相同字符的连续重复(如11、aa)'
|
||||
};
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减数字
|
||||
*/
|
||||
const checkConsecutiveNumbers = (password: string): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是数字
|
||||
if (curr >= 48 && curr <= 57 && next >= 48 && next <= 57) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的数字(如12、21)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验连续递增/递减字母
|
||||
*/
|
||||
const checkConsecutiveLetters = (password: string): { valid: boolean; message: string } => {
|
||||
for (let i = 0; i < password.length - 1; i++) {
|
||||
const curr = password.charCodeAt(i);
|
||||
const next = password.charCodeAt(i + 1);
|
||||
|
||||
// 检查是否都是字母(大写或小写)
|
||||
const isCurrLetter = (curr >= 65 && curr <= 90) || (curr >= 97 && curr <= 122);
|
||||
const isNextLetter = (next >= 65 && next <= 90) || (next >= 97 && next <= 122);
|
||||
|
||||
if (isCurrLetter && isNextLetter) {
|
||||
if (Math.abs(next - curr) === 1) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含2位及以上连续递增或递减的字母(如ab、ba)'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验用户名关联
|
||||
*/
|
||||
const checkUsernameRelation = (password: string, username: string): { valid: boolean; message: string } => {
|
||||
if (!username) {
|
||||
return { valid: true, message: '' };
|
||||
}
|
||||
|
||||
const lowerPassword = password.toLowerCase();
|
||||
const lowerUsername = username.toLowerCase();
|
||||
|
||||
if (lowerPassword.includes(lowerUsername)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '密码不能包含用户名或其核心部分'
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true, message: '' };
|
||||
};
|
||||
|
||||
// ==================== 表单验证规则 ====================
|
||||
|
||||
const rules = {
|
||||
oldPassword: [
|
||||
{ required: true, message: '请输入旧密码', trigger: 'blur' }
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 10, message: '密码长度不能少于10位', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 校验复杂度
|
||||
const complexity = checkComplexity(value);
|
||||
if (!complexity.valid) {
|
||||
return Promise.reject(new Error(complexity.message));
|
||||
}
|
||||
|
||||
// 校验连续重复
|
||||
const repeat = checkConsecutiveRepeat(value);
|
||||
if (!repeat.valid) {
|
||||
return Promise.reject(new Error(repeat.message));
|
||||
}
|
||||
|
||||
// 校验连续数字
|
||||
const numbers = checkConsecutiveNumbers(value);
|
||||
if (!numbers.valid) {
|
||||
return Promise.reject(new Error(numbers.message));
|
||||
}
|
||||
|
||||
// 校验连续字母
|
||||
const letters = checkConsecutiveLetters(value);
|
||||
if (!letters.valid) {
|
||||
return Promise.reject(new Error(letters.message));
|
||||
}
|
||||
|
||||
// 校验用户名关联
|
||||
const username = userStore.username || '';
|
||||
const usernameCheck = checkUsernameRelation(value, username);
|
||||
if (!usernameCheck.valid) {
|
||||
return Promise.reject(new Error(usernameCheck.message));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value !== formState.newPassword) {
|
||||
return Promise.reject(new Error('两次输入的密码不一致'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// ==================== 接口占位符 ====================
|
||||
|
||||
/**
|
||||
* 修改密码接口(待补充实际后端接口)
|
||||
* @param oldPassword 旧密码(已加密)
|
||||
* @param newPassword 新密码(已加密)
|
||||
*/
|
||||
// const changePasswordApi = async (oldPassword: string, newPassword: string) => {
|
||||
// // TODO: 替换为实际的 API 调用
|
||||
// // 示例:
|
||||
// // return request({
|
||||
// // url: '/user/changePassword',
|
||||
// // method: 'post',
|
||||
// // data: { oldPassword, newPassword }
|
||||
// // });
|
||||
|
||||
// // 模拟延迟
|
||||
// return new Promise((resolve) => {
|
||||
// setTimeout(() => {
|
||||
// resolve({ code: 0, msg: '修改成功' });
|
||||
// }, 1000);
|
||||
// });
|
||||
// };
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
await formRef.value?.validate();
|
||||
|
||||
submitting.value = true;
|
||||
|
||||
// RSA 加密密码
|
||||
const encryptedOldPassword = encrypt(formState.oldPassword);
|
||||
const encryptedNewPassword = encrypt(formState.newPassword);
|
||||
|
||||
if (!encryptedOldPassword || !encryptedNewPassword) {
|
||||
message.error('密码加密失败');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 调用接口
|
||||
const res: any = await updatePassword({id:userStore.userid,password:encryptedNewPassword,oldPassword:encryptedOldPassword});
|
||||
|
||||
// 兼容字符串和数字类型的状态码
|
||||
if (res.code === 0 || res.code === '0') {
|
||||
message.success('密码修改成功,请重新登录');
|
||||
|
||||
// 清空 token 和用户信息
|
||||
userStore.resetToken();
|
||||
|
||||
// 跳转到登录页
|
||||
setTimeout(() => {
|
||||
router.push('/login');
|
||||
}, 1500);
|
||||
} else {
|
||||
message.error(res.msg || '密码修改失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.errorFields) {
|
||||
// 表单验证错误
|
||||
message.error('请检查表单填写是否正确');
|
||||
} else {
|
||||
// 其他错误
|
||||
message.error(error.message || '密码修改失败,请稍后重试');
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
const handleReset = () => {
|
||||
formRef.value?.resetFields();
|
||||
formState.oldPassword = '';
|
||||
formState.newPassword = '';
|
||||
formState.confirmPassword = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 返回上一页
|
||||
*/
|
||||
const handleBack = () => {
|
||||
router.back();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="change-password-container">
|
||||
<a-card :bordered="false" class="password-card">
|
||||
<!-- 标题区域 -->
|
||||
<div class="card-header">
|
||||
<a-button type="text" @click="handleBack" class="back-btn">
|
||||
<ArrowLeftOutlined />
|
||||
</a-button>
|
||||
<h2 class="card-title">修改密码</h2>
|
||||
</div>
|
||||
|
||||
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical" class="password-form">
|
||||
<!-- 旧密码 -->
|
||||
<a-form-item label="旧密码" name="oldPassword">
|
||||
<a-input-password v-model:value="formState.oldPassword" placeholder="请输入旧密码" size="large"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
<!-- 新密码 -->
|
||||
<a-form-item label="新密码" name="newPassword" :dependencies="['username']">
|
||||
<a-input-password v-model:value="formState.newPassword" placeholder="请输入新密码" size="large"
|
||||
allow-clear />
|
||||
|
||||
</a-form-item>
|
||||
|
||||
<!-- 确认新密码 -->
|
||||
<a-form-item label="确认新密码" name="confirmPassword">
|
||||
<a-input-password v-model:value="formState.confirmPassword" placeholder="请再次输入新密码" size="large"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
<!-- 按钮区域 -->
|
||||
<a-form-item class="button-group">
|
||||
<a-button type="primary" size="large" @click="handleSubmit" :loading="submitting"
|
||||
class="submit-btn">
|
||||
提交
|
||||
</a-button>
|
||||
<a-button size="large" @click="handleReset" class="reset-btn">
|
||||
重置
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.change-password-container {
|
||||
min-height: calc(100vh - 110px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40px 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.password-card {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0px;
|
||||
border-bottom: 2px solid #005293;
|
||||
|
||||
.back-btn {
|
||||
font-size: 18px;
|
||||
color: #005293;
|
||||
padding: 4px 8px;
|
||||
margin-right: 12px;
|
||||
|
||||
&:hover {
|
||||
color: #003d6e;
|
||||
background-color: rgba(0, 82, 147, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #005293;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
padding: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.password-form {
|
||||
:deep(.ant-form-item-label) {
|
||||
label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-password) {
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
border-color: #005293;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: #005293;
|
||||
box-shadow: 0 0 0 2px rgba(0, 82, 147, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.password-tips {
|
||||
margin-top: 8px;
|
||||
padding: 12px;
|
||||
background-color: #f5f7fa;
|
||||
border-left: 3px solid #005293;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
|
||||
p {
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 0;
|
||||
|
||||
.submit-btn {
|
||||
width: 120px;
|
||||
margin-right: 16px;
|
||||
background-color: #005293;
|
||||
border-color: #005293;
|
||||
|
||||
&:hover {
|
||||
background-color: #003d6e;
|
||||
border-color: #003d6e;
|
||||
}
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式适配
|
||||
@media (max-width: 768px) {
|
||||
.change-password-container {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.password-card {
|
||||
:deep(.ant-card-body) {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.submit-btn,
|
||||
.reset-btn {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,114 +1,48 @@
|
||||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page401'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRefs } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const state = reactive({
|
||||
errGif: new URL(`../../assets/401_images/401.gif`, import.meta.url).href,
|
||||
|
||||
ewizardClap:
|
||||
'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
|
||||
dialogVisible: false
|
||||
});
|
||||
|
||||
const { errGif, ewizardClap, dialogVisible } = toRefs(state);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function back() {
|
||||
router.back();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
||||
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
|
||||
<h2>你没有权限去该页面</h2>
|
||||
<h6>如有不满请联系你领导</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>或者你可以去:</li>
|
||||
<li class="link-type">
|
||||
<router-link to="/"> 回首页 </router-link>
|
||||
</li>
|
||||
<li class="link-type">
|
||||
<a href="https://www.taobao.com/">随便看看</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" @click.prevent="dialogVisible = true">点我看图</a>
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img
|
||||
:src="errGif"
|
||||
width="313"
|
||||
height="428"
|
||||
alt="Girl has dropped her ice cream."
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog v-model="dialogVisible" title="随便看">
|
||||
<img :src="ewizardClap" class="pan-img" />
|
||||
</el-dialog>
|
||||
<div class="error-page-container">
|
||||
<a-result
|
||||
status="403"
|
||||
title="401"
|
||||
sub-title="抱歉,您没有权限访问该页面,请尝试重新登录或联系管理员"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="goHome">
|
||||
<template #icon><home-outlined /></template>
|
||||
回首页
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
max-width: 100%;
|
||||
margin: 100px auto;
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none !important;
|
||||
const router = useRouter();
|
||||
|
||||
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 调整 Result 组件样式,使其更美观 */
|
||||
.error-page-container :deep(.ant-result) {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,280 +1,47 @@
|
||||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page404'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
function message() {
|
||||
return 'The webmaster said that you can not enter this page...';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img
|
||||
class="pic-404__parent"
|
||||
src="@/assets/404_images/404.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child left"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child mid"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
<img
|
||||
class="pic-404__child right"
|
||||
src="@/assets/404_images/404_cloud.png"
|
||||
alt="404"
|
||||
/>
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__info">
|
||||
All rights reserved
|
||||
<a
|
||||
style="color: #20a0ff"
|
||||
href="https://wallstreetcn.com"
|
||||
target="_blank"
|
||||
>wallstreetcn</a
|
||||
<div class="error-page-container">
|
||||
<a-result
|
||||
status="404"
|
||||
title="404"
|
||||
sub-title="抱歉,您访问的页面不存在,请检查网址或返回首页"
|
||||
>
|
||||
</div>
|
||||
<div class="bullshit__headline">{{ message }}</div>
|
||||
<div class="bullshit__info">
|
||||
Please check that the URL you entered is correct, or click the button
|
||||
below to return to the homepage.
|
||||
</div>
|
||||
<a href="" class="bullshit__return-home">Back to home</a>
|
||||
</div>
|
||||
</div>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="goHome">
|
||||
<template #icon><home-outlined /></template>
|
||||
回首页
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wscn-http404-container {
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__child {
|
||||
position: absolute;
|
||||
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.error-page-container :deep(.ant-result) {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
|
||||
import RightDrawer from "@/components/RightDrawer/index.vue";
|
||||
import jidiInfoMod from "@/modules/jidiInfoMod/index.vue";
|
||||
import shuidianhuangjingjieruMod from "@/modules/shuidianhuangjingjieruMod/index.vue";
|
||||
import { useModelStore } from "@/store/modules/model";
|
||||
import { onMounted, ref } from 'vue';
|
||||
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
|
||||
import RightDrawer from '@/components/RightDrawer/index.vue';
|
||||
import jidiInfoMod from '@/modules/jidiInfoMod/index.vue';
|
||||
import shuidianhuangjingjieruMod from '@/modules/shuidianhuangjingjieruMod/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
|
||||
const modelStore = useModelStore();
|
||||
// import { getQgcStaticData } from "@/api/ecoFlow";
|
||||
@ -27,111 +27,111 @@ onMounted(() => {
|
||||
|
||||
const showMapModal = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "ENG";
|
||||
modelStore.title = "三峡 ";
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = '三峡 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal1 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "WT";
|
||||
modelStore.title = "水温 ";
|
||||
modelStore.params.sttp = 'WT';
|
||||
modelStore.title = '水温 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal2 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "WQFB";
|
||||
modelStore.title = "水质 ";
|
||||
modelStore.params.sttp = 'WQFB';
|
||||
modelStore.title = '水质 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal3 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "FH";
|
||||
modelStore.title = "栖息地 ";
|
||||
modelStore.params.sttp = 'FH';
|
||||
modelStore.title = '栖息地 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
}; //
|
||||
const showMapModal4 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "fh_zq_point";
|
||||
modelStore.title = "栖息地流量 ";
|
||||
modelStore.params.sttp = 'fh_zq_point';
|
||||
modelStore.title = '栖息地流量 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal5 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "fp_point";
|
||||
modelStore.title = "过鱼设施 ";
|
||||
modelStore.params.sttp = 'fp_point';
|
||||
modelStore.title = '过鱼设施 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal6 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "FB";
|
||||
modelStore.title = "鱼类增殖站 ";
|
||||
modelStore.params.sttp = 'FB';
|
||||
modelStore.title = '鱼类增殖站 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
//
|
||||
const showMapModal7 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "VA";
|
||||
modelStore.title = "动物救助站 ";
|
||||
modelStore.params.sttp = 'VA';
|
||||
modelStore.title = '动物救助站 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal8 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "VP";
|
||||
modelStore.title = "珍稀植物园 ";
|
||||
modelStore.params.sttp = 'VP';
|
||||
modelStore.title = '珍稀植物园 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal9 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "WE";
|
||||
modelStore.title = "水生生态调查断面 ";
|
||||
modelStore.params.sttp = 'WE';
|
||||
modelStore.title = '水生生态调查断面 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal10 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "WVA";
|
||||
modelStore.title = "野生动物监测 ";
|
||||
modelStore.params.sttp = 'WVA';
|
||||
modelStore.title = '野生动物监测 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal11 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "VD_FBFM";
|
||||
modelStore.title = "AI视频监控站 ";
|
||||
modelStore.params.sttp = 'VD_FBFM';
|
||||
modelStore.title = 'AI视频监控站 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal12 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "eng_alarm_point";
|
||||
modelStore.title = "水电站告警情况 ";
|
||||
modelStore.params.sttp = 'eng_alarm_point';
|
||||
modelStore.title = '水电站告警情况 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal13 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "FPRD";
|
||||
modelStore.title = "鱼类调查装置 ";
|
||||
modelStore.params.sttp = 'FPRD';
|
||||
modelStore.title = '鱼类调查装置 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal14 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "DW_2";
|
||||
modelStore.title = "低温水减缓设施-叠梁门 ";
|
||||
modelStore.params.sttp = 'DW_2';
|
||||
modelStore.title = '低温水减缓设施-叠梁门 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal15 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "DW_5";
|
||||
modelStore.title = "前置挡墙 ";
|
||||
modelStore.params.sttp = 'DW_5';
|
||||
modelStore.title = '前置挡墙 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal16 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "DW_6";
|
||||
modelStore.title = "隔水幕墙 ";
|
||||
modelStore.params.sttp = 'DW_6';
|
||||
modelStore.title = '隔水幕墙 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
const showMapModal17 = () => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = "DW_1";
|
||||
modelStore.title = "夹岩双层取水 ";
|
||||
modelStore.params.sttp = 'DW_1';
|
||||
modelStore.title = '夹岩双层取水 ';
|
||||
modelStore.isBasicEdit = true;
|
||||
};
|
||||
//DW_5
|
||||
|
||||
@ -246,15 +246,13 @@ import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vu
|
||||
import { getCaptcha } from "@/api/auth";
|
||||
import { message } from "ant-design-vue";
|
||||
// 组件依赖
|
||||
|
||||
import router from "@/router";
|
||||
import Cookies from "js-cookie";
|
||||
// API依赖
|
||||
import { useRoute } from "vue-router";
|
||||
import { LoginData } from "@/api/auth/types";
|
||||
|
||||
//密码加密
|
||||
import { encrypt, decrypt } from "@/utils/rsaEncrypt";
|
||||
import { encrypt } from "@/utils/rsaEncrypt";
|
||||
|
||||
// 状态管理依赖
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
@ -50,7 +50,7 @@ const btnLoading = ref<boolean>(false);
|
||||
const initSearchData = {
|
||||
rvcd: 'all',
|
||||
stcd: null,
|
||||
reportMonth: moment().subtract(1, 'month').format('YYYY-MM')
|
||||
reportMonth: dayjs().subtract(1, 'month').format('YYYY-MM')
|
||||
};
|
||||
|
||||
const searchData = ref<any>({ ...initSearchData });
|
||||
|
||||
@ -338,7 +338,6 @@ const videoFileList = ref<any[]>([]);
|
||||
|
||||
// 禁用今天之后的日期
|
||||
const disabledDate = (current: any) => {
|
||||
// current 是 moment/dayjs 对象
|
||||
// 返回 true 表示禁用
|
||||
return current && current > dayjs().endOf('day');
|
||||
};
|
||||
@ -370,7 +369,7 @@ const getHbrvcdDropdownSelect = async () => {
|
||||
const res = await getSelectForDropdown({});
|
||||
let list = res.data || [];
|
||||
if (list && list.length > 0) {
|
||||
list = list.filter((item: any) => item.rvcd !== "all");
|
||||
list = list.filter((item: any) => item.rvcd !== 'all');
|
||||
}
|
||||
hbrvcdOption.value = list;
|
||||
} catch (error) {
|
||||
@ -469,13 +468,13 @@ const filterOption = (inputValue: string, option: any) => {
|
||||
};
|
||||
// 验证规则
|
||||
const rules: Record<string, Rule[]> = {
|
||||
rvcd: [{ required: true, message: "请选择流域", trigger: "change" }],
|
||||
rstcd: [{ required: true, message: "请选择电站", trigger: "change" }],
|
||||
stcd: [{ required: true, message: "请选择过鱼设施", trigger: "change" }],
|
||||
strdt: [{ required: true, message: "请选择过鱼时间", trigger: "change" }],
|
||||
ftp: [{ required: true, message: "请选择鱼种类", trigger: "change" }],
|
||||
direction: [{ required: true, message: "请选择游向", trigger: "change" }],
|
||||
fcnt: [{ required: true, message: "请输入过鱼数量", trigger: "change" }],
|
||||
rvcd: [{ required: true, message: '请选择流域', trigger: 'change' }],
|
||||
rstcd: [{ required: true, message: '请选择电站', trigger: 'change' }],
|
||||
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||
strdt: [{ required: true, message: '请选择过鱼时间', trigger: 'change' }],
|
||||
ftp: [{ required: true, message: '请选择鱼种类', trigger: 'change' }],
|
||||
direction: [{ required: true, message: '请选择游向', trigger: 'change' }],
|
||||
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 计算是否为编辑模式
|
||||
|
||||