diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 32767fd625..3f52d185d0 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,9 +1,9 @@
-
+
diff --git a/frontend/src/api/modules/setting/project.ts b/frontend/src/api/modules/project-management/project.ts
similarity index 78%
rename from frontend/src/api/modules/setting/project.ts
rename to frontend/src/api/modules/project-management/project.ts
index 2d0fc60531..d6206db5c2 100644
--- a/frontend/src/api/modules/setting/project.ts
+++ b/frontend/src/api/modules/project-management/project.ts
@@ -1,5 +1,5 @@
import MSR from '@/api/http/index';
-import { ProjectListUrl } from '@/api/requrls/setting/project';
+import { ProjectListUrl } from '@/api/requrls/project-management/project';
import type { ProjectListItem } from '@/models/setting/project';
export function getProjectList(organizationId: string) {
diff --git a/frontend/src/api/modules/setting/log.ts b/frontend/src/api/modules/setting/log.ts
index 5e26d6b956..5f23545753 100644
--- a/frontend/src/api/modules/setting/log.ts
+++ b/frontend/src/api/modules/setting/log.ts
@@ -6,13 +6,15 @@ import {
GetOrgLogListUrl,
GetOrgLogOptionsUrl,
GetOrgLogUserUrl,
+ GetProjectLogListUrl,
+ GetProjectLogUserUrl,
} from '@/api/requrls/setting/log';
import type { CommonList } from '@/models/common';
-import type { LogOptions, LogItem, UserItem } from '@/models/setting/log';
+import type { LogOptions, LogItem, UserItem, LogListParams } from '@/models/setting/log';
// 获取系统日志列表
-export function getSystemLogList(data: any) {
+export function getSystemLogList(data: LogListParams) {
return MSR.post>({ url: GetSystemLogListUrl, data });
}
@@ -22,12 +24,12 @@ export function getSystemLogOptions() {
}
// 获取系统日志-操作用户列表
-export function getSystemLogUsers() {
- return MSR.get({ url: GetSystemLogUserUrl });
+export function getSystemLogUsers({ keyword }: { keyword: string }) {
+ return MSR.get({ url: GetSystemLogUserUrl, params: { keyword } });
}
// 获取组织日志列表
-export function getOrgLogList(data: any) {
+export function getOrgLogList(data: LogListParams) {
return MSR.post>({ url: GetOrgLogListUrl, data });
}
@@ -37,6 +39,16 @@ export function getOrgLogOptions(id: string) {
}
// 获取组织日志-操作用户列表
-export function getOrgLogUsers(id: string) {
- return MSR.get({ url: GetOrgLogUserUrl, params: id });
+export function getOrgLogUsers({ id, keyword }: { id: string; keyword: string }) {
+ return MSR.get({ url: `${GetOrgLogUserUrl}/${id}`, params: { keyword } });
+}
+
+// 获取项目日志列表
+export function getProjectLogList(data: LogListParams) {
+ return MSR.post>({ url: GetProjectLogListUrl, data });
+}
+
+// 获取项目日志-操作用户列表
+export function getProjectLogUsers({ id, keyword }: { id: string; keyword: string }) {
+ return MSR.get({ url: `${GetProjectLogUserUrl}/${id}`, params: { keyword } });
}
diff --git a/frontend/src/api/requrls/project-management/project.ts b/frontend/src/api/requrls/project-management/project.ts
new file mode 100644
index 0000000000..75b85d9d24
--- /dev/null
+++ b/frontend/src/api/requrls/project-management/project.ts
@@ -0,0 +1,3 @@
+export const ProjectListUrl = '/project/list/options';
+
+export default {};
diff --git a/frontend/src/api/requrls/setting/log.ts b/frontend/src/api/requrls/setting/log.ts
index 289d3ef26f..0348d2b23e 100644
--- a/frontend/src/api/requrls/setting/log.ts
+++ b/frontend/src/api/requrls/setting/log.ts
@@ -5,5 +5,9 @@ export const GetSystemLogUserUrl = '/operation/log/user/list'; // 搜索操作
// 组织级别日志
export const GetOrgLogListUrl = '/organization/log/list';
-export const GetOrgLogOptionsUrl = '/organization/log/get/options'; // 获取组织/项目级联下拉框选项
+export const GetOrgLogOptionsUrl = '/organization/log/get/options'; // 获取项目级联下拉框选
export const GetOrgLogUserUrl = '/organization/log/user/list'; // 搜索操作用户
+
+// 项目级别日志
+export const GetProjectLogListUrl = '/project/log/list';
+export const GetProjectLogUserUrl = '/project/log/user/list'; // 搜索操作用户
diff --git a/frontend/src/api/requrls/setting/project.ts b/frontend/src/api/requrls/setting/project.ts
deleted file mode 100644
index 15c761121a..0000000000
--- a/frontend/src/api/requrls/setting/project.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const ProjectListUrl = '/system/project/list';
-
-export default {};
diff --git a/frontend/src/assets/style/arco-reset.less b/frontend/src/assets/style/arco-reset.less
index d62893c4fd..74cc039d39 100644
--- a/frontend/src/assets/style/arco-reset.less
+++ b/frontend/src/assets/style/arco-reset.less
@@ -526,6 +526,18 @@
background-color: var(--color-text-input-border);
}
}
+.ms-card-container .arco-scrollbar .arco-scrollbar-track-direction-vertical {
+ right: -10px;
+}
+.ms-card-container .arco-scrollbar .arco-scrollbar-track-direction-horizontal {
+ bottom: -10px;
+}
+.ms-base-table .arco-scrollbar .arco-scrollbar-track-direction-vertical {
+ right: 0;
+}
+.ms-base-table .arco-scrollbar .arco-scrollbar-track-direction-horizontal {
+ bottom: 0;
+}
.arco-scrollbar-track-direction-vertical {
width: 6px;
.arco-scrollbar-thumb-bar {
diff --git a/frontend/src/components/business/ms-search-select/index.tsx b/frontend/src/components/business/ms-search-select/index.tsx
index 9dd77a895d..2c0a0d8d63 100644
--- a/frontend/src/components/business/ms-search-select/index.tsx
+++ b/frontend/src/components/business/ms-search-select/index.tsx
@@ -1,19 +1,30 @@
-import { watch, ref, h, defineComponent } from 'vue';
+import { watch, ref, h, defineComponent, onBeforeMount } from 'vue';
import { debounce } from 'lodash-es';
import { useI18n } from '@/hooks/useI18n';
import type { SelectOptionData } from '@arco-design/web-vue';
export type ModelType = string | number | Record | (string | number | Record)[];
+export type RemoteFieldsMap = {
+ id: string;
+ value: string;
+ label: string;
+ [key: string]: string;
+};
export interface MsSearchSelectProps {
+ mode?: 'static' | 'remote'; // 静态模式,远程模式。默认为静态模式,需要传入 options 数据;远程模式需要传入请求函数
modelValue: ModelType;
allowClear?: boolean;
placeholder?: string;
prefix?: string;
searchKeys: string[]; // 需要搜索的 key 名,关键字会遍历这个 key 数组,然后取 item[key] 进行模糊匹配
options: SelectOptionData[];
+ remoteFieldsMap?: RemoteFieldsMap; // 远程模式下的结果 key 映射,例如 { value: 'id' },表示远程请求时,会将返回结果的 id 赋值到 value 字段
+ remoteExtraParams?: Record; // 远程模式下的额外参数
+ remoteFunc?(params: Record): Promise; // 远程模式下的请求函数,返回一个 Promise
optionLabelRender?: (item: SelectOptionData) => string; // 自定义 option 的 label 渲染,返回一个 html 字符串,默认使用 item.label
+ optionTooltipContent?: (item: SelectOptionData) => string; // 自定义 option 的 tooltip 内容,返回一个字符串,默认使用 item.label
}
export default defineComponent(
@@ -21,7 +32,8 @@ export default defineComponent(
const { t } = useI18n();
const innerValue = ref(props.modelValue);
- const filterOptions = ref([...props.options]);
+ const filterOptions = ref([]);
+ const remoteOriginOptions = ref([...props.options]);
watch(
() => props.modelValue,
(val) => {
@@ -36,34 +48,73 @@ export default defineComponent(
}
);
- function handleUserSearch(val: string) {
- if (val.trim() === '') {
- filterOptions.value = [...props.options];
- return;
- }
- const highlightedKeyword = `${val}`;
- filterOptions.value = props.options
- .map((e) => {
- const item = { ...e };
- let hasMatch = false;
- for (let i = 0; i < props.searchKeys.length; i++) {
- // 遍历传入的搜索字段
- const key = props.searchKeys[i];
+ const loading = ref(false);
- if (e[key].includes(val)) {
- // 是否匹配
- hasMatch = true;
- item[key] = e[key].replace(new RegExp(val, 'gi'), highlightedKeyword); // 高亮关键字替换
+ async function handleUserSearch(val: string) {
+ try {
+ loading.value = true;
+ // 如果是远程模式,则请求接口数据
+ if (props.mode === 'remote' && typeof props.remoteFunc === 'function') {
+ remoteOriginOptions.value = (await props.remoteFunc({ ...props.remoteExtraParams, keyword: val })).map(
+ (e: any) => {
+ const item = {
+ ...e,
+ };
+ // 支持接口字段自定义映射
+ if (props.remoteFieldsMap) {
+ const map = props.remoteFieldsMap;
+ Object.keys(map).forEach((key) => {
+ item[key] = e[map[key]];
+ });
+ }
+ // 为了避免关键词搜索影响 label 值,这里需要开辟新字段存储 tooltip 内容
+ item.tooltipContent =
+ typeof props.optionTooltipContent === 'function' ? props.optionTooltipContent(e) : e.label;
+ return item;
}
- }
- if (hasMatch) {
- return item;
- }
- return null;
- })
- .filter((e) => e) as SelectOptionData[];
+ );
+ }
+ if (val.trim() === '') {
+ // 如果搜索关键字为空,则直接返回所有数据
+ filterOptions.value = [...remoteOriginOptions.value];
+ return;
+ }
+ const highlightedKeyword = `${val}`;
+ filterOptions.value = remoteOriginOptions.value
+ .map((e) => {
+ const item = { ...e };
+ let hasMatch = false;
+ for (let i = 0; i < props.searchKeys.length; i++) {
+ // 遍历传入的搜索字段
+ const key = props.searchKeys[i];
+ if (e[key].includes(val)) {
+ // 是否匹配
+ hasMatch = true;
+ item[key] = e[key].replace(new RegExp(val, 'gi'), highlightedKeyword); // 高亮关键字替换
+ }
+ }
+ if (hasMatch) {
+ return item;
+ }
+ return null;
+ })
+ .filter((e) => e) as SelectOptionData[];
+ } catch (error) {
+ console.log(error);
+ } finally {
+ loading.value = false;
+ }
}
+ const optionItemLabelRender = (item: SelectOptionData) =>
+ typeof props.optionLabelRender === 'function'
+ ? h('div', { innerHTML: props.optionLabelRender(item) })
+ : item.label;
+
+ onBeforeMount(() => {
+ handleUserSearch('');
+ });
+
return () => (
emit('update:modelValue', value)}
onInputValueChange={debounce(handleUserSearch, 300)}
>
@@ -78,19 +130,32 @@ export default defineComponent(
prefix: () => t(props.prefix || ''),
default: () =>
filterOptions.value.map((item) => (
-
- {typeof props.optionLabelRender === 'function'
- ? h('div', { innerHTML: props.optionLabelRender(item) })
- : item.label}
-
+
+
+ {optionItemLabelRender(item)}
+
+
)),
}}
);
},
{
- // eslint-disable-next-line vue/require-prop-types
- props: ['modelValue', 'allowClear', 'placeholder', 'prefix', 'searchKeys', 'options', 'optionLabelRender'],
+ /* eslint-disable vue/require-prop-types */
+ props: [
+ 'mode',
+ 'modelValue',
+ 'allowClear',
+ 'placeholder',
+ 'prefix',
+ 'searchKeys',
+ 'options',
+ 'optionLabelRender',
+ 'remoteFieldsMap',
+ 'remoteExtraParams',
+ 'remoteFunc',
+ 'optionTooltipContent',
+ ],
emits: ['update:modelValue'],
}
);
diff --git a/frontend/src/components/pure/ms-card/index.vue b/frontend/src/components/pure/ms-card/index.vue
index ba3897ae66..47bc73e3b0 100644
--- a/frontend/src/components/pure/ms-card/index.vue
+++ b/frontend/src/components/pure/ms-card/index.vue
@@ -140,9 +140,6 @@
.arco-divider {
@apply mb-0;
}
- .ms-card-container {
- padding: 0;
- }
}
.card-header {
@apply flex items-center;
@@ -160,11 +157,5 @@
}
}
}
- :deep(.arco-scrollbar-track-direction-vertical) {
- right: -10px;
- }
- :deep(.arco-scrollbar-track-direction-horizontal) {
- bottom: -10px;
- }
}
diff --git a/frontend/src/components/pure/navbar/index.vue b/frontend/src/components/pure/navbar/index.vue
index 0c27440272..028cdb7ae1 100644
--- a/frontend/src/components/pure/navbar/index.vue
+++ b/frontend/src/components/pure/navbar/index.vue
@@ -24,8 +24,9 @@
{{ project.name }}
+ {{ project.name }}
+
@@ -188,7 +189,7 @@
import TopMenu from '@/components/business/ms-top-menu/index.vue';
import MessageBox from '../message-box/index.vue';
import { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
- // import { getProjectList } from '@/api/modules/setting/project';
+ import { getProjectList } from '@/api/modules/project-management/project';
import { useI18n } from '@/hooks/useI18n';
import type { ProjectListItem } from '@/models/setting/project';
@@ -207,12 +208,12 @@
const projectList: Ref = ref([]);
onBeforeMount(async () => {
- // try {
- // const res = await getProjectList(appStore.getCurrentOrgId);
- // projectList.value = res;
- // } catch (error) {
- // console.log(error);
- // }
+ try {
+ const res = await getProjectList(appStore.getCurrentOrgId);
+ projectList.value = res;
+ } catch (error) {
+ console.log(error);
+ }
});
const showProjectSelect = computed(() => {
@@ -364,4 +365,4 @@
}
}
-@/models/setting/project @/api/modules/setting/project
+@/models/setting/project @/api/modules/setting/project @/api/modules/project-management/project
diff --git a/frontend/src/components/pure/project-selection/index.vue b/frontend/src/components/pure/project-selection/index.vue
deleted file mode 100644
index a646613ff3..0000000000
--- a/frontend/src/components/pure/project-selection/index.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/src/models/setting/log.ts b/frontend/src/models/setting/log.ts
index e518a5c224..7a098aa308 100644
--- a/frontend/src/models/setting/log.ts
+++ b/frontend/src/models/setting/log.ts
@@ -1,5 +1,36 @@
import type { RouteEnum } from '@/enums/routeEnum';
+interface Sort {
+ [key: string]: string;
+}
+
+interface Combine {
+ [key: string]: any;
+}
+
+interface Filter {
+ [key: string]: string[];
+}
+
+export interface LogListParams {
+ keyword: string;
+ filter: Filter;
+ combine: Combine;
+ current: number;
+ pageSize: number;
+ sort: Sort;
+ operUser: string; // 操作人
+ startTime: number;
+ endTime: number;
+ projectIds: string[]; // 项目 id 集合
+ organizationIds: string[]; // 组织 id 集合
+ type: string; // 操作类型
+ module: string; // 操作对象
+ content: string; // 操作名称
+ level: string; // 系统/组织/项目级别
+ sortString: string;
+}
+
export interface OptionsItem {
id: string;
name: string;
diff --git a/frontend/src/store/modules/user/index.ts b/frontend/src/store/modules/user/index.ts
index c5c710569e..95ac3782de 100644
--- a/frontend/src/store/modules/user/index.ts
+++ b/frontend/src/store/modules/user/index.ts
@@ -61,6 +61,7 @@ const useUserStore = defineStore('user', {
setToken(res.sessionId, res.csrfToken);
const appStore = useAppStore();
appStore.setCurrentOrgId(res.lastOrganizationId || '');
+ appStore.setCurrentProjectId(res.lastProjectId || '');
this.setInfo(res);
} catch (err) {
clearToken();
@@ -96,9 +97,8 @@ const useUserStore = defineStore('user', {
const appStore = useAppStore();
setToken(res.sessionId, res.csrfToken);
this.setInfo(res);
- if (appStore.currentOrgId === '') {
- appStore.setCurrentOrgId(res.lastOrganizationId || '');
- }
+ appStore.setCurrentOrgId(res.lastOrganizationId || '');
+ appStore.setCurrentProjectId(res.lastProjectId || '');
return true;
} catch (err) {
return false;
diff --git a/frontend/src/views/setting/system/log/components/logCards.vue b/frontend/src/views/setting/system/log/components/logCards.vue
index 575db77341..a63d150274 100644
--- a/frontend/src/views/setting/system/log/components/logCards.vue
+++ b/frontend/src/views/setting/system/log/components/logCards.vue
@@ -1,17 +1,29 @@
-
+
+
+
@@ -53,6 +67,7 @@
:placeholder="t('system.log.operateTargetPlaceholder')"
:panel-width="100"
strictly
+ class="filter-item"
/>
+
+
{{ t('system.log.search') }}
+
+ {{ t('system.log.reset') }}
+
+
- {{ t('system.log.search') }}
-
- {{ t('system.log.reset') }}
-
+
+ {{ t('system.log.search') }}
+
+ {{ t('system.log.reset') }}
+
+