Compare commits

..

No commits in common. "fb5ad57dc158c085df59656aa661185c18b375d6" and "12f41faa403a501ae40149698a8996aa3c6bbe6b" have entirely different histories.

49 changed files with 723 additions and 1164 deletions

25
.env
View File

@ -1,25 +0,0 @@
# 所有环境都会加载的配置
# 项目名称
VITE_APP_TITLE=智慧湿地管理平台
# 项目描述
VITE_APP_DESC=科技赋能生态保护 智慧守护绿色家园
# 版本号
VITE_APP_VERSION=1.0.0
# 版权信息
VITE_APP_COPYRIGHT=Copyright © 2025 智慧湿地管理平台 All Rights Reserved.
# 是否启用 gzip 压缩
VITE_BUILD_GZIP=false
# 是否启用 brotli 压缩
VITE_BUILD_BROTLI=false
# 是否删除 console
VITE_DROP_CONSOLE=true
# 后端 API 的基础 URL
VITE_API_BASE_URL=http://localhost:3000

View File

@ -1,14 +0,0 @@
# 开发环境
VITE_NODE_ENV=development
# API 基础路径
VITE_API_BASE_URL=http://localhost:3000
# 项目基础路径
VITE_BASE_URL=/
# Mock API 路径
VITE_MOCK_API=true
# 是否开启调试工具
VITE_DEV_TOOLS=true

View File

@ -1,14 +0,0 @@
# 生产环境
VITE_NODE_ENV=production
# API 基础路径 - 实际项目中替换为真实的后端接口地址
VITE_API_BASE_URL=https://api.your-domain.com
# 项目基础路径
VITE_BASE_URL=/
# Mock API 路径
VITE_MOCK_API=false
# 是否开启调试工具
VITE_DEV_TOOLS=false

View File

@ -1,14 +0,0 @@
# 测试环境
VITE_NODE_ENV=test
# API 基础路径
VITE_API_BASE_URL=https://test-api.your-domain.com
# 项目基础路径
VITE_BASE_URL=/
# Mock API 路径
VITE_MOCK_API=false
# 是否开启调试工具
VITE_DEV_TOOLS=true

View File

@ -8,6 +8,6 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

7
package-lock.json generated
View File

