修改 数据填报,打包报错修改,框架迁移遗留问题;修改401/404显示逻辑;去除页面中的moment改用dayjs;
@ -24,6 +24,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: ['vue', '@typescript-eslint'],
|
plugins: ['vue', '@typescript-eslint'],
|
||||||
rules: {
|
rules: {
|
||||||
|
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||||
'vue/multi-word-component-names': 'off',
|
'vue/multi-word-component-names': 'off',
|
||||||
'@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查
|
'@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查
|
||||||
'@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告
|
'@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">
|
<script setup lang="ts">
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
import { ref } from 'vue';
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Modal } from "ant-design-vue";
|
import { Modal } from 'ant-design-vue';
|
||||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons-vue";
|
import { UserOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
||||||
// 国际化
|
// 国际化
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// import LangSelect from '@/components/LangSelect/index.vue';
|
// 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 { useTagsViewStore } from '@/store/modules/tagsView';
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
|
||||||
// const url = import.meta.env.VITE_APP_BASE_API;
|
|
||||||
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
const tagsViewStore = useTagsViewStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@ -21,10 +20,10 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
function logout() {
|
function logout() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示?",
|
title: '提示?',
|
||||||
content: "确定注销并退出系统吗?",
|
content: '确定注销并退出系统吗?',
|
||||||
okText: "确定",
|
okText: '确定',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
userStore
|
userStore
|
||||||
.logout()
|
.logout()
|
||||||
@ -34,14 +33,13 @@ function logout() {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
router.push(`/login`);
|
router.push(`/login`);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const changePasswordVisible = ref(false);
|
||||||
function changePassword() {
|
function changePassword() {
|
||||||
router.push("/changePassword");
|
changePasswordVisible.value = true;
|
||||||
}
|
}
|
||||||
onMounted(() => {});
|
|
||||||
onBeforeUnmount(() => {});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -52,8 +50,9 @@ onBeforeUnmount(() => {});
|
|||||||
href="/"
|
href="/"
|
||||||
class="h-[50px] min-w-[350px] flex items-center justify-center text-white"
|
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]">
|
||||||
<h1 class="text-blank font-bold text-[16px]">{{ t("login.titleSjtb") }}</h1>
|
{{ t('login.titleSjtb') }}
|
||||||
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
</transition>
|
</transition>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -82,11 +81,12 @@ onBeforeUnmount(() => {});
|
|||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-layout-header>
|
</a-layout-header>
|
||||||
|
<ChangePassword v-model:open="changePasswordVisible" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use "@/styles/variables.module.scss" as *;
|
@use '@/styles/variables.module.scss' as *;
|
||||||
.navbar {
|
.navbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
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';
|
import 'dayjs/locale/zh-cn';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import 'virtual:svg-icons-register';
|
import 'virtual:svg-icons-register';
|
||||||
// 3d地图
|
|
||||||
import * as Cesium from 'cesium';
|
|
||||||
import 'cesium/Build/Cesium/Widgets/widgets.css';
|
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
import i18n from '@/lang/index';
|
import i18n from '@/lang/index';
|
||||||
|
|
||||||
import '@/styles/index.scss';
|
import '@/styles/index.scss';
|
||||||
import 'element-plus/theme-chalk/index.css';
|
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);
|
const app = createApp(App);
|
||||||
// 自定义指令
|
// 自定义指令
|
||||||
@ -48,7 +42,5 @@ app
|
|||||||
.use(router)
|
.use(router)
|
||||||
.use(ElementPlus)
|
.use(ElementPlus)
|
||||||
.use(Antd)
|
.use(Antd)
|
||||||
.use(Antd)
|
|
||||||
.use(WujieVue)
|
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
|||||||
@ -52,7 +52,6 @@ function findFirstAvailableRoute(routes: any[]): string | undefined {
|
|||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
const userStore = useUserStoreHook();
|
const userStore = useUserStoreHook();
|
||||||
|
|
||||||
if (userStore.Token) {
|
if (userStore.Token) {
|
||||||
// 登录成功,跳转到首页
|
// 登录成功,跳转到首页
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
@ -60,6 +59,15 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
next({ path: '/' });
|
next({ path: '/' });
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
return;
|
return;
|
||||||
|
} else if (
|
||||||
|
(to.path === '/401' || to.path === '/404') &&
|
||||||
|
from.matched.length === 0 &&
|
||||||
|
!from.name
|
||||||
|
) {
|
||||||
|
// 拦截直接 URL 访问错误页面,仅允许内部重定向触发
|
||||||
|
next({ path: '/' });
|
||||||
|
NProgress.done();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
const hasGetUserInfo = userStore.roles.length > 0;
|
const hasGetUserInfo = userStore.roles.length > 0;
|
||||||
if (hasGetUserInfo) {
|
if (hasGetUserInfo) {
|
||||||
|
|||||||
@ -35,12 +35,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||||||
path: '/401',
|
path: '/401',
|
||||||
component: () => import('@/views/error-page/401.vue'),
|
component: () => import('@/views/error-page/401.vue'),
|
||||||
meta: { hidden: true }
|
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';
|
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();
|
const user = useUserStoreHook();
|
||||||
if (user.Token) {
|
if (user.Token) {
|
||||||
config.headers.token = getToken();
|
config.headers.token = getToken();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(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>
|
<template>
|
||||||
<div class="errPage-container">
|
<div class="error-page-container">
|
||||||
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
|
<a-result
|
||||||
返回
|
status="403"
|
||||||
</el-button>
|
title="401"
|
||||||
<el-row>
|
sub-title="抱歉,您没有权限访问该页面,请尝试重新登录或联系管理员"
|
||||||
<el-col :span="12">
|
>
|
||||||
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
<template #extra>
|
||||||
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
|
<a-space>
|
||||||
<h2>你没有权限去该页面</h2>
|
<a-button type="primary" @click="goHome">
|
||||||
<h6>如有不满请联系你领导</h6>
|
<template #icon><home-outlined /></template>
|
||||||
<ul class="list-unstyled">
|
回首页
|
||||||
<li>或者你可以去:</li>
|
</a-button>
|
||||||
<li class="link-type">
|
</a-space>
|
||||||
<router-link to="/"> 回首页 </router-link>
|
</template>
|
||||||
</li>
|
</a-result>
|
||||||
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<script setup lang="ts">
|
||||||
.errPage-container {
|
import { useRouter } from 'vue-router';
|
||||||
width: 800px;
|
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
max-width: 100%;
|
|
||||||
margin: 100px auto;
|
|
||||||
|
|
||||||
.pan-back-btn {
|
const router = useRouter();
|
||||||
background: #008489;
|
|
||||||
color: #fff;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pan-gif {
|
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||||
margin: 0 auto;
|
const goHome = () => {
|
||||||
display: block;
|
router.push('/');
|
||||||
}
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
.pan-img {
|
<style scoped>
|
||||||
display: block;
|
.error-page-container {
|
||||||
margin: 0 auto;
|
height: 100vh;
|
||||||
width: 100%;
|
display: flex;
|
||||||
}
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
.text-jumbo {
|
/* 调整 Result 组件样式,使其更美观 */
|
||||||
font-size: 60px;
|
.error-page-container :deep(.ant-result) {
|
||||||
font-weight: 700;
|
padding: 48px 32px;
|
||||||
color: #484848;
|
background: #fff;
|
||||||
}
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
.list-unstyled {
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #008489;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<div class="wscn-http404-container">
|
<div class="error-page-container">
|
||||||
<div class="wscn-http404">
|
<a-result
|
||||||
<div class="pic-404">
|
status="404"
|
||||||
<img
|
title="404"
|
||||||
class="pic-404__parent"
|
sub-title="抱歉,您访问的页面不存在,请检查网址或返回首页"
|
||||||
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>
|
<template #extra>
|
||||||
<div class="bullshit__headline">{{ message }}</div>
|
<a-space>
|
||||||
<div class="bullshit__info">
|
<a-button type="primary" @click="goHome">
|
||||||
Please check that the URL you entered is correct, or click the button
|
<template #icon><home-outlined /></template>
|
||||||
below to return to the homepage.
|
回首页
|
||||||
</div>
|
</a-button>
|
||||||
<a href="" class="bullshit__return-home">Back to home</a>
|
</a-space>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</a-result>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<script setup lang="ts">
|
||||||
.wscn-http404-container {
|
import { useRouter } from 'vue-router';
|
||||||
transform: translate(-50%, -50%);
|
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
const router = useRouter();
|
||||||
left: 50%;
|
|
||||||
|
// 返回首页(由 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 {
|
.error-page-container :deep(.ant-result) {
|
||||||
position: relative;
|
padding: 48px 32px;
|
||||||
width: 1200px;
|
background: #fff;
|
||||||
padding: 0 50px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { ref, computed, onMounted, watch } from 'vue';
|
import { ref, computed, onMounted, watch } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { useShuJuTianBaoStore } from '@/store/modules/shuJuTianBao';
|
import { useShuJuTianBaoStore } from '@/store/modules/shuJuTianBao';
|
||||||
@ -50,7 +49,7 @@ const btnLoading = ref<boolean>(false);
|
|||||||
const initSearchData = {
|
const initSearchData = {
|
||||||
rvcd: 'all',
|
rvcd: 'all',
|
||||||
stcd: null,
|
stcd: null,
|
||||||
reportMonth: moment().subtract(1, 'month').format('YYYY-MM')
|
reportMonth: dayjs().subtract(1, 'month').format('YYYY-MM')
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchData = ref<any>({ ...initSearchData });
|
const searchData = ref<any>({ ...initSearchData });
|
||||||
|
|||||||
@ -338,7 +338,6 @@ const videoFileList = ref<any[]>([]);
|
|||||||
|
|
||||||
// 禁用今天之后的日期
|
// 禁用今天之后的日期
|
||||||
const disabledDate = (current: any) => {
|
const disabledDate = (current: any) => {
|
||||||
// current 是 moment/dayjs 对象
|
|
||||||
// 返回 true 表示禁用
|
// 返回 true 表示禁用
|
||||||
return current && current > dayjs().endOf('day');
|
return current && current > dayjs().endOf('day');
|
||||||
};
|
};
|
||||||
@ -370,7 +369,7 @@ const getHbrvcdDropdownSelect = async () => {
|
|||||||
const res = await getSelectForDropdown({});
|
const res = await getSelectForDropdown({});
|
||||||
let list = res.data || [];
|
let list = res.data || [];
|
||||||
if (list && list.length > 0) {
|
if (list && list.length > 0) {
|
||||||
list = list.filter((item: any) => item.rvcd !== "all");
|
list = list.filter((item: any) => item.rvcd !== 'all');
|
||||||
}
|
}
|
||||||
hbrvcdOption.value = list;
|
hbrvcdOption.value = list;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -469,13 +468,13 @@ const filterOption = (inputValue: string, option: any) => {
|
|||||||
};
|
};
|
||||||
// 验证规则
|
// 验证规则
|
||||||
const rules: Record<string, Rule[]> = {
|
const rules: Record<string, Rule[]> = {
|
||||||
rvcd: [{ required: true, message: "请选择流域", trigger: "change" }],
|
rvcd: [{ required: true, message: '请选择流域', trigger: 'change' }],
|
||||||
rstcd: [{ required: true, message: "请选择电站", trigger: "change" }],
|
rstcd: [{ required: true, message: '请选择电站', trigger: 'change' }],
|
||||||
stcd: [{ required: true, message: "请选择过鱼设施", trigger: "change" }],
|
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||||
strdt: [{ required: true, message: "请选择过鱼时间", trigger: "change" }],
|
strdt: [{ required: true, message: '请选择过鱼时间', trigger: 'change' }],
|
||||||
ftp: [{ required: true, message: "请选择鱼种类", trigger: "change" }],
|
ftp: [{ required: true, message: '请选择鱼种类', trigger: 'change' }],
|
||||||
direction: [{ required: true, message: "请选择游向", trigger: "change" }],
|
direction: [{ required: true, message: '请选择游向', trigger: 'change' }],
|
||||||
fcnt: [{ required: true, message: "请输入过鱼数量", trigger: "change" }],
|
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'change' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算是否为编辑模式
|
// 计算是否为编辑模式
|
||||||
|
|||||||
@ -103,11 +103,6 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, onMounted, watch } from 'vue';
|
import { ref, computed, onMounted, watch } from 'vue';
|
||||||
import {
|
|
||||||
SaveOutlined,
|
|
||||||
CheckSquareOutlined,
|
|
||||||
QuestionOutlined
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue'; // 确保路径正确
|
import BasicSearch from '@/components/BasicSearch/index.vue'; // 确保路径正确
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, onMounted, watch } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import BasicSearch from "@/components/BasicSearch/index.vue";
|
import BasicSearch from "@/components/BasicSearch/index.vue";
|
||||||
import { DateSetting } from "@/utils/enumeration";
|
import { DateSetting } from "@/utils/enumeration";
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 BasicSearch from "@/components/BasicSearch/index.vue"; // 确保路径正确
|
||||||
import { getDictItemsByCode } from "@/api/dict";
|
import { getDictItemsByCode } from "@/api/dict";
|
||||||
import { CheckSquareOutlined, CloseCircleOutlined } from "@ant-design/icons-vue";
|
import { CheckSquareOutlined, CloseCircleOutlined } from "@ant-design/icons-vue";
|
||||||
|
|||||||
@ -33,79 +33,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path =>
|
rewrite: path =>
|
||||||
path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
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: {
|
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>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||||
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
||||||
@ -122,7 +122,7 @@ const formModel = ref({
|
|||||||
anchorPointSelect: null,
|
anchorPointSelect: null,
|
||||||
fishSurveyZhuanZhi: false
|
fishSurveyZhuanZhi: false
|
||||||
});
|
});
|
||||||
const currentDate = moment();
|
const currentDate = dayjs();
|
||||||
const defaultYear =
|
const defaultYear =
|
||||||
currentDate.month() < 6 ? currentDate.year() - 1 : currentDate.year();
|
currentDate.month() < 6 ? currentDate.year() - 1 : currentDate.year();
|
||||||
|
|
||||||
@ -132,8 +132,8 @@ const fishSurveyZhuanZhiParams = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const searchTimeRange = ref([
|
const searchTimeRange = ref([
|
||||||
moment(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
dayjs(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
moment(mapViewStore.searchTimeRange[1]).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();
|
mapOrchestrator.resetFilterState();
|
||||||
searchTimeRange.value = [
|
searchTimeRange.value = [
|
||||||
moment(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
dayjs(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
moment(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
dayjs(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||||
];
|
];
|
||||||
formModel.value = {
|
formModel.value = {
|
||||||
siteRangePicker: null,
|
siteRangePicker: null,
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
import { ref } from 'vue';
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Modal } from "ant-design-vue";
|
import { Modal } from 'ant-design-vue';
|
||||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons-vue";
|
import { UserOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
||||||
// 国际化
|
// 国际化
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from 'vue-i18n';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// import LangSelect from '@/components/LangSelect/index.vue';
|
// 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 { useTagsViewStore } from '@/store/modules/tagsView';
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
|
||||||
// const url = import.meta.env.VITE_APP_BASE_API;
|
|
||||||
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
const tagsViewStore = useTagsViewStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@ -21,10 +20,10 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
function logout() {
|
function logout() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示?",
|
title: '提示?',
|
||||||
content: "确定注销并退出系统吗?",
|
content: '确定注销并退出系统吗?',
|
||||||
okText: "确定",
|
okText: '确定',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
userStore
|
userStore
|
||||||
.logout()
|
.logout()
|
||||||
@ -34,14 +33,13 @@ function logout() {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
router.push(`/login`);
|
router.push(`/login`);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const changePasswordVisible = ref(false);
|
||||||
function changePassword() {
|
function changePassword() {
|
||||||
router.push("/changePassword");
|
changePasswordVisible.value = true;
|
||||||
}
|
}
|
||||||
onMounted(() => {});
|
|
||||||
onBeforeUnmount(() => {});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -52,8 +50,9 @@ onBeforeUnmount(() => {});
|
|||||||
href="/"
|
href="/"
|
||||||
class="h-[50px] min-w-[350px] flex items-center justify-center text-white"
|
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]">
|
||||||
<h1 class="text-blank font-bold text-[16px]">{{ t("login.title") }}</h1>
|
{{ t('login.title') }}
|
||||||
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
</transition>
|
</transition>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -82,11 +81,12 @@ onBeforeUnmount(() => {});
|
|||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-layout-header>
|
</a-layout-header>
|
||||||
|
<ChangePassword v-model:open="changePasswordVisible" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use "@/styles/variables.module.scss" as *;
|
@use '@/styles/variables.module.scss' as *;
|
||||||
.navbar {
|
.navbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
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 '@/styles/index.scss';
|
||||||
import 'element-plus/theme-chalk/index.css';
|
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);
|
const app = createApp(App);
|
||||||
// 自定义指令
|
// 自定义指令
|
||||||
@ -49,6 +47,5 @@ app
|
|||||||
.use(ElementPlus)
|
.use(ElementPlus)
|
||||||
.use(Antd)
|
.use(Antd)
|
||||||
.use(Antd)
|
.use(Antd)
|
||||||
.use(WujieVue)
|
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { watch, type WatchStopHandle } from 'vue';
|
import { watch, type WatchStopHandle } from 'vue';
|
||||||
import { unByKey } from 'ol/Observable';
|
import { unByKey } from 'ol/Observable';
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
import { MapClass } from '@/components/gis/map.class';
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
import { getMapConfig, layerConfig2Flat } from '@/components/gis/gisUtils';
|
import { getMapConfig, layerConfig2Flat } from '@/components/gis/gisUtils';
|
||||||
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
||||||
@ -393,7 +393,7 @@ export const useMapOrchestrator = () => {
|
|||||||
|
|
||||||
// 备注:统一重置地图筛选表单依赖的时间范围,供筛选组件在切页时复位默认输入。
|
// 备注:统一重置地图筛选表单依赖的时间范围,供筛选组件在切页时复位默认输入。
|
||||||
const resetFilterState = () => {
|
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 { defineStore } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export const useMapViewStore = defineStore('map-view', () => {
|
export const useMapViewStore = defineStore('map-view', () => {
|
||||||
const checkedLayerKeys = ref<string[]>([]);
|
const checkedLayerKeys = ref<string[]>([]);
|
||||||
const legendCheckedState = ref<Record<string, number>>({});
|
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 selectedBaseId = ref('');
|
||||||
const currentZoomLevel = ref(4.5);
|
const currentZoomLevel = ref(4.5);
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ export const useMapViewStore = defineStore('map-view', () => {
|
|||||||
const resetViewState = () => {
|
const resetViewState = () => {
|
||||||
checkedLayerKeys.value = [];
|
checkedLayerKeys.value = [];
|
||||||
legendCheckedState.value = {};
|
legendCheckedState.value = {};
|
||||||
searchTimeRange.value = [moment().subtract(1, 'M'), moment()];
|
searchTimeRange.value = [dayjs().subtract(1, 'M'), dayjs()];
|
||||||
selectedBaseId.value = '';
|
selectedBaseId.value = '';
|
||||||
currentZoomLevel.value = 4.5;
|
currentZoomLevel.value = 4.5;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<div v-if="modalData" class="modal-content">
|
<div v-if="modalData" class="modal-content">
|
||||||
<MonthlyAverage
|
<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" />
|
:dataDimensionVal="modalData.baseid" :rvcd='modalData.rvcd' :stcd="modalData.stcd" :jdList="JidiSelectEventStore.jidiData" />
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@ -37,7 +37,7 @@ import { wbsbGetKendoList, avgMonGetKendoListCust } from "@/api/sw";
|
|||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
import MonthlyAverage from './monthlyAverage.vue'
|
import MonthlyAverage from './monthlyAverage.vue'
|
||||||
import MonthlyAverageMaxModal from './TwoLayers/monthlyAverageMaxModal.vue'
|
import MonthlyAverageMaxModal from './TwoLayers/monthlyAverageMaxModal.vue'
|
||||||
import moment from 'moment'
|
import dayjs from 'dayjs'
|
||||||
import {
|
import {
|
||||||
ZoomInOutlined
|
ZoomInOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
|
|||||||
@ -104,7 +104,7 @@ import { message } from 'ant-design-vue';
|
|||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts';
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { wbsbGetKendoList, getVmsstbprpt, DetGetKendoListCust } from '@/api/sw';
|
import { wbsbGetKendoList, getVmsstbprpt, DetGetKendoListCust } from '@/api/sw';
|
||||||
import BasicTable from '@/components/BasicTable/index.vue';
|
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)
|
const tmValue = dayjs.isDayjs(formValue.tm)
|
||||||
? formValue.tm.format('YYYY-MM-DD 00:00:00')
|
? 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 = {
|
const values = {
|
||||||
...formValue,
|
...formValue,
|
||||||
@ -694,12 +694,12 @@ const handleExport = async () => {
|
|||||||
try {
|
try {
|
||||||
const tmValue = dayjs.isDayjs(formValue.tm)
|
const tmValue = dayjs.isDayjs(formValue.tm)
|
||||||
? formValue.tm.format('YYYY-MM-DD 00:00:00')
|
? 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 = {
|
const params = {
|
||||||
...buildFilterParams({ ...formValue, tm: tmValue }),
|
...buildFilterParams({ ...formValue, tm: tmValue }),
|
||||||
exportType: 'excel',
|
exportType: 'excel',
|
||||||
exportFileName: `月平均水温历史对比 ${moment().format(
|
exportFileName: `月平均水温历史对比 ${dayjs().format(
|
||||||
'YYYY-MM-DD HH-mm-ss'
|
'YYYY-MM-DD HH-mm-ss'
|
||||||
)}`
|
)}`
|
||||||
};
|
};
|
||||||
|
|||||||
@ -60,6 +60,15 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
next({ path: '/' });
|
next({ path: '/' });
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
return;
|
return;
|
||||||
|
} else if (
|
||||||
|
(to.path === '/401' || to.path === '/404') &&
|
||||||
|
from.matched.length === 0 &&
|
||||||
|
!from.name
|
||||||
|
) {
|
||||||
|
// 拦截直接 URL 访问错误页面,仅允许内部重定向触发
|
||||||
|
next({ path: '/' });
|
||||||
|
NProgress.done();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
const hasGetUserInfo = userStore.roles.length > 0;
|
const hasGetUserInfo = userStore.roles.length > 0;
|
||||||
if (hasGetUserInfo) {
|
if (hasGetUserInfo) {
|
||||||
|
|||||||
@ -35,12 +35,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||||||
path: '/401',
|
path: '/401',
|
||||||
component: () => import('@/views/error-page/401.vue'),
|
component: () => import('@/views/error-page/401.vue'),
|
||||||
meta: { hidden: true }
|
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 { defineStore, storeToRefs } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { omit } from 'lodash';
|
import dayjs from 'dayjs';
|
||||||
import moment from 'moment';
|
|
||||||
import { MapClass } from '@/components/gis/map.class';
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
import { applyLayerMutualExclusionRules } from '@/modules/map/domain/map-layer-rules';
|
import { applyLayerMutualExclusionRules } from '@/modules/map/domain/map-layer-rules';
|
||||||
import {
|
import {
|
||||||
@ -13,7 +12,7 @@ import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
|||||||
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
||||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import { urlList } from './GisUrlList';
|
import { urlList } from '@/utils/GisUrlList';
|
||||||
const mapClass = MapClass.getInstance();
|
const mapClass = MapClass.getInstance();
|
||||||
const DEFAULT_LAYER_REQUEST_CONCURRENCY = 4;
|
const DEFAULT_LAYER_REQUEST_CONCURRENCY = 4;
|
||||||
|
|
||||||
@ -707,19 +706,19 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
'wq_rive_gradient_line'
|
'wq_rive_gradient_line'
|
||||||
]; // 需要时间搜索的图层key
|
]; // 需要时间搜索的图层key
|
||||||
|
|
||||||
let yearTime = moment().subtract(1, 'years'); // 鱼类分布年份
|
let yearTime = dayjs().subtract(1, 'years'); // 鱼类分布年份
|
||||||
if (timeRangeLayerKeys.includes(layerKey)) {
|
if (timeRangeLayerKeys.includes(layerKey)) {
|
||||||
requestParams.filters.push({
|
requestParams.filters.push({
|
||||||
field: 'tm',
|
field: 'tm',
|
||||||
operator: 'gte',
|
operator: 'gte',
|
||||||
dataType: 'date',
|
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({
|
requestParams.filters.push({
|
||||||
field: 'tm',
|
field: 'tm',
|
||||||
operator: 'lte',
|
operator: 'lte',
|
||||||
dataType: 'date',
|
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)) {
|
if (spjkz.includes(layerKey)) {
|
||||||
@ -737,7 +736,7 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
field: 'startTime',
|
field: 'startTime',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'date',
|
dataType: 'date',
|
||||||
value: moment(yearTime)
|
value: dayjs(yearTime)
|
||||||
.startOf('year')
|
.startOf('year')
|
||||||
.format('YYYY-MM-DD 00:00:00')
|
.format('YYYY-MM-DD 00:00:00')
|
||||||
});
|
});
|
||||||
@ -745,7 +744,7 @@ export const useMapStore = defineStore('map', () => {
|
|||||||
field: 'endTime',
|
field: 'endTime',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'date',
|
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 page =location.href.split('page=');
|
||||||
// const zaiJianParams = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{bldsttCcode:1}:{}
|
// const zaiJianParams = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{bldsttCcode:1}:{}
|
||||||
// const zaiJianParams2 = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{BLDSTT_CCODE:1}:{}
|
// const zaiJianParams2 = page?.[1]==="zaiJianDianZhanHuanBaoGongZuo"?{BLDSTT_CCODE:1}:{}
|
||||||
@ -262,7 +262,7 @@ export const urlList = [
|
|||||||
title: '环保设施告警',
|
title: '环保设施告警',
|
||||||
params: {
|
params: {
|
||||||
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
lgtd: [{ field: 'lgtd', operator: 'isnotnull' }],
|
||||||
yr: moment().format('YYYY')
|
yr: dayjs().format('YYYY')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -17,8 +17,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
|
|
||||||
|
|||||||
@ -66,7 +66,7 @@ import { ref, computed, onMounted } from 'vue';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
import { getFishReleaseMonitorSectionList } from '@/api/DataQueryMenuModule';
|
import { getFishReleaseMonitorSectionList } from '@/api/DataQueryMenuModule';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@ -70,7 +70,7 @@ import { ref, computed, onMounted } from 'vue';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'export-btn'): void;
|
(e: 'export-btn'): void;
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'export-btn'): void;
|
(e: 'export-btn'): void;
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
import { getFlowStationSectionList } from '@/api/DataQueryMenuModule';
|
import { getFlowStationSectionList } from '@/api/DataQueryMenuModule';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
import { getSurfaceTempSectionList } from '@/api/DataQueryMenuModule';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
import { DateSetting } from '@/utils/enumeration';
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
import { useTimeScale } from '@/composables/useTimeScale';
|
import { useTimeScale } from '@/store/composables/useTimeScale';
|
||||||
import { getWaterDataSectionList } from '@/api/DataQueryMenuModule';
|
import { getWaterDataSectionList } from '@/api/DataQueryMenuModule';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
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>
|
<template>
|
||||||
<div class="errPage-container">
|
<div class="error-page-container">
|
||||||
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
|
<a-result
|
||||||
返回
|
status="403"
|
||||||
</el-button>
|
title="401"
|
||||||
<el-row>
|
sub-title="抱歉,您没有权限访问该页面,请尝试重新登录或联系管理员"
|
||||||
<el-col :span="12">
|
>
|
||||||
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
<template #extra>
|
||||||
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
|
<a-space>
|
||||||
<h2>你没有权限去该页面</h2>
|
<a-button type="primary" @click="goHome">
|
||||||
<h6>如有不满请联系你领导</h6>
|
<template #icon><home-outlined /></template>
|
||||||
<ul class="list-unstyled">
|
回首页
|
||||||
<li>或者你可以去:</li>
|
</a-button>
|
||||||
<li class="link-type">
|
</a-space>
|
||||||
<router-link to="/"> 回首页 </router-link>
|
</template>
|
||||||
</li>
|
</a-result>
|
||||||
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<script setup lang="ts">
|
||||||
.errPage-container {
|
import { useRouter } from 'vue-router';
|
||||||
width: 800px;
|
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
max-width: 100%;
|
|
||||||
margin: 100px auto;
|
|
||||||
|
|
||||||
.pan-back-btn {
|
const router = useRouter();
|
||||||
background: #008489;
|
|
||||||
color: #fff;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pan-gif {
|
// 返回首页(由 permission 守卫动态匹配第一个可用路由)
|
||||||
margin: 0 auto;
|
const goHome = () => {
|
||||||
display: block;
|
router.push('/');
|
||||||
}
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
.pan-img {
|
<style scoped>
|
||||||
display: block;
|
.error-page-container {
|
||||||
margin: 0 auto;
|
height: 100vh;
|
||||||
width: 100%;
|
display: flex;
|
||||||
}
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
.text-jumbo {
|
/* 调整 Result 组件样式,使其更美观 */
|
||||||
font-size: 60px;
|
.error-page-container :deep(.ant-result) {
|
||||||
font-weight: 700;
|
padding: 48px 32px;
|
||||||
color: #484848;
|
background: #fff;
|
||||||
}
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
.list-unstyled {
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #008489;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<div class="wscn-http404-container">
|
<div class="error-page-container">
|
||||||
<div class="wscn-http404">
|
<a-result
|
||||||
<div class="pic-404">
|
status="404"
|
||||||
<img
|
title="404"
|
||||||
class="pic-404__parent"
|
sub-title="抱歉,您访问的页面不存在,请检查网址或返回首页"
|
||||||
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>
|
<template #extra>
|
||||||
<div class="bullshit__headline">{{ message }}</div>
|
<a-space>
|
||||||
<div class="bullshit__info">
|
<a-button type="primary" @click="goHome">
|
||||||
Please check that the URL you entered is correct, or click the button
|
<template #icon><home-outlined /></template>
|
||||||
below to return to the homepage.
|
回首页
|
||||||
</div>
|
</a-button>
|
||||||
<a href="" class="bullshit__return-home">Back to home</a>
|
</a-space>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</a-result>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<script setup lang="ts">
|
||||||
.wscn-http404-container {
|
import { useRouter } from 'vue-router';
|
||||||
transform: translate(-50%, -50%);
|
import { HomeOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
const router = useRouter();
|
||||||
left: 50%;
|
|
||||||
|
// 返回首页(由 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 {
|
.error-page-container :deep(.ant-result) {
|
||||||
position: relative;
|
padding: 48px 32px;
|
||||||
width: 1200px;
|
background: #fff;
|
||||||
padding: 0 50px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from 'vue';
|
||||||
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
|
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
|
||||||
import RightDrawer from "@/components/RightDrawer/index.vue";
|
import RightDrawer from '@/components/RightDrawer/index.vue';
|
||||||
import jidiInfoMod from "@/modules/jidiInfoMod/index.vue";
|
import jidiInfoMod from '@/modules/jidiInfoMod/index.vue';
|
||||||
import shuidianhuangjingjieruMod from "@/modules/shuidianhuangjingjieruMod/index.vue";
|
import shuidianhuangjingjieruMod from '@/modules/shuidianhuangjingjieruMod/index.vue';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
// import { getQgcStaticData } from "@/api/ecoFlow";
|
// import { getQgcStaticData } from "@/api/ecoFlow";
|
||||||
@ -27,111 +27,111 @@ onMounted(() => {
|
|||||||
|
|
||||||
const showMapModal = () => {
|
const showMapModal = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "ENG";
|
modelStore.params.sttp = 'ENG';
|
||||||
modelStore.title = "三峡 ";
|
modelStore.title = '三峡 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal1 = () => {
|
const showMapModal1 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WT";
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.title = "水温 ";
|
modelStore.title = '水温 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal2 = () => {
|
const showMapModal2 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WQFB";
|
modelStore.params.sttp = 'WQFB';
|
||||||
modelStore.title = "水质 ";
|
modelStore.title = '水质 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal3 = () => {
|
const showMapModal3 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "FH";
|
modelStore.params.sttp = 'FH';
|
||||||
modelStore.title = "栖息地 ";
|
modelStore.title = '栖息地 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};//
|
}; //
|
||||||
const showMapModal4 = () => {
|
const showMapModal4 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "fh_zq_point";
|
modelStore.params.sttp = 'fh_zq_point';
|
||||||
modelStore.title = "栖息地流量 ";
|
modelStore.title = '栖息地流量 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal5 = () => {
|
const showMapModal5 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "fp_point";
|
modelStore.params.sttp = 'fp_point';
|
||||||
modelStore.title = "过鱼设施 ";
|
modelStore.title = '过鱼设施 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal6 = () => {
|
const showMapModal6 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "FB";
|
modelStore.params.sttp = 'FB';
|
||||||
modelStore.title = "鱼类增殖站 ";
|
modelStore.title = '鱼类增殖站 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
const showMapModal7 = () => {
|
const showMapModal7 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "VA";
|
modelStore.params.sttp = 'VA';
|
||||||
modelStore.title = "动物救助站 ";
|
modelStore.title = '动物救助站 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal8 = () => {
|
const showMapModal8 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "VP";
|
modelStore.params.sttp = 'VP';
|
||||||
modelStore.title = "珍稀植物园 ";
|
modelStore.title = '珍稀植物园 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal9 = () => {
|
const showMapModal9 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WE";
|
modelStore.params.sttp = 'WE';
|
||||||
modelStore.title = "水生生态调查断面 ";
|
modelStore.title = '水生生态调查断面 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal10 = () => {
|
const showMapModal10 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WVA";
|
modelStore.params.sttp = 'WVA';
|
||||||
modelStore.title = "野生动物监测 ";
|
modelStore.title = '野生动物监测 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal11 = () => {
|
const showMapModal11 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "VD_FBFM";
|
modelStore.params.sttp = 'VD_FBFM';
|
||||||
modelStore.title = "AI视频监控站 ";
|
modelStore.title = 'AI视频监控站 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal12 = () => {
|
const showMapModal12 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "eng_alarm_point";
|
modelStore.params.sttp = 'eng_alarm_point';
|
||||||
modelStore.title = "水电站告警情况 ";
|
modelStore.title = '水电站告警情况 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal13 = () => {
|
const showMapModal13 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "FPRD";
|
modelStore.params.sttp = 'FPRD';
|
||||||
modelStore.title = "鱼类调查装置 ";
|
modelStore.title = '鱼类调查装置 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal14 = () => {
|
const showMapModal14 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "DW_2";
|
modelStore.params.sttp = 'DW_2';
|
||||||
modelStore.title = "低温水减缓设施-叠梁门 ";
|
modelStore.title = '低温水减缓设施-叠梁门 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal15 = () => {
|
const showMapModal15 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "DW_5";
|
modelStore.params.sttp = 'DW_5';
|
||||||
modelStore.title = "前置挡墙 ";
|
modelStore.title = '前置挡墙 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal16 = () => {
|
const showMapModal16 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "DW_6";
|
modelStore.params.sttp = 'DW_6';
|
||||||
modelStore.title = "隔水幕墙 ";
|
modelStore.title = '隔水幕墙 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
const showMapModal17 = () => {
|
const showMapModal17 = () => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "DW_1";
|
modelStore.params.sttp = 'DW_1';
|
||||||
modelStore.title = "夹岩双层取水 ";
|
modelStore.title = '夹岩双层取水 ';
|
||||||
modelStore.isBasicEdit = true;
|
modelStore.isBasicEdit = true;
|
||||||
};
|
};
|
||||||
//DW_5
|
//DW_5
|
||||||
|
|||||||
@ -246,15 +246,13 @@ import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vu
|
|||||||
import { getCaptcha } from "@/api/auth";
|
import { getCaptcha } from "@/api/auth";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
// 组件依赖
|
// 组件依赖
|
||||||
|
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import Cookies from "js-cookie";
|
|
||||||
// API依赖
|
// API依赖
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { LoginData } from "@/api/auth/types";
|
import { LoginData } from "@/api/auth/types";
|
||||||
|
|
||||||
//密码加密
|
//密码加密
|
||||||
import { encrypt, decrypt } from "@/utils/rsaEncrypt";
|
import { encrypt } from "@/utils/rsaEncrypt";
|
||||||
|
|
||||||
// 状态管理依赖
|
// 状态管理依赖
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { ref, computed, onMounted, watch } from 'vue';
|
import { ref, computed, onMounted, watch } from 'vue';
|
||||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||||
@ -50,7 +50,7 @@ const btnLoading = ref<boolean>(false);
|
|||||||
const initSearchData = {
|
const initSearchData = {
|
||||||
rvcd: 'all',
|
rvcd: 'all',
|
||||||
stcd: null,
|
stcd: null,
|
||||||
reportMonth: moment().subtract(1, 'month').format('YYYY-MM')
|
reportMonth: dayjs().subtract(1, 'month').format('YYYY-MM')
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchData = ref<any>({ ...initSearchData });
|
const searchData = ref<any>({ ...initSearchData });
|
||||||
|
|||||||
@ -338,7 +338,6 @@ const videoFileList = ref<any[]>([]);
|
|||||||
|
|
||||||
// 禁用今天之后的日期
|
// 禁用今天之后的日期
|
||||||
const disabledDate = (current: any) => {
|
const disabledDate = (current: any) => {
|
||||||
// current 是 moment/dayjs 对象
|
|
||||||
// 返回 true 表示禁用
|
// 返回 true 表示禁用
|
||||||
return current && current > dayjs().endOf('day');
|
return current && current > dayjs().endOf('day');
|
||||||
};
|
};
|
||||||
@ -370,7 +369,7 @@ const getHbrvcdDropdownSelect = async () => {
|
|||||||
const res = await getSelectForDropdown({});
|
const res = await getSelectForDropdown({});
|
||||||
let list = res.data || [];
|
let list = res.data || [];
|
||||||
if (list && list.length > 0) {
|
if (list && list.length > 0) {
|
||||||
list = list.filter((item: any) => item.rvcd !== "all");
|
list = list.filter((item: any) => item.rvcd !== 'all');
|
||||||
}
|
}
|
||||||
hbrvcdOption.value = list;
|
hbrvcdOption.value = list;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -469,13 +468,13 @@ const filterOption = (inputValue: string, option: any) => {
|
|||||||
};
|
};
|
||||||
// 验证规则
|
// 验证规则
|
||||||
const rules: Record<string, Rule[]> = {
|
const rules: Record<string, Rule[]> = {
|
||||||
rvcd: [{ required: true, message: "请选择流域", trigger: "change" }],
|
rvcd: [{ required: true, message: '请选择流域', trigger: 'change' }],
|
||||||
rstcd: [{ required: true, message: "请选择电站", trigger: "change" }],
|
rstcd: [{ required: true, message: '请选择电站', trigger: 'change' }],
|
||||||
stcd: [{ required: true, message: "请选择过鱼设施", trigger: "change" }],
|
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||||
strdt: [{ required: true, message: "请选择过鱼时间", trigger: "change" }],
|
strdt: [{ required: true, message: '请选择过鱼时间', trigger: 'change' }],
|
||||||
ftp: [{ required: true, message: "请选择鱼种类", trigger: "change" }],
|
ftp: [{ required: true, message: '请选择鱼种类', trigger: 'change' }],
|
||||||
direction: [{ required: true, message: "请选择游向", trigger: "change" }],
|
direction: [{ required: true, message: '请选择游向', trigger: 'change' }],
|
||||||
fcnt: [{ required: true, message: "请输入过鱼数量", trigger: "change" }],
|
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'change' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算是否为编辑模式
|
// 计算是否为编辑模式
|
||||||
|
|||||||