feat: 更新项目名称和配置,优化任务管理和字典功能

refactor(backend): 重命名任务分组为任务存储器并更新相关数据
fix(backend): 修正用户服务中密码重置的参数名错误
feat(frontend): 添加字典数据清理功能并在用户登出时调用
style(frontend): 简化加载页面样式并更新favicon
feat(frontend): 实现动态设置页面标题和图标功能
feat(frontend): 增强角色和用户管理界面的操作限制
feat(frontend): 优化任务管理界面,使用字典数据展示选项
refactor(frontend): 改进字典存储模块,增加数据校验和过滤
This commit is contained in:
zhangtao
2025-08-04 21:49:17 +08:00
parent 18bcd1961c
commit f583f1ce09
13 changed files with 194 additions and 111 deletions
+35 -30
View File
@@ -13,39 +13,44 @@ import "uno.css";
// 过渡动画
import "animate.css";
// import { useConfigStore } from "@/store";
import { useConfigStore } from "@/store";
const app = createApp(App);
// 注册插件
app.use(setupPlugins);
// 封装设置 title 和 favicon 的函数
const setTitleAndFavicon = async () => {
try {
const configStore = useConfigStore();
await configStore.getConfig();
const webTitle = configStore.configData.sys_web_title?.config_value;
const webFavicon = configStore.configData.sys_web_favicon?.config_value;
const webLogo = configStore.configData.sys_web_logo?.config_value;
if (webTitle) {
document.title = webTitle;
}
if (webFavicon) {
const favicon = document.querySelector('link[rel="icon"]');
if (favicon instanceof HTMLLinkElement) {
favicon.href = webFavicon;
}
}
if (webLogo) {
const loadingLogo = document.querySelector('.loading-container-logo');
if (loadingLogo instanceof HTMLImageElement) {
loadingLogo.src = webLogo;
}
}
} catch (error) {
console.error('获取配置数据失败:', error);
}
};
app.mount("#app");
// 封装设置 title 和 favicon 的函数
// const setTitleAndFavicon = async () => {
// try {
// const configStore = useConfigStore();
// await configStore.getConfig();
// console.log('配置数据获取成功:', configStore.configData);
// const loginTitle = configStore.configData.sys_web_title;
// console.log('sys_web_title:', loginTitle);
// const loginLogo = configStore.configData.sys_web_favicon;
// console.log('sys_web_favicon:', loginLogo);
// if (loginTitle) {
// document.title = loginTitle;
// }
// if (loginLogo) {
// const favicon = document.querySelector('link[rel="icon"]');
// if (favicon instanceof HTMLLinkElement) {
// favicon.href = loginLogo;
// }
// }
// } catch (error) {
// console.error('获取配置数据失败:', error);
// }
// };
// // 在 App 组件挂载后执行设置逻辑
// onMounted(setTitleAndFavicon);
// 在应用挂载后执行设置逻辑
setTitleAndFavicon();
+32 -4
View File
@@ -10,18 +10,43 @@ export const useDictStore = defineStore("dict", {
getDictData(): Record<string, DictDataTable[]> {
return this.dictData;
},
// 获取指定类型的字典数据,确保返回数组
// 获取指定类型的字典数据,确保返回有效的数组且项包含必需属性
getDictArray() {
return (type: string): Array<{dict_value: string; dict_label: string}> => {
return (this.dictData[type] || []).filter(
item => item.dict_value !== undefined && item.dict_label !== undefined
).map(item => ({
dict_value: item.dict_value!,
dict_label: item.dict_label!
}));
};
},
},
actions: {
// 批量获取字典数据
async getDict(types: string[]) {
// 批量获取字典数据
async getDict(types: string[]): Promise<Record<string, DictDataTable[]>> {
try {
for (const type of types) {
const response = await DictAPI.getInitDict(type);
this.dictData[type] = response.data.data as DictDataTable[];
this.isLoaded = true;
if (!this.dictData[type]) {
const response = await DictAPI.getInitDict(type);
// 确保数据格式正确
this.dictData[type] = (response.data.data as DictDataTable[] || []).filter(
item => item.dict_value !== undefined && item.dict_label !== undefined
);
this.isLoaded = true;
}
}
// 返回请求的字典数据
return types.reduce((result, type) => {
result[type] = this.getDictArray(type);
return result;
}, {} as Record<string, DictDataTable[]>);
} catch (error) {
console.error("获取字典数据失败", error);
return {};
}
},
@@ -41,6 +66,9 @@ export const useDictStore = defineStore("dict", {
};
return dict_data;
},
clearDictData() {
this.dictData = {};
},
},
persist: true,
});
+3 -1
View File
@@ -1,4 +1,4 @@
import { store, useTagsViewStore, usePermissionStoreHook } from "@/store";
import { store, useTagsViewStore, usePermissionStoreHook, useDictStoreHook } from "@/store";
import AuthAPI, {type LoginFormData } from "@/api/system/auth";
import UserAPI, {type UserInfo } from "@/api/system/user";
@@ -73,6 +73,8 @@ export const useUserStore = defineStore("user", {
usePermissionStoreHook().resetRouter();
// 清除标签视图
useTagsViewStore().delAllViews();
// 重置字典
useDictStoreHook().clearDictData()
return Promise.resolve();
},
+51 -41
View File
@@ -134,18 +134,30 @@
<el-table-column type="selection" align="center" min-width="55" />
<el-table-column type="index" label="序号" fixed min-width="60">
<template #default="scope">
{{
(queryFormData.page_no - 1) * queryFormData.page_size +
scope.$index +
1
}}
{{ (queryFormData.page_no - 1) * queryFormData.page_size + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="任务名称" prop="name" min-width="140" />
<el-table-column label="执行函数" prop="func" min-width="140" />
<el-table-column label="触发器" prop="trigger" min-width="100" />
<el-table-column label="存储器" prop="jobstore" min-width="100" />
<el-table-column label="执行器" prop="executor" min-width="100" />
<el-table-column label="执行函数" prop="func" min-width="140" >
<template #default="scope">
{{ (dictStore.getDictLabel('sys_job_function',scope.row.func) as any)?.dict_label || scope.row.func }}
</template>
</el-table-column>
<el-table-column label="触发器" prop="trigger" min-width="100">
<template #default="scope">
{{ (dictStore.getDictLabel('sys_job_trigger',scope.row.trigger) as any)?.dict_label || scope.row.trigger }}
</template>
</el-table-column>
<el-table-column label="存储器" prop="jobstore" min-width="120">
<template #default="scope">
{{ (dictStore.getDictLabel('sys_job_store',scope.row.jobstore) as any)?.dict_label || scope.row.jobstore }}
</template>
</el-table-column>
<el-table-column label="执行器" prop="executor" min-width="100">
<template #default="scope">
{{ (dictStore.getDictLabel('sys_job_executor',scope.row.executor) as any)?.dict_label || scope.row.executor }}
</template>
</el-table-column>
<el-table-column label="并发执行" prop="coalesce" min-width="100">
<template #default="scope">
<el-tag :type="scope.row.coalesce === true ? 'success' : 'danger'">
@@ -329,24 +341,31 @@
/>
</el-form-item>
<el-form-item label="任务函数" prop="func" style="width: 40%">
<el-input
v-model="formData.func"
placeholder="请输入任务函数"
:maxlength="50"
/>
<el-select v-model="formData.func" placeholder="请选择任务函数">
<el-option v-for="item in dictStore.getDictArray('sys_job_function')"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
<el-form-item label="存储器" prop="jobstore" style="width: 40%">
<el-select v-model="formData.jobstore" placeholder="请选择存储器">
<el-option value="default" label="默认(Memory)" />
<el-option value="sqlalchemy" label="数据库" />
<el-option value="redis" label="Redis存储器" />
</el-select>
<el-option v-for="item in dictStore.getDictArray('sys_job_store')"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
<el-form-item label="执行器" prop="executor" style="width: 40%">
<el-select v-model="formData.executor" placeholder="请选择执行器">
<el-option value="default" label="默认" />
<el-option value="processpool" label="进程池" />
</el-select>
<el-option v-for="item in dictStore.getDictArray('sys_job_executor')"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
<el-form-item label="位置参数" prop="args" style="width: 40%">
<el-input
@@ -382,10 +401,12 @@
</el-form-item>
<el-form-item label="触发器" prop="trigger" style="width: 40%">
<el-select v-model="formData.trigger" placeholder="请选择触发器">
<el-option value="date" label="指定日期(date)" />
<el-option value="interval" label="间隔触发器(interval)" />
<el-option value="cron" label="cron表达式" />
</el-select>
<el-option v-for="item in dictStore.getDictArray('sys_job_trigger')"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
<!-- 运行日期间隔时间或 Cron 表达式 -->
<el-form-item
@@ -552,10 +573,6 @@ import "vue3-cron-plus/dist/index.css"; // 引入样式
const dictStore = useDictStore();
// const DictDataStore = computed(() => {
// return dictStore.dictData;
// })
const queryFormRef = ref();
const dataFormRef = ref();
const total = ref(0);
@@ -565,18 +582,8 @@ const loading = ref(false);
const isExpand = ref(false);
const isExpandable = ref(true);
// const createForm = ref();
// const updateForm = ref();
// const tableLoading = ref(false);
// const openModal = ref(false);
const openCron = ref(false);
// const modalSubmitLoading = ref(false);
// const detailStateLoading = ref(false);
// const dataSource = ref<tableJobType[]>([]);
// const selectedRowKeys = ref<tableJobType['id'][]>([]);
// const queryState = reactive<searchType>({});
// const detailState = ref<tableJobType>({})
const openIntervalTab = ref(false);
const intervalTabRef = ref();
@@ -689,7 +696,7 @@ const initialFormData: JobForm = {
trigger: undefined,
args: undefined,
kwargs: undefined,
coalesce: undefined,
coalesce: false,
max_instances: 1,
jobstore: undefined,
executor: undefined,
@@ -892,7 +899,10 @@ const handleOption = (id: number, option: number) => {
});
};
onMounted(() => {
onMounted(async () => {
// 加载字典数据
await dictStore.getDict(['sys_job_function','sys_job_executor','sys_job_store', 'sys_job_trigger']);
// 加载表格数据
loadingData();
});
</script>
+32 -4
View File
@@ -133,10 +133,38 @@
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作" align="center" min-width="280">
<template #default="scope">
<el-button type="warning" size="small" link icon="position" @click="handleOpenAssignPermDialog(scope.row.id, scope.row.name)">分配权限</el-button>
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
<el-button type="danger" size="small" link icon="delete" @click="handleDelete([scope.row.id])">删除</el-button>
<el-button
type="warning"
size="small"
link
icon="position"
@click="scope.row.id === 1 ? ElMessage.warning('系统默认角色,不可操作') : handleOpenAssignPermDialog(scope.row.id, scope.row.name)"
:disabled="scope.row.id === 1"
>分配权限</el-button>
<el-button
type="info"
size="small"
link
icon="document"
@click="scope.row.id === 1 ? ElMessage.warning('系统默认角色,不可操作') : handleOpenDialog('detail', scope.row.id)"
:disabled="scope.row.id === 1"
>详情</el-button>
<el-button
type="primary"
size="small"
link
icon="edit"
@click="scope.row.id === 1 ? ElMessage.warning('系统默认角色,不可操作') : handleOpenDialog('update', scope.row.id)"
:disabled="scope.row.id === 1"
>编辑</el-button>
<el-button
type="danger"
size="small"
link
icon="delete"
@click="scope.row.id === 1 ? ElMessage.warning('系统默认角色,不可操作') : handleDelete([scope.row.id])"
:disabled="scope.row.id === 1"
>删除</el-button>
</template>
</el-table-column>
</el-table>
+31 -5
View File
@@ -152,10 +152,37 @@
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" min-width="280">
<template #default="scope">
<el-button type="warning" icon="RefreshLeft" size="small" link @click="hancleResetPassword(scope.row)">重置密码</el-button>
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
<el-button type="danger" size="small" link icon="delete" @click="handleDelete([scope.row.id])">删除</el-button>
<el-button
v-if="scope.row.username !== 'admin'"
type="warning"
icon="RefreshLeft"
size="small"
link
@click="hancleResetPassword(scope.row)"
>重置密码</el-button>
<el-button
type="info"
size="small"
link
icon="document"
@click="handleOpenDialog('detail', scope.row.id)"
>详情</el-button>
<el-button
v-if="scope.row.username !== 'admin'"
type="primary"
size="small"
link
icon="edit"
@click="handleOpenDialog('update', scope.row.id)"
>编辑</el-button>
<el-button
v-if="scope.row.username !== 'admin'"
type="danger"
size="small"
link
icon="delete"
@click="handleDelete([scope.row.id])"
>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -316,7 +343,6 @@ import DeptAPI from "@/api/system/dept";
import RoleAPI from "@/api/system/role";
import DeptTree from "./components/DeptTree.vue";
import UserImport from "./components/UserImport.vue";
const appStore = useAppStore();