@ -15,8 +15,7 @@
"json-server": "^1.0.0-beta.3", "json-server": "^1.0.0-beta.3",
"pinia": "^2.3.1", "pinia": "^2.3.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0", "vue-router": "^4.5.0"
"wetlandguard-admin": "file:"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.13.0", "@types/node": "^22.13.0",
@ -2751,10 +2750,6 @@
"typescript": ">=5.0.0" "typescript": ">=5.0.0"
} }
}, },
"node_modules/wetlandguard-admin": {
"resolved": "",
"link": true
},
"node_modules/zrender": { "node_modules/zrender": {
"version": "5.6.1", "version": "5.6.1",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz", "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",

View File

@ -16,8 +16,7 @@
"json-server": "^1.0.0-beta.3", "json-server": "^1.0.0-beta.3",
"pinia": "^2.3.1", "pinia": "^2.3.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0", "vue-router": "^4.5.0"
"wetlandguard-admin": "file:"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.13.0", "@types/node": "^22.13.0",

View File

@ -1,20 +0,0 @@
import request from '@/utils/request'
/**
* 用户登录
* @param {Object} data - 登录参数
* @param {string} data.username - 用户名
* @param {string} data.password - 密码
* @returns {Promise} 返回包含token和用户信息的Promise
*/
export function login(data) {
return request.post('/api/users/login', data)
}
/**
* 退出登录
* @returns {Promise}
*/
export function logout() {
return request.post('/api/users/logout')
}

49
src/api/report.ts Normal file
View File

@ -0,0 +1,49 @@
import request from './request';
// 日常报告接口
export const dailyReportApi = {
// 获取日常报告列表
getList: (params: any) => request.get('/report/daily/list', { params }),
// 获取日常报告详情
getDetail: (id: number) => request.get(`/report/daily/${id}`),
// 创建日常报告
create: (data: any) => request.post('/report/daily', data),
// 更新日常报告
update: (id: number, data: any) => request.put(`/report/daily/${id}`, data),
// 删除日常报告
delete: (id: number) => request.delete(`/report/daily/${id}`),
// 导出日常报告
export: (id: number) => request.get(`/report/daily/export/${id}`, { responseType: 'blob' })
};
// 分析报告接口
export const analysisReportApi = {
// 获取分析报告列表
getList: (params: any) => request.get('/report/analysis/list', { params }),
// 获取分析报告详情
getDetail: (id: number) => request.get(`/report/analysis/${id}`),
// 创建分析报告
create: (data: any) => request.post('/report/analysis', data),
// 更新分析报告
update: (id: number, data: any) => request.put(`/report/analysis/${id}`, data),
// 删除分析报告
delete: (id: number) => request.delete(`/report/analysis/${id}`),
// 导出分析报告
export: (id: number) => request.get(`/report/analysis/export/${id}`, { responseType: 'blob' }),
// 获取监测数据统计
getMonitorStats: (params: any) => request.get('/report/analysis/monitor-stats', { params }),
// 获取物种数据统计
getSpeciesStats: (params: any) => request.get('/report/analysis/species-stats', { params })
};

35
src/api/request.ts Normal file
View File

@ -0,0 +1,35 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
// 请求拦截器
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
(response) => {
const { data } = response
return data
},
(error) => {
ElMessage.error(error.message || '请求失败')
return Promise.reject(error)
}
)
export default request

View File

@ -1,52 +0,0 @@
import request from '@/utils/request'
/**
* 获取用户列表
* @returns {Promise} 返回用户列表数据
*/
export function getUserList() {
return request.get('/api/users')
}
/**
* 新增用户
* @param {Object} data - 用户数据
* @param {string} data.username - 用户名必选
* @param {string} data.password - 密码必选
* @param {string} data.role_id - 角色ID必选
* @param {string} [data.real_name] - 真实姓名可选
* @param {string} [data.email] - 邮箱可选
* @param {string} [data.phone] - 手机号可选
* @param {string} [data.status] - 状态可选
* @param {string} [data.expire_time] - 过期时间可选
* @returns {Promise} 返回创建结果
*/
export function createUser(data) {
return request.post('/api/users', data)
}
/**
* 编辑用户
* @param {string|number} id - 用户ID
* @param {Object} data - 用户数据
* @param {string} [data.real_name] - 真实姓名可选
* @param {string} [data.email] - 邮箱可选
* @param {string} [data.phone] - 手机号可选
* @param {string} [data.role_id] - 角色ID可选
* @param {string} [data.status] - 状态可选
* @param {string} [data.expire_time] - 过期时间可选
* @param {string} [data.password] - 新密码可选
* @returns {Promise} 返回更新结果
*/
export function updateUser(id, data) {
return request.put(`/api/users/${id}`, data)
}
/**
* 删除用户
* @param {string|number} id - 用户ID
* @returns {Promise} 返回删除结果
*/
export function deleteUser(id) {
return request.delete(`/api/users/${id}`)
}

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -1,23 +1,15 @@
<script setup> <script setup lang="ts">
const props = defineProps({ const props = defineProps<{
data: { data: {
type: Object, qualityTrend: string;
required: true, indicators: {
validator: (value) => { name: string;
return ( value: number;
value.qualityTrend && threshold: number;
Array.isArray(value.indicators) && status: string;
value.indicators.every( }[];
(indicator) => };
indicator.name && }>();
typeof indicator.value === 'number' &&
typeof indicator.threshold === 'number' &&
indicator.status
)
);
},
},
});
</script> </script>
<template> <template>

View File

@ -1,24 +1,14 @@
<script setup> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
const props = defineProps({ const props = defineProps<{
data: { data: any;
type: [Object, Array], type: 'line' | 'bar';
required: true title?: string;
}, }>();
type: {
type: String,
validator: (value) => ['line', 'bar'].includes(value),
required: true
},
title: {
type: String,
required: false
}
});
let chart = null; let chart: echarts.ECharts | null = null;
const initChart = () => { const initChart = () => {
const chartDom = document.getElementById('monitorChart'); const chartDom = document.getElementById('monitorChart');

View File

@ -1,19 +1,13 @@
<script setup> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
const props = defineProps({ const props = defineProps<{
data: { data: {
type: Object, diversity: number;
required: true, richness: number;
validator: (value) => { distribution: any[];
return ( };
typeof value.diversity === 'number' && }>();
typeof value.richness === 'number' &&
Array.isArray(value.distribution)
);
},
},
});
const diversityLevel = computed(() => { const diversityLevel = computed(() => {
const value = props.data.diversity; const value = props.data.diversity;

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
@ -45,7 +45,7 @@ watch(
} }
); );
const handleSelect = (key) => { const handleSelect = (key: string) => {
router.push(key); router.push(key);
}; };

View File

@ -1,14 +1,25 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
interface LogEntry {
id: number;
type: string;
user: string;
action: string;
ip: string;
status: string;
detail: string;
createTime: string;
}
export const useSystemLogStore = defineStore('systemLog', { export const useSystemLogStore = defineStore('systemLog', {
state: () => ({ state: () => ({
logs: [] logs: [] as LogEntry[]
}), }),
actions: { actions: {
// 添加日志 // 添加日志
addLog(log) { addLog(log: Omit<LogEntry, 'id' | 'createTime'>) {
const newLog = { const newLog: LogEntry = {
...log, ...log,
id: Date.now(), id: Date.now(),
createTime: new Date().toLocaleString('zh-CN', { createTime: new Date().toLocaleString('zh-CN', {

View File

@ -1,72 +0,0 @@
import { defineStore } from 'pinia'
import { login as loginApi, logout as logoutApi } from '@/api/login'
// 安全的 JSON 解析函数
const safeJSONParse = (str, defaultValue = null) => {
if (!str || str === 'undefined' || str === 'null') {
return defaultValue
}
try {
return JSON.parse(str)
} catch (error) {
console.warn('JSON解析错误使用默认值:', error)
return defaultValue
}
}
export const useUserStore = defineStore('user', {
state: () => {
// 从 localStorage 获取数据
const token = localStorage.getItem('token')
const userInfoStr = localStorage.getItem('userInfo')
return {
token: token || null,
userInfo: safeJSONParse(userInfoStr)
}
},
actions: {
// 登录
async login(username, password) {
try {
// 调用登录接口
const { token, userInfo } = await loginApi({ username, password })
// 保存token和用户信息
this.token = token
this.userInfo = userInfo
// 持久化存储
localStorage.setItem('token', token)
localStorage.setItem('userInfo', JSON.stringify(userInfo))
return true
} catch (error) {
console.error('登录失败:', error)
return false
}
},
// 退出登录
async logout() {
try {
await logoutApi()
} catch (error) {
console.error('退出登录失败:', error)
} finally {
// 无论是否成功调用退出接口,都清除本地存储
this.token = null
this.userInfo = null
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
}
}
},
getters: {
isLoggedIn: (state) => !!state.token,
username: (state) => state.userInfo?.username,
userRole: (state) => state.userInfo?.role
}
})

44
src/stores/user.ts Normal file
View File

@ -0,0 +1,44 @@
import { defineStore } from 'pinia'
interface UserState {
token: string | null;
userInfo: {
username: string;
role: string;
} | null;
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
token: localStorage.getItem('token'),
userInfo: null
}),
actions: {
// 登录
async login(username: string, password: string) {
if (username === 'admin' && password === '123456') {
this.token = 'demo-token';
this.userInfo = {
username: 'admin',
role: '管理员'
};
localStorage.setItem('token', this.token);
return true;
}
return false;
},
// 退出登录
logout() {
this.token = null;
this.userInfo = null;
localStorage.removeItem('token');
}
},
getters: {
isLoggedIn: (state) => !!state.token,
username: (state) => state.userInfo?.username
}
})

View File

@ -1,68 +0,0 @@
/**
* 格式化日期时间
* @param {string|number|Date} time 需要格式化的时间
* @param {string} [format='YYYY-MM-DD HH:mm:ss'] 格式化的格式
* @returns {string} 格式化后的时间字符串
*/
export function formatDateTime(time, format = 'YYYY-MM-DD HH:mm:ss') {
if (!time) return '';
const date = new Date(time);
if (isNaN(date.getTime())) return '';
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
/**
* 格式化日期
* @param {string|number|Date} time 需要格式化的时间
* @returns {string} 格式化后的日期字符串 YYYY-MM-DD
*/
export function formatDate(time) {
return formatDateTime(time, 'YYYY-MM-DD');
}
/**
* 格式化时间为相对时间
* @param {string|number|Date} time 需要格式化的时间
* @returns {string} 相对时间描述
*/
export function formatRelativeTime(time) {
if (!time) return '';
const date = new Date(time);
if (isNaN(date.getTime())) return '';
const now = new Date();
const diff = now - date;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 30) {
return formatDateTime(time);
} else if (days > 0) {
return `${days}天前`;
} else if (hours > 0) {
return `${hours}小时前`;
} else if (minutes > 0) {
return `${minutes}分钟前`;
} else {
return '刚刚';
}
}

View File

@ -1,156 +0,0 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { useUserStore } from '../stores/user'
import router from '../router'
// 创建axios实例
const service = axios.create({
// 从环境变量获取基础URL如果没有则使用默认值
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 15000, // 请求超时时间
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 请求拦截器
service.interceptors.request.use(
(config) => {
const userStore = useUserStore()
const token = userStore.token
// 如果有token添加到请求头
if (token) {
config.headers = config.headers || {}
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
(error) => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(response) => {
const res = response.data
// 如果响应成功
if (res.success) {
return res
}
// 处理特定错误码
switch (res.code) {
case 401:
handleUnauthorized()
break
case 403:
ElMessage.error('没有权限访问该资源')
break
case 500:
console.error('服务器错误详情:', res)
ElMessage.error(res.message || '服务器错误,请稍后重试')
break
default:
ElMessage.error(res.message || '请求失败')
}
return Promise.reject(new Error(res.message || '请求失败'))
},
(error) => {
console.error('响应错误详情:', {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
headers: error.response?.headers,
config: {
url: error.config?.url,
method: error.config?.method,
headers: error.config?.headers,
params: error.config?.params
}
})
// 处理网络错误
if (!error.response) {
ElMessage.error('网络错误,请检查您的网络连接')
return Promise.reject(error)
}
// 处理HTTP状态码错误
const status = error.response.status
switch (status) {
case 401:
handleUnauthorized()
break
case 403:
ElMessage.error('没有权限访问该资源')
break
case 404:
ElMessage.error('请求的资源不存在')
break
case 500:
ElMessage.error('服务器错误,请稍后重试')
break
default:
ElMessage.error(`请求失败:${error.message}`)
}
return Promise.reject(error)
}
)
// 处理未授权情况
const handleUnauthorized = () => {
const userStore = useUserStore()
userStore.logout() // 清除用户信息
// 跳转到登录页,并携带当前页面路径
const currentPath = router.currentRoute.value.fullPath
router.push({
path: '/login',
query: {
redirect: currentPath
}
})
ElMessage.error('登录已过期,请重新登录')
}
// 封装请求方法
export const request = {
get(url, config) {
return service.get(url, config)
},
post(url, data, config) {
return service.post(url, data, config)
},
put(url, data, config) {
return service.put(url, data, config)
},
delete(url, config) {
return service.delete(url, config)
},
// 上传文件的专用方法
upload(url, file, config) {
const formData = new FormData()
formData.append('file', file)
return service.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
...config
})
}
}
export default request

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { markRaw } from 'vue' import { markRaw } from 'vue'

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import * as echarts from "echarts"; import * as echarts from "echarts";

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref, reactive } from "vue"; import { ref, reactive } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
@ -42,7 +42,7 @@ const getCurrentTime = () => {
.replace(/\//g, "-"); .replace(/\//g, "-");
}; };
const handleLogin = async (formEl) => { const handleLogin = async (formEl: any) => {
if (!formEl) return; if (!formEl) return;
loading.value = true; loading.value = true;
try { try {
@ -67,7 +67,7 @@ const handleLogin = async (formEl) => {
}); });
// //
const redirect = route.query.redirect; const redirect = route.query.redirect as string;
router.push(redirect || "/dashboard"); router.push(redirect || "/dashboard");
} else { } else {
// //
@ -92,7 +92,7 @@ const handleLogin = async (formEl) => {
}; };
// //
const handleFocus = (prop) => { const handleFocus = (prop: "username" | "password") => {
if (formRef.value) { if (formRef.value) {
// //
formRef.value.clearValidate(prop); formRef.value.clearValidate(prop);
@ -161,7 +161,7 @@ const handleFocus = (prop) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "../../styles/variables.scss" as *; @import "../../styles/variables.scss";
.login-container { .login-container {
height: 100vh; height: 100vh;

View File

@ -1,10 +1,19 @@
<script setup> <script setup lang="ts">
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { Monitor, Warning, TrendCharts } from "@element-plus/icons-vue"; import { Monitor, Warning, TrendCharts } from "@element-plus/icons-vue";
const tableData = ref([ interface EnvData {
id: number;
location: string;
temperature: number;
humidity: number;
waterQuality: string;
time: string;
}
const tableData = ref<EnvData[]>([
{ {
id: 1, id: 1,
location: "A区监测点", location: "A区监测点",
@ -81,9 +90,9 @@ const initChart = () => {
textStyle: { textStyle: {
color: "#666", color: "#666",
}, },
formatter: function (params) { formatter: function (params: any) {
let result = `${params[0].axisValue}<br/>`; let result = `${params[0].axisValue}<br/>`;
params.forEach((item) => { params.forEach((item: any) => {
result += `${item.marker} ${item.seriesName}: ${item.value}${ result += `${item.marker} ${item.seriesName}: ${item.value}${
item.seriesName.includes("温度") item.seriesName.includes("温度")
? "°C" ? "°C"
@ -238,30 +247,33 @@ const initChart = () => {
myChart.setOption(option); myChart.setOption(option);
// //
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
myChart.resize(); myChart.resize();
}); });
}; };
// //
const exportDialogVisible = ref(false); const exportDialogVisible = ref(false);
const exportForm = reactive({ const exportForm = reactive({
timeRange: [], timeRange: [] as string[],
dataType: ["temperature", "humidity", "waterQuality"],
format: "excel", format: "excel",
dataType: ["temperature", "humidity", "waterQuality"],
}); });
// const handleExport = () => {
exportDialogVisible.value = true;
};
const handleExportConfirm = () => { const handleExportConfirm = () => {
ElMessage.success("数据导出成功"); console.log("导出数据:", exportForm);
exportDialogVisible.value = false; exportDialogVisible.value = false;
}; };
// //
const initMiniChart = (el, data) => { const initMiniChart = (el: HTMLElement, data: number[]) => {
const chart = echarts.init(el); const chart = echarts.init(el);
const option = { chart.setOption({
grid: { grid: {
left: 0, left: 0,
right: 0, right: 0,
@ -345,8 +357,7 @@ const initMiniChart = (el, data) => {
}, },
padding: [4, 8], padding: [4, 8],
}, },
}; });
chart.setOption(option);
// hover // hover
const card = el.closest(".env-card"); const card = el.closest(".env-card");
@ -367,12 +378,15 @@ const initMiniChart = (el, data) => {
}; };
onMounted(() => { onMounted(() => {
initChart();
// //
const chartEls = document.querySelectorAll(".env-chart"); envStats.value.forEach((stat, index) => {
chartEls.forEach((el, index) => { const el = document.getElementById(`miniChart${index}`);
initMiniChart(el, envStats.value[index].trend); if (el) {
initMiniChart(el, stat.trend);
}
}); });
//
initChart();
}); });
</script> </script>
@ -468,7 +482,7 @@ onMounted(() => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.env-container { .env-container {
.env-card { .env-card {

View File

@ -1,9 +1,18 @@
<script setup> <script setup lang="ts">
import { ref, reactive } from "vue"; import { ref, reactive } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { onMounted } from "vue"; import { onMounted } from "vue";
const tableData = ref([ interface SpeciesData {
id: number;
name: string;
type: string;
count: number;
location: string;
time: string;
}
const tableData = ref<SpeciesData[]>([
{ {
id: 1, id: 1,
name: "东方白鹳", name: "东方白鹳",
@ -139,7 +148,7 @@ const initChart = () => {
label: { label: {
show: true, show: true,
position: 'outside', position: 'outside',
formatter: function(params) { formatter: function(params: any) {
return `${params.name}\n${params.percent.toFixed(1)}%`; return `${params.name}\n${params.percent.toFixed(1)}%`;
}, },
color: '#666', color: '#666',
@ -163,10 +172,10 @@ const initChart = () => {
animationDurationUpdate: 500, animationDurationUpdate: 500,
animationType: 'expansion', animationType: 'expansion',
animationEasing: 'cubicInOut', animationEasing: 'cubicInOut',
animationDelay: function(idx) { animationDelay: function(idx: number) {
return idx * 100; return idx * 100;
}, },
animationDelayUpdate: function(idx) { animationDelayUpdate: function(idx: number) {
return idx * 100; return idx * 100;
}, },
emphasis: { emphasis: {
@ -205,7 +214,7 @@ onMounted(() => {
// //
const exportDialogVisible = ref(false); const exportDialogVisible = ref(false);
const exportForm = reactive({ const exportForm = reactive({
timeRange: [], timeRange: [] as string[],
format: "excel", format: "excel",
}); });
@ -297,7 +306,7 @@ const handleExportConfirm = () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.species-container { .species-container {
padding: 20px; padding: 20px;

View File

@ -1,8 +1,18 @@
<script setup> <script setup lang="ts">
import { ref, reactive, computed } from "vue"; import { ref, reactive, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
const tableData = ref([ interface PointData {
id: number;
name: string;
location: string;
type: string;
description: string;
status: string;
lastPatrolTime: string;
}
const tableData = ref<PointData[]>([
{ {
id: 1, id: 1,
name: "A区-1号点位", name: "A区-1号点位",
@ -46,8 +56,8 @@ const handleAdd = () => {
// //
const searchText = ref(""); const searchText = ref("");
const typeFilter = ref([]); const typeFilter = ref<string[]>([]);
const statusFilter = ref([]); const statusFilter = ref<string[]>([]);
// //
const filteredTableData = computed(() => { const filteredTableData = computed(() => {
@ -80,9 +90,9 @@ const resetFilters = () => {
// //
const isEdit = ref(false); const isEdit = ref(false);
const currentPoint = ref(null); const currentPoint = ref<PointData | null>(null);
const handleEdit = (row) => { const handleEdit = (row: PointData) => {
isEdit.value = true; isEdit.value = true;
currentPoint.value = row; currentPoint.value = row;
formData.name = row.name; formData.name = row.name;
@ -135,16 +145,16 @@ const paginatedData = computed(() => {
return filteredTableData.value.slice(start, end); return filteredTableData.value.slice(start, end);
}); });
const handleSizeChange = (val) => { const handleSizeChange = (val: number) => {
pageSize.value = val; pageSize.value = val;
currentPage.value = 1; currentPage.value = 1;
}; };
const handleCurrentChange = (val) => { const handleCurrentChange = (val: number) => {
currentPage.value = val; currentPage.value = val;
}; };
const handleDelete = (row) => { const handleDelete = (row: PointData) => {
ElMessageBox.confirm("确认删除该巡护点位?", "提示", { ElMessageBox.confirm("确认删除该巡护点位?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
@ -318,7 +328,7 @@ const pointTypes = [
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.point-container { .point-container {
.el-card { .el-card {

View File

@ -1,9 +1,20 @@
<script setup> <script setup lang="ts">
import { ref, computed, reactive } from "vue"; import { ref, computed, reactive } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { Search } from "@element-plus/icons-vue"; import { Search } from "@element-plus/icons-vue";
const tableData = ref([ interface RecordData {
id: number;
taskTitle: string;
patroller: string;
route: string;
findings: string;
images: string[];
status: string;
completedTime: string;
}
const tableData = ref<RecordData[]>([
{ {
id: 1, id: 1,
taskTitle: "A区日常巡查", taskTitle: "A区日常巡查",
@ -27,9 +38,9 @@ const tableData = ref([
]); ]);
const dialogVisible = ref(false); const dialogVisible = ref(false);
const currentRecord = ref(null); const currentRecord = ref<RecordData | null>(null);
const handleView = (row) => { const handleView = (row: RecordData) => {
currentRecord.value = row; currentRecord.value = row;
dialogVisible.value = true; dialogVisible.value = true;
}; };
@ -50,7 +61,7 @@ const filteredTableData = computed(() => {
const exportDialogVisible = ref(false); const exportDialogVisible = ref(false);
const exportForm = reactive({ const exportForm = reactive({
timeRange: [], timeRange: [] as string[],
format: "excel", format: "excel",
types: ["basic", "findings", "images"], types: ["basic", "findings", "images"],
}); });
@ -74,12 +85,12 @@ const paginatedData = computed(() => {
return filteredTableData.value.slice(start, end); return filteredTableData.value.slice(start, end);
}); });
const handleSizeChange = (val) => { const handleSizeChange = (val: number) => {
pageSize.value = val; pageSize.value = val;
currentPage.value = 1; currentPage.value = 1;
}; };
const handleCurrentChange = (val) => { const handleCurrentChange = (val: number) => {
currentPage.value = val; currentPage.value = val;
}; };
</script> </script>
@ -231,7 +242,7 @@ const handleCurrentChange = (val) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.record-container { .record-container {
.el-card { .el-card {

View File

@ -1,8 +1,19 @@
<script setup> <script setup lang="ts">
import { ref, reactive, computed } from "vue"; import { ref, reactive, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
const tableData = ref([ interface TaskData {
id: number;
title: string;
area: string;
assignee: string;
status: string;
priority: string;
startTime: string;
endTime: string;
}
const tableData = ref<TaskData[]>([
{ {
id: 1, id: 1,
title: "A区日常巡查", title: "A区日常巡查",
@ -25,8 +36,8 @@ const tableData = ref([
}, },
]); ]);
const getStatusType = (status) => { const getStatusType = (status: string) => {
const map = { const map: Record<string, string> = {
进行中: "success", 进行中: "success",
待开始: "info", 待开始: "info",
已完成: "primary", 已完成: "primary",
@ -35,8 +46,8 @@ const getStatusType = (status) => {
return map[status] || "info"; return map[status] || "info";
}; };
const getPriorityType = (priority) => { const getPriorityType = (priority: string) => {
const map = { const map: Record<string, string> = {
: "danger", : "danger",
: "warning", : "warning",
: "info", : "info",
@ -44,7 +55,7 @@ const getPriorityType = (priority) => {
return map[priority] || "info"; return map[priority] || "info";
}; };
// //
const newTaskDialogVisible = ref(false); const newTaskDialogVisible = ref(false);
const newTaskForm = reactive({ const newTaskForm = reactive({
title: "", title: "",
@ -56,23 +67,24 @@ const newTaskForm = reactive({
description: "", description: "",
}); });
// //
const detailDialogVisible = ref(false); const detailDialogVisible = ref(false);
const currentTask = ref(null); const currentTask = ref<TaskData | null>(null);
// //
const handleView = (row) => { const handleView = (row: TaskData) => {
currentTask.value = row; currentTask.value = row;
detailDialogVisible.value = true; detailDialogVisible.value = true;
}; };
const handleComplete = (row) => { const handleComplete = (row: TaskData) => {
ElMessageBox.confirm("确认完成该巡护任务?", "提示", { ElMessageBox.confirm("确认完成该巡护任务?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning", type: "warning",
}) })
.then(() => { .then(() => {
//
ElMessage.success("任务已完成"); ElMessage.success("任务已完成");
}) })
.catch(() => { .catch(() => {
@ -80,13 +92,14 @@ const handleComplete = (row) => {
}); });
}; };
const handleCancel = (row) => { const handleCancel = (row: TaskData) => {
ElMessageBox.confirm("确认取消该巡护任务?", "提示", { ElMessageBox.confirm("确认取消该巡护任务?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning", type: "warning",
}) })
.then(() => { .then(() => {
//
ElMessage.success("任务已取消"); ElMessage.success("任务已取消");
}) })
.catch(() => { .catch(() => {
@ -94,8 +107,14 @@ const handleCancel = (row) => {
}); });
}; };
// 线 // 线
const taskTimeline = ref([ interface TimelineItem {
type: 'primary' | 'success' | 'warning' | 'danger' | 'info';
timestamp: string;
content: string;
}
const taskTimeline = ref<TimelineItem[]>([
{ {
type: 'primary', type: 'primary',
timestamp: '2024-03-20 09:00', timestamp: '2024-03-20 09:00',
@ -118,8 +137,8 @@ const taskTimeline = ref([
}, },
]); ]);
// //
const validateTime = (rule, value, callback) => { const validateTime = (rule: any, value: string, callback: Function) => {
if (!value) { if (!value) {
callback(new Error('请选择时间')); callback(new Error('请选择时间'));
return; return;
@ -180,18 +199,18 @@ const resetForm = () => {
} }
}; };
// //
const handleDialogClose = () => { const handleDialogClose = () => {
resetForm(); resetForm();
}; };
// //
const searchText = ref(""); const searchText = ref("");
const statusFilter = ref([]); const statusFilter = ref<string[]>([]);
const priorityFilter = ref([]); const priorityFilter = ref<string[]>([]);
const dateRange = ref(['', '']); const dateRange = ref<[string, string]>(['', '']);
// //
const statistics = computed(() => { const statistics = computed(() => {
const total = tableData.value.length; const total = tableData.value.length;
const statusCount = { const statusCount = {
@ -207,8 +226,8 @@ const statistics = computed(() => {
}; };
tableData.value.forEach(task => { tableData.value.forEach(task => {
statusCount[task.status]++; statusCount[task.status as keyof typeof statusCount]++;
priorityCount[task.priority]++; priorityCount[task.priority as keyof typeof priorityCount]++;
}); });
return { return {
@ -527,7 +546,7 @@ const resetFilters = () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.task-container { .task-container {
.el-card { .el-card {

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { markRaw } from "vue"; import { markRaw } from "vue";
import { import {

View File

@ -1,11 +1,43 @@
<script setup> <script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { ElMessageBox, ElMessage } from "element-plus"; import { ElMessageBox, ElMessage } from "element-plus";
import { Plus } from '@element-plus/icons-vue'; import { Plus } from "@element-plus/icons-vue";
interface AnalysisReport {
id: number;
title: string;
type: "species" | "environment"; // /
timeRange: {
//
start: string;
end: string;
};
dataSource: {
//
type: string;
points: string[]; //
}[];
analysis: {
summary: string; //
trends: {
//
indicator: string; //
trend: string; //
data: any[]; //
}[];
abnormal: {
//
type: string;
description: string;
level: string;
}[];
};
recommendations: string[]; //
}
// //
const tableData = ref([ const tableData = ref<AnalysisReport[]>([
{ {
id: 1, id: 1,
title: "2024年第一季度水质监测分析报告", title: "2024年第一季度水质监测分析报告",
@ -55,15 +87,15 @@ const filterForm = ref({
// //
const detailVisible = ref(false); const detailVisible = ref(false);
const currentReport = ref(null); const currentReport = ref<AnalysisReport | null>(null);
const handleView = (row) => { const handleView = (row: AnalysisReport) => {
currentReport.value = row; currentReport.value = row;
detailVisible.value = true; detailVisible.value = true;
}; };
// //
const handleExport = (row) => { const handleExport = (row: AnalysisReport) => {
ElMessageBox.confirm("确认导出该分析报告?", "提示", { ElMessageBox.confirm("确认导出该分析报告?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
@ -78,7 +110,7 @@ const handleExport = (row) => {
}; };
// resize // resize
let myChart = null; let myChart: echarts.ECharts | null = null;
const initChart = () => { const initChart = () => {
const chartDom = document.getElementById("trendChart"); const chartDom = document.getElementById("trendChart");
@ -234,12 +266,12 @@ const paginatedData = computed(() => {
return tableData.value.slice(start, end); return tableData.value.slice(start, end);
}); });
const handleSizeChange = (val) => { const handleSizeChange = (val: number) => {
pageSize.value = val; pageSize.value = val;
currentPage.value = 1; currentPage.value = 1;
}; };
const handleCurrentChange = (val) => { const handleCurrentChange = (val: number) => {
currentPage.value = val; currentPage.value = val;
}; };
@ -448,7 +480,7 @@ const handleRefresh = () => {
<el-button @click="detailVisible = false">关闭</el-button> <el-button @click="detailVisible = false">关闭</el-button>
<el-button <el-button
type="warning" type="warning"
@click="handleExport(currentReport)" @click="handleExport(currentReport!)"
:disabled="!currentReport" :disabled="!currentReport"
> >
导出 导出
@ -460,7 +492,7 @@ const handleRefresh = () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.analysis-report { .analysis-report {
.card-header { .card-header {

View File

@ -1,11 +1,29 @@
<script setup> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'; import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import { Plus, Document, Download, TrendCharts, Connection } from '@element-plus/icons-vue'; import { Plus, Document, Download, TrendCharts, Connection } from '@element-plus/icons-vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
interface DailyReport {
id: number;
title: string;
reporter: string;
department: string;
type: string;
content: string;
summary: string;
data: {
type: string;
value: string | number;
trend?: string;
}[];
attachments: string[];
status: string;
createTime: string;
}
// //
const tableData = ref([ const tableData = ref<DailyReport[]>([
{ {
id: 1, id: 1,
title: "A区巡护日报", title: "A区巡护日报",
@ -42,9 +60,9 @@ const formData = ref({
// //
const detailVisible = ref(false); const detailVisible = ref(false);
const currentReport = ref(null); const currentReport = ref<DailyReport | null>(null);
const handleView = (row) => { const handleView = (row: DailyReport) => {
currentReport.value = row; currentReport.value = row;
detailVisible.value = true; detailVisible.value = true;
}; };
@ -60,12 +78,13 @@ const handleSubmit = () => {
}; };
// //
let trendChart = null; let trendChart: echarts.ECharts | null = null;
// //
const initTrendChart = () => { const initTrendChart = () => {
const chartDom = document.getElementById('trendChart'); const chartDom = document.getElementById('trendChart');
if (!chartDom) return; if (!chartDom) return;
const myChart = echarts.init(chartDom); const myChart = echarts.init(chartDom);
const option = { const option = {
backgroundColor: 'transparent', backgroundColor: 'transparent',
@ -216,6 +235,7 @@ const handleGenerate = () => {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'info' type: 'info'
}).then(() => { }).then(() => {
//
const loadingInstance = ElLoading.service({ const loadingInstance = ElLoading.service({
text: '正在汇总数据,请稍候...' text: '正在汇总数据,请稍候...'
}); });
@ -423,7 +443,7 @@ const showTrendAnalysis = ref(false);
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button> <el-button @click="detailVisible = false">关闭</el-button>
<el-button type="warning" @click="handleExport(currentReport)">导出</el-button> <el-button type="warning" @click="handleExport(currentReport!)">导出</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@ -461,7 +481,7 @@ const showTrendAnalysis = ref(false);
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.daily-report { .daily-report {
.card-header { .card-header {

View File

@ -1,10 +1,21 @@
<script setup> <script setup lang="ts">
import { ref, computed, reactive } from "vue"; import { ref, computed, reactive } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { onMounted } from "vue"; import { onMounted } from "vue";
const tableData = ref([ interface AnalysisReport {
id: number;
title: string;
type: string;
period: string;
author: string;
summary: string;
charts: string[];
createTime: string;
}
const tableData = ref<AnalysisReport[]>([
{ {
id: 1, id: 1,
title: "2024年第一季度水质分析报告", title: "2024年第一季度水质分析报告",
@ -96,7 +107,7 @@ const handleSubmit = () => {
}; };
// //
const handleExport = (row) => { const handleExport = (row: AnalysisReport) => {
ElMessageBox.confirm( ElMessageBox.confirm(
'确认导出该分析报告?', '确认导出该分析报告?',
'提示', '提示',
@ -119,9 +130,9 @@ const handleExport = (row) => {
// //
const detailVisible = ref(false); const detailVisible = ref(false);
const currentReport = ref(null); const currentReport = ref<AnalysisReport | null>(null);
const handleView = (row) => { const handleView = (row: AnalysisReport) => {
currentReport.value = row; currentReport.value = row;
detailVisible.value = true; detailVisible.value = true;
}; };
@ -262,7 +273,7 @@ const handleView = (row) => {
<el-button @click="detailVisible = false">关闭</el-button> <el-button @click="detailVisible = false">关闭</el-button>
<el-button <el-button
type="warning" type="warning"
@click="handleExport(currentReport)" @click="handleExport(currentReport!)"
:disabled="!currentReport" :disabled="!currentReport"
> >
导出 导出
@ -274,7 +285,7 @@ const handleView = (row) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.analysis-report-container { .analysis-report-container {
.el-card { .el-card {

View File

@ -1,9 +1,21 @@
<script setup> <script setup lang="ts">
import { ref, computed, reactive } from "vue"; import { ref, computed, reactive } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import { Search } from "@element-plus/icons-vue"; import { Search } from "@element-plus/icons-vue";
const tableData = ref([ interface DailyReport {
id: number;
title: string;
reporter: string;
department: string;
type: string;
content: string;
attachments: string[];
status: string;
createTime: string;
}
const tableData = ref<DailyReport[]>([
{ {
id: 1, id: 1,
title: "A区巡护日报", title: "A区巡护日报",
@ -30,9 +42,9 @@ const tableData = ref([
// //
const searchText = ref(""); const searchText = ref("");
const typeFilter = ref([]); const typeFilter = ref<string[]>([]);
const statusFilter = ref([]); const statusFilter = ref<string[]>([]);
const dateRange = ref(["", ""]); const dateRange = ref<[string, string]>(["", ""]);
// //
const filteredData = computed(() => { const filteredData = computed(() => {
@ -58,7 +70,7 @@ const formData = reactive({
title: "", title: "",
type: "", type: "",
content: "", content: "",
attachments: [], attachments: [] as string[],
}); });
const handleCreate = () => { const handleCreate = () => {
@ -73,15 +85,15 @@ const handleSubmit = () => {
// //
const detailVisible = ref(false); const detailVisible = ref(false);
const currentReport = ref(null); const currentReport = ref<DailyReport | null>(null);
const handleView = (row) => { const handleView = (row: DailyReport) => {
currentReport.value = row; currentReport.value = row;
detailVisible.value = true; detailVisible.value = true;
}; };
// //
const handleExport = (row) => { const handleExport = (row: DailyReport) => {
ElMessageBox.confirm( ElMessageBox.confirm(
'确认导出该报告?', '确认导出该报告?',
'提示', '提示',
@ -269,7 +281,7 @@ const handleExport = (row) => {
<el-button @click="detailVisible = false">关闭</el-button> <el-button @click="detailVisible = false">关闭</el-button>
<el-button <el-button
type="warning" type="warning"
@click="handleExport(currentReport)" @click="handleExport(currentReport!)"
:disabled="!currentReport" :disabled="!currentReport"
> >
导出 导出
@ -281,7 +293,7 @@ const handleExport = (row) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.daily-report-container { .daily-report-container {
.el-card { .el-card {

View File

@ -1,11 +1,22 @@
<script setup> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import type { UploadProps } from "element-plus";
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { Upload, Download, Refresh } from "@element-plus/icons-vue"; import { Upload, Download, Refresh } from "@element-plus/icons-vue";
//
interface BackupRecord {
id: number;
name: string;
size: string;
type: string;
status: string;
createTime: string;
}
// //
const backupRecords = ref([ const backupRecords = ref<BackupRecord[]>([
{ {
id: 1, id: 1,
name: "系统完整备份_20240320", name: "系统完整备份_20240320",
@ -25,7 +36,7 @@ const backupRecords = ref([
]); ]);
// //
const uploadConfig = { const uploadConfig: UploadProps = {
action: "/api/upload", action: "/api/upload",
multiple: true, multiple: true,
accept: ".xlsx,.csv", accept: ".xlsx,.csv",
@ -64,7 +75,7 @@ const handleBackup = () => {
type: "warning", type: "warning",
}).then(() => { }).then(() => {
ElMessage.success("备份任务已启动"); ElMessage.success("备份任务已启动");
const newBackup = { const newBackup: BackupRecord = {
id: Date.now(), id: Date.now(),
name: `系统备份_${new Date().toISOString().split("T")[0].replace(/-/g, "")}`, name: `系统备份_${new Date().toISOString().split("T")[0].replace(/-/g, "")}`,
size: "处理中", size: "处理中",
@ -86,7 +97,7 @@ const handleBackup = () => {
}; };
// //
const handleRestore = (record) => { const handleRestore = (record: BackupRecord) => {
if (record.status !== "成功") { if (record.status !== "成功") {
ElMessage.warning("只能恢复成功的备份"); ElMessage.warning("只能恢复成功的备份");
return; return;
@ -102,7 +113,7 @@ const handleRestore = (record) => {
}; };
// //
const handleDelete = (record) => { const handleDelete = (record: BackupRecord) => {
ElMessageBox.confirm("确认删除该备份?", "提示", { ElMessageBox.confirm("确认删除该备份?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
@ -237,7 +248,7 @@ const icons = {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.data-container { .data-container {
.mb-20 { .mb-20 {

View File

@ -1,11 +1,22 @@
<script setup> <script setup lang="ts">
import { ref, onUnmounted } from "vue"; import { ref, onUnmounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { Plus } from "@element-plus/icons-vue"; import { Plus } from "@element-plus/icons-vue";
interface Device {
id: number;
name: string;
type: string;
location: string;
status: string;
ip: string;
lastHeartbeat: string;
lastMaintenance: string;
}
// //
const tableData = ref([ const tableData = ref<Device[]>([
{ {
id: 1, id: 1,
name: "A区-水质监测器-01", name: "A区-水质监测器-01",
@ -39,7 +50,7 @@ const deviceTypes = [
// / // /
const dialogVisible = ref(false); const dialogVisible = ref(false);
const isEdit = ref(false); const isEdit = ref(false);
const currentDevice = ref(null); const currentDevice = ref<Device | null>(null);
const formData = ref({ const formData = ref({
name: "", name: "",
@ -67,7 +78,7 @@ const handleAdd = () => {
}; };
// //
const handleEdit = (row) => { const handleEdit = (row: Device) => {
isEdit.value = true; isEdit.value = true;
currentDevice.value = row; currentDevice.value = row;
formData.value = { formData.value = {
@ -88,7 +99,7 @@ const handleSubmit = async () => {
if (isEdit.value && currentDevice.value) { if (isEdit.value && currentDevice.value) {
// //
const index = tableData.value.findIndex( const index = tableData.value.findIndex(
(item) => item.id === currentDevice.value.id (item) => item.id === currentDevice.value!.id
); );
if (index !== -1) { if (index !== -1) {
tableData.value[index] = { tableData.value[index] = {
@ -100,7 +111,7 @@ const handleSubmit = async () => {
} }
} else { } else {
// //
const newDevice = { const newDevice: Device = {
id: tableData.value.length + 1, id: tableData.value.length + 1,
...formData.value, ...formData.value,
status: "在线", status: "在线",
@ -130,7 +141,7 @@ const resetForm = () => {
}; };
// //
const handleDelete = (row) => { const handleDelete = (row: Device) => {
ElMessageBox.confirm("确认删除该设备?", "提示", { ElMessageBox.confirm("确认删除该设备?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
@ -147,12 +158,12 @@ const handleDelete = (row) => {
}; };
// //
const handleConfig = (row) => { const handleConfig = (row: Device) => {
ElMessage.success("正在连接设备..."); ElMessage.success("正在连接设备...");
}; };
// //
const handleMaintenance = (row) => { const handleMaintenance = (row: Device) => {
ElMessage.success("已记录维护时间"); ElMessage.success("已记录维护时间");
const index = tableData.value.findIndex((item) => item.id === row.id); const index = tableData.value.findIndex((item) => item.id === row.id);
if (index !== -1) { if (index !== -1) {
@ -195,8 +206,8 @@ const defaultProps = {
// //
const monitorVisible = ref(false); const monitorVisible = ref(false);
const currentMonitorDevice = ref(null); const currentMonitorDevice = ref<Device | null>(null);
const monitorChart = ref(null); const monitorChart = ref<echarts.ECharts | null>(null);
// //
const realTimeData = ref({ const realTimeData = ref({
@ -246,13 +257,13 @@ const handleAddGroup = () => {
}); });
}; };
const handleNodeClick = (data) => { const handleNodeClick = (data: any) => {
console.log("选中分组:", data); console.log("选中分组:", data);
// //
}; };
// //
const handleMonitor = (device) => { const handleMonitor = (device: Device) => {
currentMonitorDevice.value = device; currentMonitorDevice.value = device;
monitorVisible.value = true; monitorVisible.value = true;
initMonitorChart(); initMonitorChart();
@ -319,7 +330,7 @@ const initMonitorChart = () => {
}; };
// //
let dataTimer; let dataTimer: number;
const startRealTimeData = () => { const startRealTimeData = () => {
clearInterval(dataTimer); clearInterval(dataTimer);
dataTimer = setInterval(() => { dataTimer = setInterval(() => {
@ -365,7 +376,7 @@ const startRealTimeData = () => {
}; };
// //
const checkAlarms = (data) => { const checkAlarms = (data: any) => {
const { thresholds } = alarmConfig.value; const { thresholds } = alarmConfig.value;
if ( if (
@ -599,7 +610,7 @@ onUnmounted(() => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.device-container { .device-container {
.group-card { .group-card {

View File

@ -1,10 +1,21 @@
<script setup> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useSystemLogStore } from '../../../stores/systemLog'; import { useSystemLogStore } from '../../../stores/systemLog';
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { Download, Delete } from "@element-plus/icons-vue"; import { Download, Delete } from "@element-plus/icons-vue";
interface LogEntry {
id: number;
type: string;
user: string;
action: string;
ip: string;
status: string;
detail: string;
createTime: string;
}
const systemLogStore = useSystemLogStore(); const systemLogStore = useSystemLogStore();
// //
@ -23,7 +34,7 @@ const statusOptions = [
]; ];
// //
const getStatusType = (status) => { const getStatusType = (status: string) => {
switch (status) { switch (status) {
case "成功": case "成功":
return "success"; return "success";
@ -173,7 +184,7 @@ const icons = {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.logs-container { .logs-container {
.card-header { .card-header {

View File

@ -1,8 +1,18 @@
<script setup> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
const tableData = ref([ interface Permission {
id: number;
name: string;
code: string;
description: string;
type: string;
status: boolean;
createTime: string;
}
const tableData = ref<Permission[]>([
{ {
id: 1, id: 1,
name: "用户管理", name: "用户管理",
@ -35,7 +45,7 @@ const tableData = ref([
// / // /
const dialogVisible = ref(false); const dialogVisible = ref(false);
const isEdit = ref(false); const isEdit = ref(false);
const currentPermission = ref(null); const currentPermission = ref<Permission | null>(null);
const formData = ref({ const formData = ref({
name: "", name: "",
@ -63,7 +73,7 @@ const handleAdd = () => {
}; };
// //
const handleEdit = (row) => { const handleEdit = (row: Permission) => {
isEdit.value = true; isEdit.value = true;
currentPermission.value = row; currentPermission.value = row;
formData.value = { formData.value = {
@ -85,7 +95,7 @@ const handleSubmit = async () => {
if (isEdit.value && currentPermission.value) { if (isEdit.value && currentPermission.value) {
// //
const index = tableData.value.findIndex( const index = tableData.value.findIndex(
(item) => item.id === currentPermission.value.id (item) => item.id === currentPermission.value!.id
); );
if (index !== -1) { if (index !== -1) {
tableData.value[index] = { tableData.value[index] = {
@ -96,7 +106,7 @@ const handleSubmit = async () => {
} }
} else { } else {
// //
const newPermission = { const newPermission: Permission = {
id: tableData.value.length + 1, id: tableData.value.length + 1,
...formData.value, ...formData.value,
createTime: new Date().toLocaleString(), createTime: new Date().toLocaleString(),
@ -132,7 +142,7 @@ const handleDialogClose = () => {
}; };
// //
const handleDelete = (row) => { const handleDelete = (row: Permission) => {
ElMessageBox.confirm("确认删除该权限?", "提示", { ElMessageBox.confirm("确认删除该权限?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
@ -236,7 +246,7 @@ const handleDelete = (row) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "./styles/variables" as v; @import "../../../styles/variables.scss";
.permission-container { .permission-container {
.el-card { .el-card {

View File

@ -1,7 +1,15 @@
<script setup> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
const tableData = ref([ interface RoleData {
id: number;
name: string;
description: string;
permissions: string[];
createTime: string;
}
const tableData = ref<RoleData[]>([
{ {
id: 1, id: 1,
name: "超级管理员", name: "超级管理员",
@ -18,11 +26,11 @@ const tableData = ref([
}, },
]); ]);
const handleEdit = (row) => { const handleEdit = (row: RoleData) => {
console.log("编辑角色:", row); console.log("编辑角色:", row);
}; };
const handleDelete = (row) => { const handleDelete = (row: RoleData) => {
console.log("删除角色:", row); console.log("删除角色:", row);
}; };
</script> </script>
@ -65,7 +73,7 @@ const handleDelete = (row) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "../../../styles/variables.scss" as *; @import "../../../styles/variables.scss";
.role-container { .role-container {
.el-card { .el-card {

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
// //
@ -31,7 +31,7 @@ const alertConfig = ref({
}); });
// //
const handleSave = (type) => { const handleSave = (type: string) => {
// //
console.log( console.log(
`保存${type}配置:`, `保存${type}配置:`,
@ -174,7 +174,7 @@ const handleSave = (type) => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "../../../styles/variables.scss" as *; @import "../../../styles/variables.scss";
.settings-container { .settings-container {
.mb-20 { .mb-20 {

View File

@ -1,260 +1,41 @@
<script setup> <script setup lang="ts">
import { ref, onMounted, computed } from "vue"; import { ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { getUserList, createUser, updateUser, deleteUser } from "@/api/user";
import { formatDateTime } from "@/utils/format";
const tableData = ref([]); interface UserData {
const allData = ref([]); // id: number;
const loading = ref(false); username: string;
const total = ref(0); role: string;
email: string;
status: boolean;
createTime: string;
}
// const tableData = ref<UserData[]>([
const queryParams = ref({ {
username: '', id: 1,
role: '', username: "admin",
status: '' role: "超级管理员",
}); email: "admin@example.com",
status: true,
createTime: "2024-03-20",
},
{
id: 2,
username: "manager",
role: "管理人员",
email: "manager@example.com",
status: true,
createTime: "2024-03-20",
},
]);
// const handleEdit = (row: UserData) => {
const filteredData = computed(() => { console.log("编辑用户:", row);
return allData.value.filter(item => {
//
if (queryParams.value.username &&
!item.real_name.toLowerCase().includes(queryParams.value.username.toLowerCase())) {
return false;
}
//
if (queryParams.value.role &&
item.role.name !== queryParams.value.role) {
return false;
}
//
if (queryParams.value.status !== '' &&
item.status !== queryParams.value.status) {
return false;
}
return true;
}).sort((a, b) => a.id - b.id); // ID
});
//
const getList = async () => {
loading.value = true;
try {
const res = await getUserList();
if (res.success) {
allData.value = res.data.list; //
tableData.value = filteredData.value; //
total.value = filteredData.value.length;
} else {
ElMessage.error(res.message || '获取用户列表失败');
}
} catch (error) {
if (error.response) {
ElMessage.error(error.response.data.message || '获取用户列表失败');
} else if (error.request) {
ElMessage.error('网络错误,请检查网络连接');
} else {
ElMessage.error(error.message || '获取用户列表失败');
}
} finally {
loading.value = false;
}
}; };
// const handleDelete = (row: UserData) => {
const handleQuery = () => { console.log("删除用户:", row);
tableData.value = filteredData.value;
total.value = filteredData.value.length;
}; };
//
const resetQuery = () => {
queryParams.value = {
username: '',
role: '',
status: ''
};
tableData.value = allData.value;
total.value = allData.value.length;
};
// /
const dialogVisible = ref(false);
const dialogTitle = ref('新增用户');
const formData = ref({
username: '',
password: '',
role_id: undefined,
real_name: '',
email: '',
phone: '',
status: 1,
expire_time: undefined
});
//
const rules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 30, message: '长度在 6 到 30 个字符', trigger: 'blur' },
{
pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,30}$/,
message: '密码必须包含字母和数字',
trigger: 'blur'
}
],
role_id: [
{ required: true, message: '请选择角色', trigger: 'change' }
],
email: [
{
pattern: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/,
message: '请输入正确的邮箱格式',
trigger: 'blur'
}
],
phone: [
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号格式',
trigger: 'blur'
}
]
};
const formRef = ref(null);
const isEdit = ref(false);
const editId = ref(null);
//
const handleAdd = () => {
dialogTitle.value = '新增用户';
isEdit.value = false;
editId.value = null;
formData.value = {
username: '',
password: '',
role_id: undefined,
real_name: '',
email: '',
phone: '',
status: 1,
expire_time: undefined
};
dialogVisible.value = true;
};
//
const handleEdit = (row) => {
dialogTitle.value = '编辑用户';
isEdit.value = true;
editId.value = row.id;
formData.value = {
real_name: row.real_name || '',
email: row.email || '',
phone: row.phone || '',
role_id: row.role.id.toString(),
status: row.status.toString(),
expire_time: row.expire_time || '',
password: '' //
};
dialogVisible.value = true;
};
//
const handleSubmit = async () => {
if (!formRef.value) return;
try {
await formRef.value.validate();
const submitData = { ...formData.value };
//
if (submitData.status) {
submitData.status = parseInt(submitData.status); //
}
if (submitData.role_id) {
submitData.role_id = parseInt(submitData.role_id); //
}
//
if (!submitData.expire_time) {
delete submitData.expire_time;
}
console.log('提交的数据:', submitData); //
if (isEdit.value) {
//
if (!submitData.password) {
delete submitData.password;
}
await updateUser(editId.value, submitData);
ElMessage.success('编辑成功');
} else {
await createUser(submitData);
ElMessage.success('添加成功');
}
dialogVisible.value = false;
getList(); //
} catch (error) {
console.error('表单提交错误:', error);
if (error.response) {
console.error('错误响应数据:', error.response.data);
ElMessage.error(error.response.data.message || '提交失败,请检查表单数据');
} else if (error.request) {
ElMessage.error('网络错误,请检查网络连接');
} else {
ElMessage.error(error.message || '操作失败');
}
}
};
//
const handleDelete = (row) => {
ElMessageBox.confirm(
'确认删除该用户吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
try {
await deleteUser(row.id);
ElMessage.success('删除成功');
getList(); //
} catch (error) {
ElMessage.error(error.message || '删除失败');
}
}).catch(() => {
//
});
};
//
const roleOptions = [
{ label: '超级管理员', value: '1' },
{ label: '管理人员', value: '2' },
{ label: '普通用户', value: '3' }
];
//
onMounted(() => {
getList();
});
</script> </script>
<template> <template>
@ -263,50 +44,14 @@ onMounted(() => {
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>用户管理</span> <span>用户管理</span>
<el-button type="primary" @click="handleAdd">新增用户</el-button> <el-button type="primary">新增用户</el-button>
</div> </div>
</template> </template>
<!-- 搜索表单 --> <el-table :data="tableData" style="width: 100%">
<el-form :model="queryParams" ref="queryForm" :inline="true" class="search-form">
<el-form-item label="用户名" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入用户名"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="queryParams.role" placeholder="请选择角色" clearable>
<el-option
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option :value="1" label="启用" />
<el-option :value="0" label="禁用" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="tableData"
style="width: 100%"
>
<el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="real_name" label="用户名" width="120" /> <el-table-column prop="username" label="用户名" width="120" />
<el-table-column prop="role.name" label="角色" width="120" /> <el-table-column prop="role" label="角色" width="120" />
<el-table-column prop="email" label="邮箱" /> <el-table-column prop="email" label="邮箱" />
<el-table-column prop="status" label="状态" width="100"> <el-table-column prop="status" label="状态" width="100">
<template #default="{ row }"> <template #default="{ row }">
@ -315,121 +60,24 @@ onMounted(() => {
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="updated_at" label="创建时间" width="180"> <el-table-column prop="createTime" label="创建时间" width="180" />
<template #default="{ row }">
{{ formatDateTime(row.updated_at) }}
</template>
</el-table-column>
<el-table-column label="操作" width="180"> <el-table-column label="操作" width="180">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)"> <el-button type="primary" size="small" @click="handleEdit(row)">
编辑 编辑
</el-button> </el-button>
<el-button type="danger" link @click="handleDelete(row)"> <el-button type="danger" size="small" @click="handleDelete(row)">
删除 删除
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
:size="'small'"
:total="total"
:default-current-page="1"
:default-page-size="10"
layout="total, prev, pager, next"
:disabled="true"
/>
</div>
</el-card> </el-card>
<!-- 新增/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="500px"
@close="dialogVisible = false"
destroy-on-close
>
<el-form
ref="formRef"
:model="formData"
:rules="isEdit ? {
...rules,
password: [] //
} : rules"
label-width="100px"
class="user-form"
>
<el-form-item label="用户名" prop="username" v-if="!isEdit">
<el-input v-model="formData.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item
:label="isEdit ? '新密码' : '密码'"
prop="password"
>
<el-input
v-model="formData.password"
type="password"
show-password
:placeholder="isEdit ? '不修改请留空' : '请输入密码'"
/>
</el-form-item>
<el-form-item label="角色" prop="role_id">
<el-select v-model="formData.role_id" placeholder="请选择角色" class="w-full">
<el-option
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="真实姓名" prop="real_name">
<el-input
v-model="formData.real_name"
placeholder="请输入真实姓名"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :value="'1'">启用</el-radio>
<el-radio :value="'0'">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="过期时间" prop="expire_time">
<el-date-picker
v-model="formData.expire_time"
type="datetime"
placeholder="请选择过期时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
class="w-full"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "../../../styles/variables.scss" as *; @import "../../../styles/variables.scss";
.user-container { .user-container {
.el-card { .el-card {
@ -457,25 +105,6 @@ onMounted(() => {
} }
} }
.search-form {
margin-bottom: 20px;
.el-form-item {
margin-bottom: 16px;
:deep(.el-input),
:deep(.el-select) {
width: 240px;
}
}
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
:deep(.el-table) { :deep(.el-table) {
th.el-table__cell { th.el-table__cell {
background-color: #fafafa; background-color: #fafafa;
@ -491,23 +120,4 @@ onMounted(() => {
.el-tag { .el-tag {
border-radius: 2px; border-radius: 2px;
} }
.user-form {
.el-form-item {
margin-bottom: 20px;
}
}
.dialog-footer {
text-align: right;
padding-top: 20px;
}
.w-full {
width: 100%;
}
:deep(.el-dialog__body) {
padding: 20px 40px;
}
</style> </style>

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

14
tsconfig.app.json Normal file
View File

@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}

24
tsconfig.node.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -9,12 +9,5 @@ export default defineConfig({
alias: { alias: {
'@': path.resolve(__dirname, 'src') '@': path.resolve(__dirname, 'src')
} }
},
build: {
rollupOptions: {
input: {
main: path.resolve(__dirname, 'index.html')
}
}
} }
}) })