2
0

Merge remote-tracking branch 'origin/main'

This commit is contained in:
zhongzm
2024-12-13 19:20:44 +08:00
20 changed files with 1471 additions and 39 deletions

View File

@@ -0,0 +1,213 @@
<script setup lang="tsx">
import { Button, Popconfirm, Tag } from 'ant-design-vue';
import type { Key } from 'ant-design-vue/es/_util/type';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { SimpleScrollbar } from '~/packages/materials/src';
import logSearch from './modules/log-search.vue';
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: 1000
};
});
const { columns, columnChecks, data, loading, getData, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: doGetlogList,
apiParams: {
pageNum: 1,
pageSize: 10,
status: undefined,
},
rowKey: 'operId',
columns: () => [
{
key: 'operId',
dataIndex: 'operId',
title: t('page.manage.log.logId'),
align: 'center'
},
{
key: 'title',
dataIndex: 'title',
title: t('page.manage.log.module'),
align: 'center'
},
{
key: 'operatorType',
dataIndex: 'operatorType',
title: t('page.manage.log.operType'),
align: 'center',
customRender: ({ record }: any) => {
if (record.operatorType === null) {
return null;
}
const tagMap: any = {
0: t('page.manage.log.other'),
1: t('page.manage.log.backUser'),
2: t('page.manage.log.phoneUser'),
};
const tagColor: any = {
'0': 'pink',
'1': 'warning',
'2': 'blue',
};
return <Tag color={tagColor[record.operatorType]}> {tagMap[record.operatorType]} </Tag>;
}
},
{
key: 'operName',
dataIndex: 'operName',
title: t('page.manage.log.operName'),
align: 'center'
},
{
key: 'operIp',
dataIndex: 'operIp',
title: t('page.manage.log.operIp'),
align: 'center',
},
{
key: 'status',
dataIndex: 'status',
align: 'center',
customRender: ({ record }: any) => {
if (record.status === null) {
return null;
}
const tagMap: any = {
0: t('common.normal'),
1: t('common.abnormal'),
};
const tagColor: any = {
'0': 'success',
'1': 'error'
};
return <Tag color={tagColor[record.status]}> {tagMap[record.status]} </Tag>;
},
title: t('page.manage.log.operStatus'),
},
{
key: 'operTime',
dataIndex: 'operTime',
align: 'center',
title: t('page.manage.log.operTime'),
},
{
key: 'costTime',
dataIndex: 'costTime',
align: 'center',
title: t('page.manage.log.useTime'),
customRender: ({ record }: any) => {
if (!record.costTime) {
return '0ms';
}
return `${record.costTime} ${t('common.ms')}`;
},
},
// {
// key: 'operate',
// title: t('common.operate'),
// align: 'center',
// width: 200,
// customRender: ({ record }: any) =>
// !record.admin && (
// <div class="flex justify-around gap-8px">
// {isShowBtn('system:user:remove') && (
// <Popconfirm onConfirm={() => handleDelete(record.operId)} title={t('common.confirmDelete')} >
// <Button danger size="small" >
// {t('common.delete')}
// </Button>
// </Popconfirm>
// )}
// </div>
// )
// }
]
});
const {
handleAdd,
checkedRowKeys,
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, { getData, idKey: 'operId' });
const deptTreeData = ref<Api.Common.CommonTree>([]);
onMounted(() => {
getUserDeptTree();
});
async function handleBatchDelete() {
const { error } = await doDeleteLog(checkedRowKeys.value);
if (!error) {
onBatchDeleted();
}
}
async function handleDelete(id: number) {
const { error } = await doDeleteLog([id]);
if (!error) {
onDeleted();
}
}
function handleUserSelectChange(selectedRowKeys: Key[], selectedRows: any[]) {
checkedRowKeys.value = selectedRowKeys as number[];
}
function fnTest() {
searchParams.value = {
};
resetSearchParams();
// 使用 nextTick 确保视图更新后检查
nextTick(() => {
console.log('视图更新后:', { ...searchParams.value });
console.log(searchParams.value);
});
}
async function getUserDeptTree() {
const { error, data: tree } = await doGetUserDeptTree();
if (!error) {
deptTreeData.value = tree;
}
}
</script>
<template>
<SimpleScrollbar>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<logSearch v-model:model="searchParams" :dept-tree-data="deptTreeData" @reset="fnTest" @search="getData" />
<ACard :title="t('page.manage.user.title')" :bordered="false" :body-style="{ flex: 1, overflow: 'hidden' }"
class="flex-col-stretch sm:flex-1-hidden card-wrapper">
<template #extra>
<TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0"
:loading="loading" :show-delete="true" @add="handleAdd" @delete="handleBatchDelete" @refresh="getData"
:not-show-add="true" />
</template>
<ATable ref="wrapperEl" row-key="operId" :columns="columns" :data-source="data" :loading="loading"
:row-selection="{
selectedRowKeys: checkedRowKeys,
onChange: handleUserSelectChange,
}" size="small" :pagination="mobilePagination" :scroll="scrollConfig" class="h-full" />
</ACard>
</div>
</SimpleScrollbar>
</template>
<style scoped></style>

View File

@@ -0,0 +1,127 @@
<script setup lang="ts">
import { $t } from '@/locales';
import { enableStatusOptions } from '@/constants/business';
import { SyncOutlined, SearchOutlined } from '@ant-design/icons-vue';
defineOptions({
name: 'logSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
defineProps<{
deptTreeData: Api.Common.CommonTree;
}>();
const emit = defineEmits<Emits>();
const model = defineModel<any>('model', { required: true });
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
function reset() {
// 重置表单
//formRef.value?.resetFields();
// 重置日期选择器
queryRangePicker.value = ['', ''];
// 确保 model 也被重置
model.value = {};
// 使用 nextTick 确保视图更新后检查
nextTick(() => {
console.log('视图更新后:', { ...model.value });
console.log(model);
});
emit('reset');
}
function search() {
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
model.value.beginTime = queryRangePicker.value[0];
model.value.endTime = queryRangePicker.value[1];
emit('search');
}
</script>
<template>
<ACard :title="$t('common.search')" :bordered="false" class="card-wrapper">
<AForm :model="model" :label-width="80">
<ARow :gutter="[16, 16]" wrap>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.log.operIp')" name="operIp" class="m-0">
<AInput v-model:value="model.operIp" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.log.module')" name="title" class="m-0">
<AInput v-model:value="model.title" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.user.status')" name="status" class="m-0">
<ASelect v-model:value="model.status" :placeholder="$t('page.manage.user.form.status')" allow-clear>
<ASelectOption v-for="option in enableStatusOptions" :key="option.value" :value="option.value">
{{ $t(option.label) }}
</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.log.operName')" name="operName" class="m-0">
<AInput v-model:value="model.operName" />
</AFormItem>
</ACol>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item :label="$t('page.manage.log.useTime')" name="queryRangePicker">
<a-range-picker v-model:value="queryRangePicker" allow-clear bordered show-time
value-format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" style="width: 100%"></a-range-picker>
</a-form-item>
</a-col>
<ACol :lg="16" :md="12" :xs="24">
<AFormItem class="m-0">
<div class="w-full flex-y-center justify-end gap-8px">
<a-space :size="8">
<a-button @click="reset">
<template #icon>
<SyncOutlined />
</template>
{{ $t('common.reset') }}
</a-button>
<AButton type="primary" ghost @click="search">
<template #icon>
<SearchOutlined />
</template>
{{ $t('common.search') }}
</AButton>
</a-space>
</div>
</AFormItem>
</ACol>
</ARow>
</AForm>
</ACard>
</template>
<style scoped></style>

View File

@@ -6,6 +6,8 @@ import { $t } from '@/locales';
import { enableStatusRecord } from '@/constants/business';
import RoleOperateDrawer from './modules/role-operate-drawer.vue';
import RoleSearch from './modules/role-search.vue';
import {useI18n} from "vue-i18n";
const { t } = useI18n();
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
@@ -45,17 +47,17 @@ const { columns, columnChecks, data, loading, getData, mobilePagination, searchP
dataIndex: 'status',
title: $t('page.manage.role.roleStatus'),
align: 'center',
customRender: ({ record }) => {
customRender: ({ record }:any) => {
if (record.status === null) {
return null;
}
const tagMap: Record<Api.Common.EnableStatus, string> = {
const tagMap: any = {
'0': 'success',
'1': 'warning'
};
const label = $t(enableStatusRecord[record.status]);
const label = t(enableStatusRecord[record.status]);
return <Tag color={tagMap[record.status]}>{label}</Tag>;
}
@@ -76,7 +78,7 @@ const { columns, columnChecks, data, loading, getData, mobilePagination, searchP
title: $t('common.operate'),
align: 'center',
width: 200,
customRender: ({ record }) =>
customRender: ({ record }:any) =>
!record.admin && (
<div class="flex justify-around gap-8px">
{isShowBtn('system:role:edit') && (

View File

@@ -14,7 +14,7 @@ interface Props {
/** the type of operation */
operateType: AntDesign.TableOperateType;
/** the edit row data */
rowData?: Api.SystemManage.Role | null;
rowData?: any;
}
const props = defineProps<Props>();

View File

@@ -0,0 +1,630 @@
<script setup lang="tsx">
import { message, Tag } from 'ant-design-vue';
import type { Key } from 'ant-design-vue/es/_util/type';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { SimpleScrollbar } from '~/packages/materials/src';
import taskOperateDrawer from './modules/task-operate-drawer.vue';
import { useI18n } from "vue-i18n";
import { SyncOutlined, SearchOutlined, ProfileOutlined, FormOutlined, DeleteOutlined, RocketOutlined, ContainerOutlined } from '@ant-design/icons-vue';
import { enableStatusOptions } from '@/constants/business';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
const { defaultRequiredRule, formRules } = useFormRules();
const rules = {
jonName: defaultRequiredRule,
invokeTarget: defaultRequiredRule,
cronExpression: defaultRequiredRule,
};
const { t } = useI18n();
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
/**对话框对象信息状态 */
let modalState: any = reactive({
openByView: false,
openByEdit: false,
title: '任务',
from: {
jobId: undefined,
jobName: '',
invokeTarget: '',
cronExpression: '',
misfirePolicy: '3',
concurrent: '0',
jobGroup: 'DEFAULT',
status: '0',
saveLog: '0',
targetParams: '',
remark: '',
},
confirmLoading: false,
openByCron: false,
});
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: 1000
};
});
const { columns, columnChecks, data, loading, getData, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: doGetjobList,
apiParams: {
pageNum: 1,
pageSize: 10,
},
rowKey: 'jobId',
columns: () => [
{
key: 'jobId',
dataIndex: 'jobId',
title: t('page.manage.task.taskId'),
align: 'center'
},
{
key: 'jobName',
dataIndex: 'jobName',
title: t('page.manage.task.taskName'),
align: 'center'
},
{
key: 'jobGroup',
dataIndex: 'jobGroup',
title: t('page.manage.task.group'),
align: 'center',
// customRender: ({ record }: any) => {
// if (record.operatorType === null) {
// return null;
// }
// const tagMap: any = {
// 0: t('page.manage.log.other'),
// 1: t('page.manage.log.backUser'),
// 2: t('page.manage.log.phoneUser'),
// };
// const tagColor: any = {
// '0': 'pink',
// '1': 'warning',
// '2': 'blue',
// };
// return <Tag color={tagColor[record.operatorType]}>{tagMap[record.operatorType]}</Tag>;
// }
},
{
key: 'invokeTarget',
dataIndex: 'invokeTarget',
title: t('page.manage.task.invoke'),
align: 'center'
},
{
key: 'cronExpression',
dataIndex: 'cronExpression',
title: t('page.manage.task.cron'),
align: 'center',
},
{
key: 'status',
dataIndex: 'status',
align: 'center',
customRender: ({ record }: any) => {
if (record.status === null) {
return null;
}
const tagMap: any = {
'0': t('common.normal'),
'1': t('common.abnormal'),
};
const tagColor: any = {
'0': 'success',
'1': 'error'
};
return <Tag color={tagColor[record.status]}>{tagMap[record.status]}</Tag>;
},
title: t('page.manage.log.operStatus'),
},
{
key: 'operate',
title: t('common.operate'),
align: 'center',
width: 200,
// customRender: ({ record }: any) =>
// !record.admin && (
// <div class="flex justify-around gap-8px">
// <Popconfirm onConfirm={() => handleDelete(record.operId)} title={t('common.confirmDelete')}>
// <a-button danger size="small">
// <template #icon>
// <SyncOutlined />
// </template>
// </a-button>
// </Popconfirm>
// </div>
// )
}
]
});
const {
drawerVisible,
operateType,
editingData,
handleAdd,
checkedRowKeys,
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, { getData, idKey: 'jobId' });
const deptTreeData = ref<Api.Common.CommonTree>([]);
onMounted(() => {
});
async function handleBatchDelete() {
const { error } = await doDeleteLog(checkedRowKeys.value);
if (!error) {
onBatchDeleted();
}
}
async function handleDelete(id: number) {
const { error } = await doDeleteLog([id]);
if (!error) {
onDeleted();
}
}
function handleUserSelectChange(selectedRowKeys: Key[], selectedRows: any[]) {
checkedRowKeys.value = selectedRowKeys as number[];
}
function fnTest() {
searchParams.value = {
};
resetSearchParams();
// 使用 nextTick 确保视图更新后检查
nextTick(() => {
console.log(operateType)
});
}
async function fnRecordView(jobId: number) {
// const { error, data } = await doGetjobInfo(jobId);
// // if (!error) {
// // onDeleted();
// // }
// console.log(data);
operateType.value = 'view';
if (modalState.confirmLoading) return;
const hide = message.loading('Waiting...', 0);
modalState.confirmLoading = true;
doGetjobInfo(jobId).then(res => {
modalState.confirmLoading = false;
hide();
console.log(res);
if (!res.error) {
modalState.from = Object.assign(modalState.from, res.data);
modalState.openByView = true;
modalState.title = t('page.manage.task.viewJob');
console.log(modalState.openByView);
}
// if (res.code === RESULT_CODE_SUCCESS && res.data) {
// modalState.from = Object.assign(modalState.from, res.data);
// modalState.title = t('views.manage.job.viewJob');
// modalState.openByView = true;
// } else {
// message.error(t('views.manage.job.viewInfoErr'), 2);
// }
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.openByView = false;
// modalStateFrom.resetFields();
}
/**
* 对话框弹出显示为 新增或者修改
* @param jobId 任务id, 不传为新增
*/
function fnModalVisibleByEdit(jobId?: string | number) {
if (!jobId) {
//modalStateFrom.resetFields();
modalState.title = t('page.manage.task.addJob');
modalState.openByEdit = true;
} else {
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
doGetjobInfo(jobId).then(res => {
modalState.confirmLoading = false;
hide();
if (!res.error) {
modalState.from = Object.assign(modalState.from, res.data);
modalState.title = t('page.manage.task.editJob');
modalState.openByEdit = true;
} else {
message.error(t('page.manage.task.getInfoError'), 2);
}
});
}
}
</script>
<template>
<SimpleScrollbar>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<!-- 搜索框 -->
<ACard :title="$t('common.search')" :bordered="false" class="card-wrapper">
<AForm :model="searchParams" :label-width="80">
<ARow :gutter="[16, 16]" wrap>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem label="Name" name="operIp" class="m-0">
<AInput v-model:value="searchParams.operIp" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem label="Group" name="title" class="m-0">
<AInput v-model:value="searchParams.title" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.user.status')" name="status" class="m-0">
<ASelect v-model:value="searchParams.status" :placeholder="$t('page.manage.user.form.status')"
allow-clear>
<ASelectOption v-for="option in enableStatusOptions" :key="option.value" :value="option.value">
{{ $t(option.label) }}
</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem class="m-0">
<div class="w-full flex-y-center justify-end gap-8px">
<a-space :size="8">
<a-button @click="fnTest">
<template #icon>
<SyncOutlined />
</template>
{{ $t('common.reset') }}
</a-button>
<AButton type="primary" ghost @click="getData">
<template #icon>
<SearchOutlined />
</template>
{{ $t('common.search') }}
</AButton>
</a-space>
</div>
</AFormItem>
</ACol>
</ARow>
</AForm>
</ACard>
<ACard title="Task Management" :bordered="false" :body-style="{ flex: 1, overflow: 'hidden' }"
class="flex-col-stretch sm:flex-1-hidden card-wrapper">
<template #extra>
<!-- <TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0"
:loading="loading" :show-delete="true" @add="handleAdd" @delete="handleBatchDelete" @refresh="getData"
:showExport="true" :not-show-add="false" /> -->
<div class="flex flex-wrap justify-end gap-x-12px gap-y-8px lt-sm:(w-200px py-12px)">
<slot name="prefix"></slot>
<slot name="default">
<AButton size="small" ghost type="primary" @click="fnModalVisibleByEdit()">
<div class="flex-y-center gap-8px">
<icon-ic-round-plus class="text-icon" />
<span>{{ $t('common.add') }}</span>
</div>
</AButton>
<APopconfirm :title="$t('common.confirmDelete')" :disabled="checkedRowKeys.length === 0"
@confirm="handleBatchDelete">
<AButton size="small" danger :disabled="checkedRowKeys.length <= 0">
<div class="flex-y-center gap-8px">
<icon-ic-round-delete class="text-icon" />
<span>{{ $t('common.batchDelete') }}</span>
</div>
</AButton>
</APopconfirm>
</slot>
<AButton size="small" @click="getData">
<div class="flex-y-center gap-8px">
<icon-mdi-refresh class="text-icon" :class="{ 'animate-spin': loading }" />
<span>{{ $t('common.refresh') }}</span>
</div>
</AButton>
<!-- <AButton size="small" type="primary" @click="handleExport" v-show="showExport">
<div class="flex-y-center gap-8px">
<icon-mdi-refresh class="text-icon" :class="{ 'animate-spin': loading }" />
<span>{{ $t('common.export') }}</span>
</div>
</AButton> -->
<TableColumnSetting v-model:columns="columns" />
<slot name="suffix"></slot>
</div>
</template>
<ATable ref="wrapperEl" row-key="jobId" :columns="columns" :data-source="data" :loading="loading"
:row-selection="{
selectedRowKeys: checkedRowKeys,
onChange: handleUserSelectChange,
}" size="small" :pagination="mobilePagination" :scroll="scrollConfig" class="h-full">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<a-switch v-model:checked="record.status" checked-value="0" un-checked-value="1" size="small" />
<!-- <DictTag :options="{
'0': t('common.normal'),
'1': t('common.abnormal'),
}" :value="record.status" /> -->
</template>
<template v-if="column.key === 'operate'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ 'View' }}</template>
<a-button type="link" @click.prevent="fnRecordView(record.jobId)">
<template #icon>
<ProfileOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ 'Edit' }}</template>
<a-button type="link" @click.prevent="fnModalVisibleByEdit(record.jobId)">
<template #icon>
<FormOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ 'Delete' }}</template>
<a-button type="link">
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ 'run' }}</template>
<a-button type="link">
<template #icon>
<RocketOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ 'Job Log' }}</template>
<a-button type="link">
<template #icon>
<ContainerOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
</ATable>
<!-- <taskOperateDrawer v-model:visible="drawerVisible" :dept-tree-data="deptTreeData" :operate-type="operateType"
:row-data="editingData" @submitted="getData" /> -->
<!-- 详情框 -->
<a-modal :width="800" v-model:open="modalState.openByView" :title="modalState.title" @cancel="fnModalCancel">
<a-form layout=" horizontal" :label-col="{ span: 6 }" :label-wrap="true">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="$t('page.manage.task.taskName')" name="jobName">
{{ modalState.from.jobName }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.status')" name="status">
{{
[{
label: t('common.normal'),
value: '0',
}, {
label: t('common.abnormal'),
value: '1',
}].find(s => s.value === modalState.from.status)
?.label
}}
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.invoke')" name="invokeTarget">
{{ modalState.from.invokeTarget }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.group')" name="jobGroup">
<!-- <DictTag
:options="dict.sysJobGroup"
:value="modalState.from.jobGroup"
/> -->
{{ modalState.from.jobGroup }}
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.cron')" name="cronExpression">
<a-tag color="default">
{{ modalState.from.cronExpression }}
</a-tag>
</a-form-item>
</a-col>
<!-- <a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.log')" name="saveLog">
<DictTag
:options="dict.sysJobSaveLog"
:value="modalState.from.saveLog"
/>
</a-form-item>
</a-col> -->
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.createTime')" name="createTime" :label-wrap="true">
{{ modalState.from.createTime }}
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('page.manage.task.targetParams')" name="targetParams" :label-col="{ span: 3 }"
:label-wrap="true">
<a-textarea v-model:value="modalState.from.targetParams" :auto-size="{ minRows: 2, maxRows: 6 }"
:disabled="true" />
</a-form-item>
<a-form-item :label="t('page.manage.task.remark')" name="remark" :label-col="{ span: 3 }"
:label-wrap="true">
<a-textarea v-model:value="modalState.from.remark" :auto-size="{ minRows: 2, maxRows: 6 }"
:disabled="true" />
</a-form-item>
</a-form>
<template #footer>
<a-button key="cancel" @click="fnModalCancel">
{{ t('common.close') }}
</a-button>
</template>
</a-modal>
<!-- 新增框或修改框 -->
<!-- <a-modal :drag="true" :width="800" :destroyOnClose="true" :keyboard="false" :mask-closable="false"
:open="modalState.openByEdit" :title="modalState.title" :confirm-loading="modalState.confirmLoading"
@ok="fnModalOk" @cancel="fnModalCancel">
<a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 6 }" :label-wrap="true" :rules="rules">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.taskName')" name="jobName">
<a-input v-model:value="modalState.from.jobName" allow-clear
:placeholder="t('page.manage.task.jobNamePlease')"></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.status')" name="status">
<a-select v-model:value="modalState.from.status" default-value="0"
:placeholder="t('common.selectPlease')" :options="dict.sysJobStatus">
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.jobGroup')" name="jobGroup">
<a-select v-model:value="modalState.from.jobGroup" default-value="DEFAULT"
:placeholder="t('common.selectPlease')" :options="dict.sysJobGroup">
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('page.manage.task.saveLog')" name="saveLog">
<a-select v-model:value="modalState.from.saveLog" default-value="0"
:placeholder="t('common.selectPlease')" :options="dict.sysJobSaveLog">
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('page.manage.task.invokeTarget')" name="invokeTarget" :label-col="{ span: 3 }"
:label-wrap="true">
<a-input v-model:value="modalState.from.invokeTarget" allow-clear
:placeholder="t('page.manage.task.invokeTargetPlease')">
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>{{ t('page.manage.task.invokeTargetTip') }}</div>
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item :label="t('page.manage.task.cronExpression')" name="cronExpression" :label-col="{ span: 3 }"
:label-wrap="true">
<a-input v-model:value="modalState.from.cronExpression" allow-clear
:placeholder="t('page.manage.task.cronExpressionPlease')">
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>
{{ t('page.manage.task.cronExpressionTip') }}<br />
{{ t('page.manage.task.cronExpressionTip1') }}
</div>
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
<template #addonAfter>
<a-button type="text" size="small" @click.prevent="fnModalCron(true)">
<template #icon>
<FieldTimeOutlined />
</template>
{{ t('page.manage.task.cronExpressionNew') }}
</a-button>
</template>
</a-input>
</a-form-item>
<a-form-item :label="t('page.manage.task.targetParams')" name="targetParams" :label-col="{ span: 3 }"
:label-wrap="true">
<a-textarea v-model:value="modalState.from.targetParams" :auto-size="{ minRows: 2, maxRows: 6 }"
:maxlength="400" :placeholder="t('page.manage.task.targetParamsPlease')" />
</a-form-item>
<a-form-item :label="t('page.manage.task.remark')" name="remark" :label-col="{ span: 3 }"
:label-wrap="true">
<a-textarea v-model:value="modalState.from.remark" :auto-size="{ minRows: 2, maxRows: 6 }"
:maxlength="400" :show-count="true" />
</a-form-item>
</a-form>
</a-modal> -->
</ACard>
</div>
</SimpleScrollbar>
</template>
<style scoped></style>

View File

@@ -0,0 +1,222 @@
<script setup lang="ts">
import { SimpleScrollbar } from '@sa/materials';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { $t } from '@/locales';
import { enableStatusOptions } from '@/constants/business';
defineOptions({
name: 'logOperateDrawer'
});
interface Props {
/** the type of operation */
operateType: any;
/** the edit row data */
rowData?:any;
deptTreeData:any;
}
const props = defineProps<Props>();
interface Emits {
(e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const { formRef, validate, resetFields } = useAntdForm();
const { defaultRequiredRule, formRules } = useFormRules();
const [spinning, spin] = useToggle();
const title = computed(() => {
const titles: any = {
add: $t('page.manage.user.addUser'),
edit: $t('page.manage.user.editUser')
};
return titles[props.operateType];
});
type Model = Partial<
Pick<Api.Auth.User, 'userName' | 'nickName' | 'email' | 'phonenumber' | 'status' | 'deptId' | 'remark'> & {
postIds: number[];
roleIds: number[];
password?: string;
}
>;
const model = ref<Model>(createDefaultModel());
const userPosts = ref<Api.SystemManage.Post[]>([]);
const userRoles = ref<Api.SystemManage.Role[]>([]);
function createDefaultModel(): Model {
return {
userName: '',
nickName: '',
email: '',
phonenumber: '',
status: '0',
deptId: undefined,
remark: '',
postIds: [],
roleIds: [],
password: ''
};
}
//制定规则
const rules = {
userName: defaultRequiredRule,
nickName: defaultRequiredRule,
status: defaultRequiredRule,
deptId: defaultRequiredRule,
email: formRules.email,
phonenumber: formRules.phone,
password: formRules.pwd,
postIds: defaultRequiredRule,
roleIds: defaultRequiredRule
};
// 修改时触发
async function init(userId: number | undefined = undefined) {
spin(true);
try {
await Promise.all([getUserPostAndRole(userId)]);
spin(false);
} catch (error) {
spin(false);
}
}
async function getUserPostAndRole(userId: number | undefined) {
const { error, data } = await doGetUserPostsAndRoles(userId);
//渲染用户岗位和角色option
if (!error) {
const { postIds, posts, roleIds, roles } = data;
userPosts.value = posts;
userRoles.value = roles;
model.value.postIds = postIds;
model.value.roleIds = roleIds;
}
}
async function handleUpdateModelWhenEdit() {
if (props.operateType === 'add') {
model.value = createDefaultModel(); //新增时赋值
}
if (props.operateType === 'edit' && props.rowData) {
await init(props.rowData.userId);
model.value = Object.assign(model.value, omit(props.rowData, ['postIds', 'roleIds']));
} else {
await init();
model.value = createDefaultModel();
}
}
function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
await validate();
const { error } = await (props.operateType === 'edit' ? doPutUser : doPostUser)(model.value as Api.Auth.User);
if (!error) {
$message?.success($t(props.operateType === 'add' ? 'common.addSuccess' : 'common.updateSuccess'));
closeDrawer();
emit('submitted');
}
}
watch(
() => visible.value,
val => {
if (val) {
handleUpdateModelWhenEdit();
resetFields();
}
}
);
// console.log(props.operateType)
</script>
<template>
<ADrawer
v-model:open="visible"
:body-style="{ paddingRight: '0px', paddingTop: '0', paddingBottom: '0' }"
:title="title"
:width="460"
>
<SimpleScrollbar>
<ASpin :spinning="spinning" size="small">
<AForm ref="formRef" py-20px pr-20px layout="vertical" :model="model" :rules="rules">
<AFormItem :label="$t('page.manage.user.userName')" name="userName">
<AInput v-model:value="model.userName" :placeholder="$t('page.manage.user.form.userName')" />
</AFormItem>
<AFormItem :label="$t('page.manage.user.nickName')" name="nickName">
<AInput v-model:value="model.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
</AFormItem>
<AFormItem :label="$t('page.manage.user.email')" name="email">
<AInput v-model:value="model.email" :placeholder="$t('page.manage.user.form.email')" />
</AFormItem>
<AFormItem :label="$t('page.manage.user.phonenumber')" name="phonenumber">
<AInput v-model:value="model.phonenumber" :placeholder="$t('page.manage.user.form.phonenumber')" />
</AFormItem>
<AFormItem v-if="props.operateType === 'add'" :label="$t('page.manage.user.password')" name="password">
<AInput
v-model:value="model.password"
type="password"
:placeholder="$t('page.manage.user.form.password')"
/>
</AFormItem>
<AFormItem :label="$t('page.manage.user.post')" name="postIds">
<ASelect
v-model:value="model.postIds"
:field-names="{ label: 'postName', value: 'postId' }"
mode="multiple"
:options="userPosts"
/>
</AFormItem>
<AFormItem :label="$t('page.manage.user.role')" name="roleIds">
<ASelect
v-model:value="model.roleIds"
:field-names="{ label: 'roleName', value: 'roleId' }"
mode="multiple"
:options="userRoles"
/>
</AFormItem>
<AFormItem :label="$t('page.manage.user.status')" name="status">
<ARadioGroup v-model:value="model.status">
<ARadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value">
{{ $t(item.label) }}
</ARadio>
</ARadioGroup>
</AFormItem>
<AFormItem :label="$t('page.manage.user.dept')" name="deptId">
<ATreeSelect v-model:value="model.deptId" :field-names="{ value: 'id' }" :tree-data="deptTreeData" />
</AFormItem>
<AFormItem :label="$t('page.manage.user.remark')" name="remark">
<ATextarea v-model:value="model.remark" :placeholder="$t('page.manage.user.form.remark')" />
</AFormItem>
</AForm>
</ASpin>
</SimpleScrollbar>
<template #footer>
<div class="flex-y-center justify-end gap-12px">
<AButton @click="closeDrawer">{{ $t('common.cancel') }}</AButton>
<AButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</AButton>
</div>
</template>
</ADrawer>
</template>
<style scoped></style>

View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
import { $t } from '@/locales';
import { enableStatusOptions } from '@/constants/business';
import { SyncOutlined, SearchOutlined } from '@ant-design/icons-vue';
defineOptions({
name: 'logSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const model = defineModel<any>('model', { required: true });
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
function reset() {
// 重置表单
//formRef.value?.resetFields();
// 重置日期选择器
queryRangePicker.value = ['', ''];
// 确保 model 也被重置
model.value = {};
// 使用 nextTick 确保视图更新后检查
nextTick(() => {
console.log('视图更新后:', { ...model.value });
console.log(model);
});
emit('reset');
}
function search() {
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
model.value.beginTime = queryRangePicker.value[0];
model.value.endTime = queryRangePicker.value[1];
emit('search');
}
</script>
<template>
<ACard :title="$t('common.search')" :bordered="false" class="card-wrapper">
<AForm :model="model" :label-width="80">
<ARow :gutter="[16, 16]" wrap>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem label="Name" name="operIp" class="m-0">
<AInput v-model:value="model.operIp" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem label="Group" name="title" class="m-0">
<AInput v-model:value="model.title" />
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem :label="$t('page.manage.user.status')" name="status" class="m-0">
<ASelect v-model:value="model.status" :placeholder="$t('page.manage.user.form.status')" allow-clear>
<ASelectOption v-for="option in enableStatusOptions" :key="option.value" :value="option.value">
{{ $t(option.label) }}
</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="6" :md="12" :xs="24">
<AFormItem class="m-0">
<div class="w-full flex-y-center justify-end gap-8px">
<a-space :size="8">
<a-button @click="reset">
<template #icon>
<SyncOutlined />
</template>
{{ $t('common.reset') }}
</a-button>
<AButton type="primary" ghost @click="search">
<template #icon>
<SearchOutlined />
</template>
{{ $t('common.search') }}
</AButton>
</a-space>
</div>
</AFormItem>
</ACol>
</ARow>
</AForm>
</ACard>
</template>
<style scoped></style>