refactor:基于 lint 处理排版
This commit is contained in:
@@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
||||
|
||||
import {DICT_TYPE, getDictOptions} from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
@@ -70,7 +70,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraApiAccessLogApi.SystemApiAccessLog>(
|
||||
export function useGridColumns<T = InfraApiAccessLogApi.ApiAccessLog>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
@@ -125,9 +125,7 @@ export function useGridColumns<T = InfraApiAccessLogApi.SystemApiAccessLog>(
|
||||
title: '操作结果',
|
||||
minWidth: 150,
|
||||
formatter: ({ row }) => {
|
||||
return row.resultCode === 0
|
||||
? '成功'
|
||||
: `失败(${row.resultMsg})`;
|
||||
return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Download } from '@vben/icons';
|
||||
import Detail from './modules/detail.vue';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { exportApiAccessLog, getApiAccessLogPage } from '#/api/infra/api-access-log';
|
||||
import {
|
||||
exportApiAccessLog,
|
||||
getApiAccessLogPage,
|
||||
} from '#/api/infra/api-access-log';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
|
||||
const [DetailModal, detailModalApi] = useVbenModal({
|
||||
connectedComponent: Detail,
|
||||
@@ -32,7 +39,7 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 查看 API 访问日志详情 */
|
||||
function onDetail(row: InfraApiAccessLogApi.SystemApiAccessLog) {
|
||||
function onDetail(row: InfraApiAccessLogApi.ApiAccessLog) {
|
||||
detailModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
@@ -40,7 +47,7 @@ function onDetail(row: InfraApiAccessLogApi.SystemApiAccessLog) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraApiAccessLogApi.SystemApiAccessLog>) {
|
||||
}: OnActionClickParams<InfraApiAccessLogApi.ApiAccessLog>) {
|
||||
switch (code) {
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
@@ -75,7 +82,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraApiAccessLogApi.SystemApiAccessLog>,
|
||||
} as VxeTableGridOptions<InfraApiAccessLogApi.ApiAccessLog>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -86,11 +93,16 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<DetailModal @success="onRefresh" />
|
||||
<Grid table-title="API 访问日志列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" class="ml-2" @click="onExport" v-access:code="['infra:api-access-log:export']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:api-access-log:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const formData = ref<InfraApiAccessLogApi.SystemApiAccessLog>();
|
||||
const formData = ref<InfraApiAccessLogApi.ApiAccessLog>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -17,7 +19,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraApiAccessLogApi.SystemApiAccessLog>();
|
||||
const data = modalApi.getData<InfraApiAccessLogApi.ApiAccessLog>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
@@ -32,8 +34,19 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="API 访问日志详情" class="w-1/2" :show-cancel-button="false" :show-confirm-button="false">
|
||||
<Descriptions bordered :column="1" size="middle" class="mx-4" :label-style="{ width: '110px' }">
|
||||
<Modal
|
||||
title="API 访问日志详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions
|
||||
bordered
|
||||
:column="1"
|
||||
size="middle"
|
||||
class="mx-4"
|
||||
:label-style="{ width: '110px' }"
|
||||
>
|
||||
<Descriptions.Item label="日志编号">
|
||||
{{ formData?.id }}
|
||||
</Descriptions.Item>
|
||||
@@ -63,11 +76,15 @@ const [Modal, modalApi] = useVbenModal({
|
||||
{{ formData?.responseBody }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="请求时间">
|
||||
{{ formatDateTime(formData?.beginTime || '') }} ~ {{ formatDateTime(formData?.endTime || '') }}
|
||||
{{ formatDateTime(formData?.beginTime || '') }} ~
|
||||
{{ formatDateTime(formData?.endTime || '') }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="请求耗时">
|
||||
{{ formData?.duration }} ms
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="请求耗时">{{ formData?.duration }} ms</Descriptions.Item>
|
||||
<Descriptions.Item label="操作结果">
|
||||
<div v-if="formData?.resultCode === 0">正常</div>
|
||||
<!-- TODO @芋艿:处理爆红 -->
|
||||
<div v-else-if="formData?.resultCode > 0">
|
||||
失败 | {{ formData?.resultCode }} | {{ formData?.resultMsg }}
|
||||
</div>
|
||||
@@ -79,7 +96,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
{{ formData?.operateName }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="操作类型">
|
||||
<DictTag :type="DICT_TYPE.INFRA_OPERATE_TYPE" :value="formData?.operateType" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.INFRA_OPERATE_TYPE"
|
||||
:value="formData?.operateType"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Modal>
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
||||
|
||||
import {DICT_TYPE, getDictOptions} from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { useAccess } from '@vben/access';
|
||||
import { InfraApiErrorLogProcessStatusEnum } from '#/utils/constants';
|
||||
@@ -54,7 +54,10 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
label: '处理状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, 'number'),
|
||||
options: getDictOptions(
|
||||
DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS,
|
||||
'number',
|
||||
),
|
||||
allowClear: true,
|
||||
placeholder: '请选择处理状态',
|
||||
},
|
||||
@@ -64,7 +67,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraApiErrorLogApi.SystemApiErrorLog>(
|
||||
export function useGridColumns<T = InfraApiErrorLogApi.ApiErrorLog>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
@@ -144,17 +147,21 @@ export function useGridColumns<T = InfraApiErrorLogApi.SystemApiErrorLog>(
|
||||
{
|
||||
code: 'done',
|
||||
text: '已处理',
|
||||
show: (row: InfraApiErrorLogApi.SystemApiErrorLog) => {
|
||||
return row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
||||
hasAccessByCodes(['infra:api-error-log:update-status']);
|
||||
show: (row: InfraApiErrorLogApi.ApiErrorLog) => {
|
||||
return (
|
||||
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
||||
hasAccessByCodes(['infra:api-error-log:update-status'])
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'ignore',
|
||||
text: '已忽略',
|
||||
show: (row: InfraApiErrorLogApi.SystemApiErrorLog) => {
|
||||
return row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
||||
hasAccessByCodes(['infra:api-error-log:update-status']);
|
||||
show: (row: InfraApiErrorLogApi.ApiErrorLog) => {
|
||||
return (
|
||||
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
||||
hasAccessByCodes(['infra:api-error-log:update-status'])
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
import { Download } from '@vben/icons';
|
||||
import Detail from './modules/detail.vue';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { exportApiErrorLog, getApiErrorLogPage, updateApiErrorLogStatus } from '#/api/infra/api-error-log';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import {
|
||||
exportApiErrorLog,
|
||||
getApiErrorLogPage,
|
||||
updateApiErrorLogStatus,
|
||||
} from '#/api/infra/api-error-log';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { $t } from '#/locales';
|
||||
import { InfraApiErrorLogProcessStatusEnum } from '#/utils/constants';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
|
||||
const [DetailModal, detailModalApi] = useVbenModal({
|
||||
connectedComponent: Detail,
|
||||
@@ -33,7 +41,7 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 查看 API 错误日志详情 */
|
||||
function onDetail(row: InfraApiErrorLogApi.SystemApiErrorLog) {
|
||||
function onDetail(row: InfraApiErrorLogApi.ApiErrorLog) {
|
||||
detailModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
@@ -50,7 +58,7 @@ async function onProcess(id: number, processStatus: number) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,7 +66,7 @@ async function onProcess(id: number, processStatus: number) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraApiErrorLogApi.SystemApiErrorLog>) {
|
||||
}: OnActionClickParams<InfraApiErrorLogApi.ApiErrorLog>) {
|
||||
switch (code) {
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
@@ -101,7 +109,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraApiErrorLogApi.SystemApiErrorLog>,
|
||||
} as VxeTableGridOptions<InfraApiErrorLogApi.ApiErrorLog>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -112,11 +120,16 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<DetailModal @success="onRefresh" />
|
||||
<Grid table-title="API 错误日志列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" class="ml-2" @click="onExport" v-access:code="['infra:api-error-log:export']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:api-error-log:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Descriptions, Input } from 'ant-design-vue';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, Input } from 'ant-design-vue';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const formData = ref<InfraApiErrorLogApi.SystemApiErrorLog>();
|
||||
const formData = ref<InfraApiErrorLogApi.ApiErrorLog>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -17,7 +19,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraApiErrorLogApi.SystemApiErrorLog>();
|
||||
const data = modalApi.getData<InfraApiErrorLogApi.ApiErrorLog>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
@@ -32,8 +34,19 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="API错误日志详情" class="w-1/2" :show-cancel-button="false" :show-confirm-button="false">
|
||||
<Descriptions bordered :column="1" size="middle" class="mx-4" :label-style="{ width: '110px' }">
|
||||
<Modal
|
||||
title="API错误日志详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions
|
||||
bordered
|
||||
:column="1"
|
||||
size="middle"
|
||||
class="mx-4"
|
||||
:label-style="{ width: '110px' }"
|
||||
>
|
||||
<Descriptions.Item label="日志编号">
|
||||
{{ formData?.id }}
|
||||
</Descriptions.Item>
|
||||
@@ -68,7 +81,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<Descriptions.Item v-if="formData?.exceptionStackTrace" label="异常堆栈">
|
||||
<Input.TextArea
|
||||
:value="formData?.exceptionStackTrace"
|
||||
:autoSize="{ maxRows: 20 }"
|
||||
:auto-size="{ maxRows: 20 }"
|
||||
readonly
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
|
||||
@@ -129,7 +129,10 @@ export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] {
|
||||
fieldName: 'templateType',
|
||||
label: '生成模板',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, 'number'),
|
||||
options: getDictOptions(
|
||||
DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE,
|
||||
'number',
|
||||
),
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
@@ -166,7 +169,7 @@ export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] {
|
||||
data.unshift({
|
||||
id: 0,
|
||||
name: '顶级菜单',
|
||||
} as SystemMenuApi.SystemMenu);
|
||||
} as SystemMenuApi.Menu);
|
||||
return handleTree(data);
|
||||
},
|
||||
class: 'w-full',
|
||||
@@ -232,7 +235,9 @@ export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 树表信息 schema */
|
||||
export function useGenerationInfoTreeFormSchema(columns: InfraCodegenApi.CodegenColumn[] = []): VbenFormSchema[] {
|
||||
export function useGenerationInfoTreeFormSchema(
|
||||
columns: InfraCodegenApi.CodegenColumn[] = [],
|
||||
): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Divider',
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import BasicInfo from '../modules/basic-info.vue';
|
||||
import ColumnInfo from '../modules/column-info.vue';
|
||||
import GenerationInfo from '../modules/generation-info.vue';
|
||||
import { ref, unref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useTabs } from '@vben/hooks';
|
||||
|
||||
import { Button, message, Steps } from 'ant-design-vue';
|
||||
|
||||
import { getCodegenTable, updateCodegenTable } from '#/api/infra/codegen';
|
||||
import { $t } from '#/locales';
|
||||
import { ref, unref } from 'vue';
|
||||
|
||||
import { useTabs } from '@vben/hooks';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import BasicInfo from '../modules/basic-info.vue';
|
||||
import ColumnInfo from '../modules/column-info.vue';
|
||||
import GenerationInfo from '../modules/generation-info.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -67,7 +69,10 @@ const submitForm = async () => {
|
||||
const basicInfo = await basicInfoRef.value?.getValues();
|
||||
const columns = columnInfoRef.value?.getData() || unref(formData).columns;
|
||||
const generateInfo = await generateInfoRef.value?.getValues();
|
||||
await updateCodegenTable({ table: { ...unref(formData).table, ...basicInfo, ...generateInfo }, columns });
|
||||
await updateCodegenTable({
|
||||
table: { ...unref(formData).table, ...basicInfo, ...generateInfo },
|
||||
columns,
|
||||
});
|
||||
// 关闭并提示
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.operationSuccess'),
|
||||
@@ -118,15 +123,33 @@ getDetail();
|
||||
|
||||
<template>
|
||||
<Page auto-content-height v-loading="loading">
|
||||
<div class="flex h-[95%] flex-col rounded-md bg-white p-4 dark:bg-[#1f1f1f] dark:text-gray-300">
|
||||
<Steps type="navigation" v-model:current="currentStep" class="mb-8 rounded shadow-sm dark:bg-[#141414]">
|
||||
<Steps.Step v-for="(step, index) in steps" :key="index" :title="step.title" />
|
||||
<div
|
||||
class="flex h-[95%] flex-col rounded-md bg-white p-4 dark:bg-[#1f1f1f] dark:text-gray-300"
|
||||
>
|
||||
<Steps
|
||||
type="navigation"
|
||||
v-model:current="currentStep"
|
||||
class="mb-8 rounded shadow-sm dark:bg-[#141414]"
|
||||
>
|
||||
<Steps.Step
|
||||
v-for="(step, index) in steps"
|
||||
:key="index"
|
||||
:title="step.title"
|
||||
/>
|
||||
</Steps>
|
||||
|
||||
<div class="flex-1 overflow-auto py-4">
|
||||
<!-- 根据当前步骤显示对应的组件 -->
|
||||
<BasicInfo v-show="currentStep === 0" ref="basicInfoRef" :table="formData.table" />
|
||||
<ColumnInfo v-show="currentStep === 1" ref="columnInfoRef" :columns="formData.columns" />
|
||||
<BasicInfo
|
||||
v-show="currentStep === 0"
|
||||
ref="basicInfoRef"
|
||||
:table="formData.table"
|
||||
/>
|
||||
<ColumnInfo
|
||||
v-show="currentStep === 1"
|
||||
ref="columnInfoRef"
|
||||
:columns="formData.columns"
|
||||
/>
|
||||
<GenerationInfo
|
||||
v-show="currentStep === 2"
|
||||
ref="generateInfoRef"
|
||||
@@ -137,8 +160,12 @@ getDetail();
|
||||
|
||||
<div class="mt-4 flex justify-end space-x-2">
|
||||
<Button v-show="currentStep > 0" @click="prevStep">上一步</Button>
|
||||
<Button v-show="currentStep < steps.length - 1" @click="nextStep">下一步</Button>
|
||||
<Button type="primary" :loading="loading" @click="submitForm"> 保存 </Button>
|
||||
<Button v-show="currentStep < steps.length - 1" @click="nextStep">
|
||||
下一步
|
||||
</Button>
|
||||
<Button type="primary" :loading="loading" @click="submitForm">
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||
|
||||
@@ -11,7 +14,12 @@ import { Plus } from '@vben/icons';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteCodegenTable, downloadCodegen, getCodegenTablePage, syncCodegenFromDB } from '#/api/infra/codegen';
|
||||
import {
|
||||
deleteCodegenTable,
|
||||
downloadCodegen,
|
||||
getCodegenTablePage,
|
||||
syncCodegenFromDB,
|
||||
} from '#/api/infra/codegen';
|
||||
import { getDataSourceConfigList } from '#/api/infra/data-source-config';
|
||||
import { $t } from '#/locales';
|
||||
import { ref } from 'vue';
|
||||
@@ -21,11 +29,15 @@ import { useGridColumns, useGridFormSchema } from './data';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const dataSourceConfigList = ref<InfraDataSourceConfigApi.InfraDataSourceConfig[]>([]);
|
||||
const dataSourceConfigList = ref<InfraDataSourceConfigApi.DataSourceConfig[]>(
|
||||
[],
|
||||
);
|
||||
|
||||
/** 获取数据源名称 */
|
||||
const getDataSourceConfigName = (dataSourceConfigId: number) => {
|
||||
return dataSourceConfigList.value.find((item) => item.id === dataSourceConfigId)?.name;
|
||||
return dataSourceConfigList.value.find(
|
||||
(item) => item.id === dataSourceConfigId,
|
||||
)?.name;
|
||||
};
|
||||
|
||||
const [ImportModal, importModalApi] = useVbenModal({
|
||||
@@ -122,7 +134,10 @@ async function onGenerate(row: InfraCodegenApi.CodegenTable) {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<InfraCodegenApi.CodegenTable>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraCodegenApi.CodegenTable>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
@@ -190,16 +205,29 @@ initDataSourceConfig();
|
||||
</script>
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<DocAlert title="代码生成(单表)" url="https://doc.iocoder.cn/new-feature/" />
|
||||
<DocAlert title="代码生成(树表)" url="https://doc.iocoder.cn/new-feature/tree/" />
|
||||
<DocAlert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||
<DocAlert
|
||||
title="代码生成(单表)"
|
||||
url="https://doc.iocoder.cn/new-feature/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(树表)"
|
||||
url="https://doc.iocoder.cn/new-feature/tree/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(主子表)"
|
||||
url="https://doc.iocoder.cn/new-feature/master-sub/"
|
||||
/>
|
||||
<DocAlert title="单元测试" url="https://doc.iocoder.cn/unit-test/" />
|
||||
|
||||
<ImportModal @success="onRefresh" />
|
||||
<PreviewModal />
|
||||
<Grid table-title="代码生成列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onImport" v-access:code="['infra:codegen:create']">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onImport"
|
||||
v-access:code="['infra:codegen:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
导入
|
||||
</Button>
|
||||
|
||||
@@ -55,7 +55,7 @@ defineExpose({
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
const dictTypeOptions = ref<SystemDictTypeApi.SystemDictType[]>([]); // 字典类型选项
|
||||
const dictTypeOptions = ref<SystemDictTypeApi.DictType[]>([]); // 字典类型选项
|
||||
onMounted(async () => {
|
||||
dictTypeOptions.value = await getSimpleDictTypeList();
|
||||
});
|
||||
@@ -71,7 +71,11 @@ onMounted(async () => {
|
||||
<!-- Java 类型 -->
|
||||
<template #javaType="{ row, column }">
|
||||
<Select v-model:value="row.javaType" style="width: 100%">
|
||||
<Select.Option v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
<Select.Option
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
@@ -101,7 +105,11 @@ onMounted(async () => {
|
||||
<!-- 查询方式 -->
|
||||
<template #listOperationCondition="{ row, column }">
|
||||
<Select v-model:value="row.listOperationCondition" class="w-full">
|
||||
<Select.Option v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
<Select.Option
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
@@ -115,7 +123,11 @@ onMounted(async () => {
|
||||
<!-- 显示类型 -->
|
||||
<template #htmlType="{ row, column }">
|
||||
<Select v-model:value="row.htmlType" class="w-full">
|
||||
<Select.Option v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
<Select.Option
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
@@ -123,8 +135,17 @@ onMounted(async () => {
|
||||
|
||||
<!-- 字典类型 -->
|
||||
<template #dictType="{ row }">
|
||||
<Select v-model:value="row.dictType" class="w-full" allow-clear show-search>
|
||||
<Select.Option v-for="option in dictTypeOptions" :key="option.type" :value="option.type">
|
||||
<Select
|
||||
v-model:value="row.dictType"
|
||||
class="w-full"
|
||||
allow-clear
|
||||
show-search
|
||||
>
|
||||
<Select.Option
|
||||
v-for="option in dictTypeOptions"
|
||||
:key="option.type"
|
||||
:value="option.type"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getCodegenTableList } from '#/api/infra/codegen';
|
||||
import { InfraCodegenTemplateTypeEnum } from '#/utils/constants';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import {
|
||||
useGenerationInfoBaseFormSchema,
|
||||
@@ -23,8 +24,12 @@ const tables = ref<InfraCodegenApi.CodegenTable[]>([]);
|
||||
|
||||
/** 计算当前模板类型 */
|
||||
const currentTemplateType = ref<number>();
|
||||
const isTreeTable = computed(() => currentTemplateType.value === InfraCodegenTemplateTypeEnum.TREE);
|
||||
const isSubTable = computed(() => currentTemplateType.value === InfraCodegenTemplateTypeEnum.SUB);
|
||||
const isTreeTable = computed(
|
||||
() => currentTemplateType.value === InfraCodegenTemplateTypeEnum.TREE,
|
||||
);
|
||||
const isSubTable = computed(
|
||||
() => currentTemplateType.value === InfraCodegenTemplateTypeEnum.SUB,
|
||||
);
|
||||
|
||||
/** 基础表单实例 */
|
||||
const [BaseForm, baseFormApi] = useVbenForm({
|
||||
@@ -34,7 +39,10 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
schema: useGenerationInfoBaseFormSchema(),
|
||||
handleValuesChange: (values) => {
|
||||
// 监听模板类型变化
|
||||
if (values.templateType !== undefined && values.templateType !== currentTemplateType.value) {
|
||||
if (
|
||||
values.templateType !== undefined &&
|
||||
values.templateType !== currentTemplateType.value
|
||||
) {
|
||||
currentTemplateType.value = values.templateType;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { createCodegenList, getSchemaTableList } from '#/api/infra/codegen';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { useImportTableColumns, useImportTableFormSchema } from '#/views/infra/codegen/data';
|
||||
import {
|
||||
useImportTableColumns,
|
||||
useImportTableFormSchema,
|
||||
} from '#/views/infra/codegen/data';
|
||||
|
||||
/** 定义组件事件 */
|
||||
const emit = defineEmits<{
|
||||
@@ -63,7 +66,11 @@ const [Grid] = useVbenVxeGrid({
|
||||
},
|
||||
} as VxeTableGridOptions<InfraCodegenApi.DatabaseTable>,
|
||||
gridEvents: {
|
||||
checkboxChange: ({ records }: { records: InfraCodegenApi.DatabaseTable[] }) => {
|
||||
checkboxChange: ({
|
||||
records,
|
||||
}: {
|
||||
records: InfraCodegenApi.DatabaseTable[];
|
||||
}) => {
|
||||
formData.tableNames = records.map((item) => item.name);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
// TODO @芋艿:待定,vben2.0 有 CodeEditor,不确定官方后续会不会迁移!!!
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Copy } from '@vben/icons';
|
||||
import { Button, DirectoryTree, message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { previewCodegen } from '#/api/infra/codegen';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Copy } from '@vben/icons';
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { Button, DirectoryTree, message, Tabs } from 'ant-design-vue';
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import java from 'highlight.js/lib/languages/java';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
@@ -17,6 +16,8 @@ import sql from 'highlight.js/lib/languages/sql';
|
||||
import typescript from 'highlight.js/lib/languages/typescript';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
|
||||
import { previewCodegen } from '#/api/infra/codegen';
|
||||
|
||||
/** 注册代码高亮语言 */
|
||||
hljs.registerLanguage('java', java);
|
||||
hljs.registerLanguage('xml', xml);
|
||||
@@ -72,7 +73,9 @@ const removeCodeMapKey = (targetKey: any) => {
|
||||
/** 复制代码 */
|
||||
const copyCode = async () => {
|
||||
const { copy } = useClipboard();
|
||||
const file = previewFiles.value.find((item) => item.filePath === activeKey.value);
|
||||
const file = previewFiles.value.find(
|
||||
(item) => item.filePath === activeKey.value,
|
||||
);
|
||||
if (file) {
|
||||
await copy(file.code);
|
||||
message.success('复制成功');
|
||||
@@ -123,7 +126,18 @@ const handleFiles = (data: InfraCodegenApi.CodegenPreview[]): FileNode[] => {
|
||||
let packagePath = '';
|
||||
while (cursor < paths.length) {
|
||||
const nextPath = paths[cursor] || '';
|
||||
if (['controller', 'convert', 'dal', 'dataobject', 'enums', 'mysql', 'service', 'vo'].includes(nextPath)) {
|
||||
if (
|
||||
[
|
||||
'controller',
|
||||
'convert',
|
||||
'dal',
|
||||
'dataobject',
|
||||
'enums',
|
||||
'mysql',
|
||||
'service',
|
||||
'vo',
|
||||
].includes(nextPath)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
packagePath = packagePath ? `${packagePath}.${nextPath}` : nextPath;
|
||||
@@ -213,7 +227,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<Modal title="代码预览">
|
||||
<div class="flex h-full" v-loading="loading">
|
||||
<!-- 文件树 -->
|
||||
<div class="h-full w-1/3 overflow-auto border-r border-gray-200 pr-4 dark:border-gray-700">
|
||||
<div
|
||||
class="h-full w-1/3 overflow-auto border-r border-gray-200 pr-4 dark:border-gray-700"
|
||||
>
|
||||
<DirectoryTree
|
||||
v-if="fileTree.length > 0"
|
||||
default-expand-all
|
||||
@@ -224,15 +240,31 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</div>
|
||||
<!-- 代码预览 -->
|
||||
<div class="h-full w-2/3 overflow-auto pl-4">
|
||||
<Tabs v-model:active-key="activeKey" hide-add type="editable-card" @edit="removeCodeMapKey">
|
||||
<Tabs.TabPane v-for="key in codeMap.keys()" :key="key" :tab="key.split('/').pop()">
|
||||
<div class="h-full rounded-md bg-gray-50 !p-0 text-gray-800 dark:bg-gray-800 dark:text-gray-200">
|
||||
<Tabs
|
||||
v-model:active-key="activeKey"
|
||||
hide-add
|
||||
type="editable-card"
|
||||
@edit="removeCodeMapKey"
|
||||
>
|
||||
<Tabs.TabPane
|
||||
v-for="key in codeMap.keys()"
|
||||
:key="key"
|
||||
:tab="key.split('/').pop()"
|
||||
>
|
||||
<div
|
||||
class="h-full rounded-md bg-gray-50 !p-0 text-gray-800 dark:bg-gray-800 dark:text-gray-200"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<code v-html="codeMap.get(activeKey)" class="code-highlight"></code>
|
||||
<code
|
||||
v-html="codeMap.get(activeKey)"
|
||||
class="code-highlight"
|
||||
></code>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
<template #rightExtra>
|
||||
<Button type="primary" ghost @click="copyCode" :icon="h(Copy)"> 复制代码 </Button>
|
||||
<Button type="primary" ghost @click="copyCode" :icon="h(Copy)">
|
||||
复制代码
|
||||
</Button>
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,11 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraConfigApi } from '#/api/infra/config';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
@@ -73,7 +74,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -88,7 +89,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入参数名称',
|
||||
clearable: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'key',
|
||||
@@ -97,7 +98,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入参数键名',
|
||||
clearable: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
@@ -122,7 +123,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraConfigApi.InfraConfig>(
|
||||
export function useGridColumns<T = InfraConfigApi.Config>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraConfigApi } from '#/api/infra/config';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { Plus, Download } from '@vben/icons';
|
||||
import Form from './modules/form.vue';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getConfigPage, deleteConfig, exportConfig } from '#/api/infra/config';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import { deleteConfig, exportConfig, getConfigPage } from '#/api/infra/config';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
@@ -35,12 +40,12 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑参数 */
|
||||
function onEdit(row: InfraConfigApi.InfraConfig) {
|
||||
function onEdit(row: InfraConfigApi.Config) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除参数 */
|
||||
async function onDelete(row: InfraConfigApi.InfraConfig) {
|
||||
async function onDelete(row: InfraConfigApi.Config) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
@@ -53,7 +58,7 @@ async function onDelete(row: InfraConfigApi.InfraConfig) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
@@ -62,22 +67,22 @@ async function onDelete(row: InfraConfigApi.InfraConfig) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraConfigApi.InfraConfig>) {
|
||||
}: OnActionClickParams<InfraConfigApi.Config>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema()
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
@@ -101,7 +106,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraConfigApi.InfraConfig>,
|
||||
} as VxeTableGridOptions<InfraConfigApi.Config>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -110,11 +115,20 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="参数列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onCreate" v-access:code="['infra:config:create']">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:config:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', ['参数']) }}
|
||||
</Button>
|
||||
<Button type="primary" class="ml-2" @click="onExport" v-access:code="['infra:config:export']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:config:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraConfigApi } from '#/api/infra/config';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createConfig, updateConfig, getConfig } from '#/api/infra/config';
|
||||
import { createConfig, getConfig, updateConfig } from '#/api/infra/config';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<InfraConfigApi.InfraConfig>();
|
||||
const formData = ref<InfraConfigApi.Config>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['参数'])
|
||||
@@ -33,7 +35,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as InfraConfigApi.InfraConfig;
|
||||
const data = (await formApi.getValues()) as InfraConfigApi.Config;
|
||||
try {
|
||||
await (formData.value?.id ? updateConfig(data) : createConfig(data));
|
||||
// 关闭并提示
|
||||
@@ -52,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraConfigApi.InfraConfig>();
|
||||
const data = modalApi.getData<InfraConfigApi.Config>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraDataSourceConfigApi.InfraDataSourceConfig>(
|
||||
export function useGridColumns<T = InfraDataSourceConfigApi.DataSourceConfig>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { Plus, } from '@vben/icons';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDataSourceConfigList, deleteDataSourceConfig } from '#/api/infra/data-source-config';
|
||||
import { useGridColumns } from './data';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteDataSourceConfig,
|
||||
getDataSourceConfigList,
|
||||
} from '#/api/infra/data-source-config';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
@@ -24,12 +33,12 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑数据源 */
|
||||
function onEdit(row: InfraDataSourceConfigApi.InfraDataSourceConfig) {
|
||||
function onEdit(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除数据源 */
|
||||
async function onDelete(row: InfraDataSourceConfigApi.InfraDataSourceConfig) {
|
||||
async function onDelete(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
@@ -42,7 +51,7 @@ async function onDelete(row: InfraDataSourceConfigApi.InfraDataSourceConfig) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
await handleLoadData();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
@@ -51,16 +60,16 @@ async function onDelete(row: InfraDataSourceConfigApi.InfraDataSourceConfig) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraDataSourceConfigApi.InfraDataSourceConfig>) {
|
||||
}: OnActionClickParams<InfraDataSourceConfigApi.DataSourceConfig>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +86,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: getDataSourceConfigList
|
||||
}
|
||||
query: getDataSourceConfigList,
|
||||
},
|
||||
},
|
||||
} as VxeTableGridOptions<InfraDataSourceConfigApi.InfraDataSourceConfig>,
|
||||
} as VxeTableGridOptions<InfraDataSourceConfigApi.DataSourceConfig>,
|
||||
});
|
||||
|
||||
/** 加载数据 */
|
||||
@@ -104,11 +113,15 @@ onMounted(() => {
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="数据源列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onCreate" v-access:code="['infra:data-source-config:create']">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:data-source-config:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', ['数据源']) }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDataSourceConfig, updateDataSourceConfig, getDataSourceConfig } from '#/api/infra/data-source-config';
|
||||
import {
|
||||
createDataSourceConfig,
|
||||
getDataSourceConfig,
|
||||
updateDataSourceConfig,
|
||||
} from '#/api/infra/data-source-config';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<InfraDataSourceConfigApi.InfraDataSourceConfig>();
|
||||
const formData = ref<InfraDataSourceConfigApi.DataSourceConfig>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['数据源'])
|
||||
@@ -33,9 +39,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as InfraDataSourceConfigApi.InfraDataSourceConfig;
|
||||
const data =
|
||||
(await formApi.getValues()) as InfraDataSourceConfigApi.DataSourceConfig;
|
||||
try {
|
||||
await (formData.value?.id ? updateDataSourceConfig(data) : createDataSourceConfig(data));
|
||||
await (formData.value?.id
|
||||
? updateDataSourceConfig(data)
|
||||
: createDataSourceConfig(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -52,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraDataSourceConfigApi.InfraDataSourceConfig>();
|
||||
const data = modalApi.getData<InfraDataSourceConfigApi.DataSourceConfig>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||
|
||||
import Form from './modules/form.vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo01Contact, exportDemo01Contact, getDemo01ContactPage } from '#/api/infra/demo/demo01';
|
||||
import {
|
||||
deleteDemo01Contact,
|
||||
exportDemo01Contact,
|
||||
getDemo01ContactPage,
|
||||
} from '#/api/infra/demo/demo01';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
@@ -61,16 +70,19 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo01ContactApi.Demo01Contact>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo01ContactApi.Demo01Contact>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +125,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<Grid table-title="示例联系人列表">
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo01-contact:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo01-contact:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['示例联系人']) }}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo01Contact, getDemo01Contact, updateDemo01Contact } from '#/api/infra/demo/demo01';
|
||||
import {
|
||||
createDemo01Contact,
|
||||
getDemo01Contact,
|
||||
updateDemo01Contact,
|
||||
} from '#/api/infra/demo/demo01';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo01ContactApi.Demo01Contact>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['示例联系人']) : $t('ui.actionTitle.create', ['示例联系人']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['示例联系人'])
|
||||
: $t('ui.actionTitle.create', ['示例联系人']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
@@ -33,7 +41,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo01ContactApi.Demo01Contact;
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo01Contact(data) : createDemo01Contact(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo01Contact(data)
|
||||
: createDemo01Contact(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getDemo02CategoryList } from '#/api/infra/demo/demo02';
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { handleTree } from '#/utils/tree';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||
|
||||
import Form from './modules/form.vue';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo02Category, exportDemo02Category, getDemo02CategoryList } from '#/api/infra/demo/demo02';
|
||||
import {
|
||||
deleteDemo02Category,
|
||||
exportDemo02Category,
|
||||
getDemo02CategoryList,
|
||||
} from '#/api/infra/demo/demo02';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
@@ -73,7 +82,10 @@ async function onDelete(row: Demo02CategoryApi.Demo02Category) {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo02CategoryApi.Demo02Category>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo02CategoryApi.Demo02Category>) {
|
||||
switch (code) {
|
||||
case 'append': {
|
||||
onAppend(row);
|
||||
@@ -135,7 +147,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<Button @click="toggleExpand" class="mr-2">
|
||||
{{ isExpanded ? '收缩' : '展开' }}
|
||||
</Button>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo02-category:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo02-category:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['示例分类']) }}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo02Category, getDemo02Category, updateDemo02Category } from '#/api/infra/demo/demo02';
|
||||
import {
|
||||
createDemo02Category,
|
||||
getDemo02Category,
|
||||
updateDemo02Category,
|
||||
} from '#/api/infra/demo/demo02';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
@@ -19,7 +25,9 @@ const getTitle = computed(() => {
|
||||
if (formData.value?.id) {
|
||||
return $t('ui.actionTitle.edit', ['示例分类']);
|
||||
}
|
||||
return parentId.value ? $t('ui.actionTitle.create', ['下级示例分类']) : $t('ui.actionTitle.create', ['示例分类']);
|
||||
return parentId.value
|
||||
? $t('ui.actionTitle.create', ['下级示例分类'])
|
||||
: $t('ui.actionTitle.create', ['示例分类']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
@@ -36,9 +44,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo02CategoryApi.Demo02Category;
|
||||
const data =
|
||||
(await formApi.getValues()) as Demo02CategoryApi.Demo02Category;
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo02Category(data) : createDemo02Category(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo02Category(data)
|
||||
: createDemo02Category(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import Demo03CourseList from './modules/demo03-course-list.vue';
|
||||
import Demo03GradeList from './modules/demo03-grade-list.vue';
|
||||
import Form from './modules/form.vue';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo03Student, exportDemo03Student, getDemo03StudentPage } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
deleteDemo03Student,
|
||||
exportDemo03Student,
|
||||
getDemo03StudentPage,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Demo03CourseList from './modules/demo03-course-list.vue';
|
||||
import Demo03GradeList from './modules/demo03-grade-list.vue';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
/** 子表的列表 */
|
||||
const subTabsName = ref('demo03Course');
|
||||
@@ -67,16 +76,19 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +138,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<div>
|
||||
<Grid table-title="学生列表">
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo03Course, getDemo03Course, updateDemo03Course } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
createDemo03Course,
|
||||
getDemo03Course,
|
||||
updateDemo03Course,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useDemo03CourseFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo03StudentApi.Demo03Course>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['学生课程']) : $t('ui.actionTitle.create', ['学生课程']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['学生课程'])
|
||||
: $t('ui.actionTitle.create', ['学生课程']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
@@ -35,7 +43,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Course;
|
||||
data.studentId = formData.value?.studentId;
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo03Course(data) : createDemo03Course(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo03Course(data)
|
||||
: createDemo03Course(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo03Course, getDemo03CoursePage } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
deleteDemo03Course,
|
||||
getDemo03CoursePage,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03CourseGridColumns, useDemo03CourseGridFormSchema } from '../data';
|
||||
import {
|
||||
useDemo03CourseGridColumns,
|
||||
useDemo03CourseGridFormSchema,
|
||||
} from '../data';
|
||||
import Demo03CourseForm from './demo03-course-form.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -57,16 +68,19 @@ async function onDelete(row: Demo03StudentApi.Demo03Course) {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +143,12 @@ watch(
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="学生课程列表">
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo03Grade, getDemo03Grade, updateDemo03Grade } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
createDemo03Grade,
|
||||
getDemo03Grade,
|
||||
updateDemo03Grade,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useDemo03GradeFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo03StudentApi.Demo03Grade>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['学生班级']) : $t('ui.actionTitle.create', ['学生班级']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['学生班级'])
|
||||
: $t('ui.actionTitle.create', ['学生班级']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
@@ -35,7 +43,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Grade;
|
||||
data.studentId = formData.value?.studentId;
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo03Grade(data) : createDemo03Grade(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo03Grade(data)
|
||||
: createDemo03Grade(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo03Grade, getDemo03GradePage } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
deleteDemo03Grade,
|
||||
getDemo03GradePage,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03GradeGridColumns, useDemo03GradeGridFormSchema } from '../data';
|
||||
import {
|
||||
useDemo03GradeGridColumns,
|
||||
useDemo03GradeGridFormSchema,
|
||||
} from '../data';
|
||||
import Demo03GradeForm from './demo03-grade-form.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -57,16 +68,19 @@ async function onDelete(row: Demo03StudentApi.Demo03Grade) {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Grade>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Grade>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +143,12 @@ watch(
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="学生班级列表">
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生班级']) }}
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo03Student, getDemo03Student, updateDemo03Student } from '#/api/infra/demo/demo03/erp';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
updateDemo03Student,
|
||||
} from '#/api/infra/demo/demo03/erp';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo03StudentApi.Demo03Student>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['学生']) : $t('ui.actionTitle.create', ['学生']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['学生'])
|
||||
: $t('ui.actionTitle.create', ['学生']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
@@ -33,7 +41,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo03Student(data) : createDemo03Student(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo03Student(data)
|
||||
: createDemo03Student(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import Demo03CourseList from './modules/demo03-course-list.vue';
|
||||
import Demo03GradeList from './modules/demo03-grade-list.vue';
|
||||
import Form from './modules/form.vue';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo03Student, exportDemo03Student, getDemo03StudentPage } from '#/api/infra/demo/demo03/inner';
|
||||
import {
|
||||
deleteDemo03Student,
|
||||
exportDemo03Student,
|
||||
getDemo03StudentPage,
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Demo03CourseList from './modules/demo03-course-list.vue';
|
||||
import Demo03GradeList from './modules/demo03-grade-list.vue';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
/** 子表的列表 */
|
||||
const subTabsName = ref('demo03Course');
|
||||
@@ -66,16 +75,19 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +141,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
</Tabs>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, Input } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { $t } from '#/locales';
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03CourseGridEditColumns } from '../data';
|
||||
|
||||
@@ -17,7 +19,10 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
@@ -59,8 +64,10 @@ const onDelete = async (row: Demo03StudentApi.Demo03Course) => {
|
||||
defineExpose({
|
||||
getData: (): Demo03StudentApi.Demo03Course[] => {
|
||||
const data = gridApi.grid.getData() as Demo03StudentApi.Demo03Course[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const removeRecords =
|
||||
gridApi.grid.getRemoveRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const insertRecords =
|
||||
gridApi.grid.getInsertRecords() as Demo03StudentApi.Demo03Course[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
@@ -75,7 +82,9 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getDemo03CourseListByStudentId(props.studentId!));
|
||||
await gridApi.grid.loadData(
|
||||
await getDemo03CourseListByStudentId(props.studentId!),
|
||||
);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
@@ -91,7 +100,13 @@ watch(
|
||||
</template>
|
||||
</Grid>
|
||||
<div class="-mt-4 flex justify-center">
|
||||
<Button :icon="h(Plus)" type="primary" ghost @click="onAdd" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="onAdd"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03CourseGridColumns } from '../data';
|
||||
|
||||
@@ -31,7 +32,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
/** 刷新表格 */
|
||||
const onRefresh = async () => {
|
||||
await gridApi.grid.loadData(await getDemo03CourseListByStudentId(props.studentId!));
|
||||
await gridApi.grid.loadData(
|
||||
await getDemo03CourseListByStudentId(props.studentId!),
|
||||
);
|
||||
};
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03GradeFormSchema } from '../data';
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03GradeGridColumns } from '../data';
|
||||
|
||||
@@ -31,7 +32,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
/** 刷新表格 */
|
||||
const onRefresh = async () => {
|
||||
await gridApi.grid.loadData([await getDemo03GradeByStudentId(props.studentId!)]);
|
||||
await gridApi.grid.loadData([
|
||||
await getDemo03GradeByStudentId(props.studentId!),
|
||||
]);
|
||||
};
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo03Student, getDemo03Student, updateDemo03Student } from '#/api/infra/demo/demo03/inner';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
updateDemo03Student,
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import Demo03CourseForm from './demo03-course-form.vue';
|
||||
@@ -16,7 +22,9 @@ import Demo03GradeForm from './demo03-grade-form.vue';
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo03StudentApi.Demo03Student>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['学生']) : $t('ui.actionTitle.create', ['学生']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['学生'])
|
||||
: $t('ui.actionTitle.create', ['学生']);
|
||||
});
|
||||
|
||||
/** 子表的表单 */
|
||||
@@ -49,7 +57,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo03Student(data) : createDemo03Student(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo03Student(data)
|
||||
: createDemo03Student(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -93,7 +103,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<!-- 子表的表单 -->
|
||||
<Tabs v-model:active-key="subTabsName">
|
||||
<Tabs.TabPane key="demo03Course" tab="学生课程" force-render>
|
||||
<Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData?.id" />
|
||||
<Demo03CourseForm
|
||||
ref="demo03CourseFormRef"
|
||||
:student-id="formData?.id"
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="demo03Grade" tab="学生班级" force-render>
|
||||
<Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData?.id" />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
import Form from './modules/form.vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDemo03Student, exportDemo03Student, getDemo03StudentPage } from '#/api/infra/demo/demo03/normal';
|
||||
import {
|
||||
deleteDemo03Student,
|
||||
exportDemo03Student,
|
||||
getDemo03StudentPage,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
@@ -61,16 +70,19 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Student>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +125,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<Grid table-title="学生列表">
|
||||
<template #toolbar-tools>
|
||||
<Button :icon="h(Plus)" type="primary" @click="onCreate" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, Input } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { $t } from '#/locales';
|
||||
import { h, nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03CourseGridEditColumns } from '../data';
|
||||
|
||||
@@ -17,7 +19,10 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<Demo03StudentApi.Demo03Course>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
@@ -59,8 +64,10 @@ const onDelete = async (row: Demo03StudentApi.Demo03Course) => {
|
||||
defineExpose({
|
||||
getData: (): Demo03StudentApi.Demo03Course[] => {
|
||||
const data = gridApi.grid.getData() as Demo03StudentApi.Demo03Course[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const removeRecords =
|
||||
gridApi.grid.getRemoveRecords() as Demo03StudentApi.Demo03Course[];
|
||||
const insertRecords =
|
||||
gridApi.grid.getInsertRecords() as Demo03StudentApi.Demo03Course[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
@@ -75,7 +82,9 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getDemo03CourseListByStudentId(props.studentId!));
|
||||
await gridApi.grid.loadData(
|
||||
await getDemo03CourseListByStudentId(props.studentId!),
|
||||
);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
@@ -91,7 +100,13 @@ watch(
|
||||
</template>
|
||||
</Grid>
|
||||
<div class="-mt-4 flex justify-center">
|
||||
<Button :icon="h(Plus)" type="primary" ghost @click="onAdd" v-access:code="['infra:demo03-student:create']">
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="onAdd"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { nextTick, watch } from 'vue';
|
||||
|
||||
import { useDemo03GradeFormSchema } from '../data';
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createDemo03Student, getDemo03Student, updateDemo03Student } from '#/api/infra/demo/demo03/normal';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
updateDemo03Student,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import Demo03CourseForm from './demo03-course-form.vue';
|
||||
@@ -16,7 +22,9 @@ import Demo03GradeForm from './demo03-grade-form.vue';
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo03StudentApi.Demo03Student>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? $t('ui.actionTitle.edit', ['学生']) : $t('ui.actionTitle.create', ['学生']);
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['学生'])
|
||||
: $t('ui.actionTitle.create', ['学生']);
|
||||
});
|
||||
|
||||
/** 子表的表单 */
|
||||
@@ -49,7 +57,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
||||
try {
|
||||
await (formData.value?.id ? updateDemo03Student(data) : createDemo03Student(data));
|
||||
await (formData.value?.id
|
||||
? updateDemo03Student(data)
|
||||
: createDemo03Student(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -93,7 +103,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<!-- 子表的表单 -->
|
||||
<Tabs v-model:active-key="subTabsName">
|
||||
<Tabs.TabPane key="demo03Course" tab="学生课程" force-render>
|
||||
<Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData?.id" />
|
||||
<Demo03CourseForm
|
||||
ref="demo03CourseFormRef"
|
||||
:student-id="formData?.id"
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="demo03Grade" tab="学生班级" force-render>
|
||||
<Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData?.id" />
|
||||
|
||||
@@ -1,31 +1,35 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui'
|
||||
import { DocAlert } from '#/components/doc-alert'
|
||||
import { IFrame } from '#/components/iframe'
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getConfigKey } from '#/api/infra/config'
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
const loading = ref(true) // 是否加载中
|
||||
const src = ref(import.meta.env.VITE_BASE_URL + '/druid/index.html')
|
||||
import { getConfigKey } from '#/api/infra/config';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
const loading = ref(true); // 是否加载中
|
||||
const src = ref(`${import.meta.env.VITE_BASE_URL}/druid/index.html`);
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await getConfigKey('url.druid')
|
||||
const data = await getConfigKey('url.druid');
|
||||
if (data && data.length > 0) {
|
||||
src.value = data
|
||||
src.value = data;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<DocAlert title="数据库 MyBatis" url="https://doc.iocoder.cn/mybatis/" />
|
||||
<DocAlert title="多数据源(读写分离)" url="https://doc.iocoder.cn/dynamic-datasource/" />
|
||||
<DocAlert
|
||||
title="多数据源(读写分离)"
|
||||
url="https://doc.iocoder.cn/dynamic-datasource/"
|
||||
/>
|
||||
|
||||
<IFrame v-if="!loading" v-loading="loading" :src="src" />
|
||||
</Page>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { type VbenFormSchema } from '#/adapter/form';
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraFileApi } from '#/api/infra/file';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
@@ -18,7 +19,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择要上传的文件',
|
||||
},
|
||||
rules: 'required',
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -56,7 +57,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraFileApi.InfraFile>(
|
||||
export function useGridColumns<T = InfraFileApi.File>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
@@ -86,9 +87,9 @@ export function useGridColumns<T = InfraFileApi.InfraFile>(
|
||||
if (!cellValue) return '0 B';
|
||||
const unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const index = Math.floor(Math.log(cellValue) / Math.log(1024));
|
||||
const size = cellValue / Math.pow(1024, index);
|
||||
const size = cellValue / 1024 ** index;
|
||||
const formattedSize = size.toFixed(2);
|
||||
return formattedSize + ' ' + unitArr[index];
|
||||
return `${formattedSize} ${unitArr[index]}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraFileApi } from '#/api/infra/file';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button, message, Image } from 'ant-design-vue';
|
||||
import { Upload } from '@vben/icons';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getFilePage, deleteFile } from '#/api/infra/file';
|
||||
import { deleteFile, getFilePage } from '#/api/infra/file';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
@@ -30,7 +35,7 @@ function onUpload() {
|
||||
|
||||
/** 复制链接到剪贴板 */
|
||||
const { copy } = useClipboard({ legacy: true });
|
||||
async function onCopyUrl(row: InfraFileApi.InfraFile) {
|
||||
async function onCopyUrl(row: InfraFileApi.File) {
|
||||
if (!row.url) {
|
||||
message.error('文件 URL 为空');
|
||||
return;
|
||||
@@ -39,7 +44,7 @@ async function onCopyUrl(row: InfraFileApi.InfraFile) {
|
||||
try {
|
||||
await copy(row.url);
|
||||
message.success('复制成功');
|
||||
} catch (error) {
|
||||
} catch {
|
||||
message.error('复制失败');
|
||||
}
|
||||
}
|
||||
@@ -52,7 +57,7 @@ function openUrl(url?: string) {
|
||||
}
|
||||
|
||||
/** 删除文件 */
|
||||
async function onDelete(row: InfraFileApi.InfraFile) {
|
||||
async function onDelete(row: InfraFileApi.File) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name || row.path]),
|
||||
duration: 0,
|
||||
@@ -65,31 +70,28 @@ async function onDelete(row: InfraFileApi.InfraFile) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraFileApi.InfraFile>) {
|
||||
function onActionClick({ code, row }: OnActionClickParams<InfraFileApi.File>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'copyUrl': {
|
||||
onCopyUrl(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema()
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
@@ -113,7 +115,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraFileApi.InfraFile>,
|
||||
} as VxeTableGridOptions<InfraFileApi.File>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -129,8 +131,16 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
</template>
|
||||
<template #file-content="{ row }">
|
||||
<Image v-if="row.type && row.type.includes('image')" :src="row.url" />
|
||||
<Button v-else-if="row.type && row.type.includes('pdf')" type="link" @click="() => openUrl(row.url)"> 预览 </Button>
|
||||
<Button v-else type="link" @click="() => openUrl(row.url)"> 下载 </Button>
|
||||
<Button
|
||||
v-else-if="row.type && row.type.includes('pdf')"
|
||||
type="link"
|
||||
@click="() => openUrl(row.url)"
|
||||
>
|
||||
预览
|
||||
</Button>
|
||||
<Button v-else type="link" @click="() => openUrl(row.url)">
|
||||
下载
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import type { FileType } from 'ant-design-vue/es/upload/interface';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { Upload } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { message, Upload } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { useUpload } from '#/components/upload/use-upload';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
@@ -15,11 +15,11 @@ const emit = defineEmits(['success']);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema().map(item => ({ ...item, label: '' })), // 去除label
|
||||
schema: useFormSchema().map((item) => ({ ...item, label: '' })), // 去除label
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -43,7 +43,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** 上传前 */
|
||||
@@ -63,7 +63,7 @@ function beforeUpload(file: FileType) {
|
||||
name="file"
|
||||
:max-count="1"
|
||||
accept=".jpg,.png,.gif,.webp"
|
||||
:beforeUpload="beforeUpload"
|
||||
:before-upload="beforeUpload"
|
||||
list-type="picture-card"
|
||||
>
|
||||
<p class="ant-upload-drag-icon">
|
||||
|
||||
@@ -2,10 +2,11 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraFileConfigApi } from '#/api/infra/file-config';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils/date';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
@@ -39,7 +40,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
show: (formValues) => !formValues.id
|
||||
show: (formValues) => !formValues.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -61,7 +62,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['storage'],
|
||||
show: (formValues) => formValues.storage >= 10 && formValues.storage <= 12,
|
||||
show: (formValues) =>
|
||||
formValues.storage >= 10 && formValues.storage <= 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -74,7 +76,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['storage'],
|
||||
show: (formValues) => formValues.storage >= 11 && formValues.storage <= 12,
|
||||
show: (formValues) =>
|
||||
formValues.storage >= 11 && formValues.storage <= 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -90,7 +93,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['storage'],
|
||||
show: (formValues) => formValues.storage >= 11 && formValues.storage <= 12,
|
||||
show: (formValues) =>
|
||||
formValues.storage >= 11 && formValues.storage <= 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -103,7 +107,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['storage'],
|
||||
show: (formValues) => formValues.storage >= 11 && formValues.storage <= 12,
|
||||
show: (formValues) =>
|
||||
formValues.storage >= 11 && formValues.storage <= 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -116,7 +121,8 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: ['storage'],
|
||||
show: (formValues) => formValues.storage >= 11 && formValues.storage <= 12,
|
||||
show: (formValues) =>
|
||||
formValues.storage >= 11 && formValues.storage <= 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -240,7 +246,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = InfraFileConfigApi.InfraFileConfig>(
|
||||
export function useGridColumns<T = InfraFileConfigApi.FileConfig>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraFileConfigApi } from '#/api/infra/file-config';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
import { Plus } from '@vben/icons';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getFileConfigPage, deleteFileConfig, updateFileConfigMaster, testFileConfig } from '#/api/infra/file-config';
|
||||
import {
|
||||
deleteFileConfig,
|
||||
getFileConfigPage,
|
||||
testFileConfig,
|
||||
updateFileConfigMaster,
|
||||
} from '#/api/infra/file-config';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
@@ -28,12 +38,12 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑文件配置 */
|
||||
function onEdit(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
function onEdit(row: InfraFileConfigApi.FileConfig) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 设为主配置 */
|
||||
async function onMaster(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
async function onMaster(row: InfraFileConfigApi.FileConfig) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.updating', [row.name]),
|
||||
duration: 0,
|
||||
@@ -46,13 +56,13 @@ async function onMaster(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 测试文件配置 */
|
||||
async function onTest(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
async function onTest(row: InfraFileConfigApi.FileConfig) {
|
||||
const hideLoading = message.loading({
|
||||
content: '测试上传中...',
|
||||
duration: 0,
|
||||
@@ -71,13 +81,13 @@ async function onTest(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
window.open(response, '_blank');
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除文件配置 */
|
||||
async function onDelete(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
async function onDelete(row: InfraFileConfigApi.FileConfig) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
@@ -90,7 +100,7 @@ async function onDelete(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
@@ -99,8 +109,12 @@ async function onDelete(row: InfraFileConfigApi.InfraFileConfig) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraFileConfigApi.InfraFileConfig>) {
|
||||
}: OnActionClickParams<InfraFileConfigApi.FileConfig>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
@@ -113,16 +127,12 @@ function onActionClick({
|
||||
onTest(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema()
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
@@ -146,7 +156,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraFileConfigApi.InfraFileConfig>,
|
||||
} as VxeTableGridOptions<InfraFileConfigApi.FileConfig>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -155,7 +165,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="文件配置列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onCreate" v-access:code="['infra:file-config:create']">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:file-config:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', ['文件配置']) }}
|
||||
</Button>
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraFileConfigApi } from '#/api/infra/file-config';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createFileConfig, updateFileConfig, getFileConfig } from '#/api/infra/file-config';
|
||||
import {
|
||||
createFileConfig,
|
||||
getFileConfig,
|
||||
updateFileConfig,
|
||||
} from '#/api/infra/file-config';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<InfraFileConfigApi.InfraFileConfig>();
|
||||
const formData = ref<InfraFileConfigApi.FileConfig>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['文件配置'])
|
||||
@@ -33,9 +39,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as InfraFileConfigApi.InfraFileConfig;
|
||||
const data = (await formApi.getValues()) as InfraFileConfigApi.FileConfig;
|
||||
try {
|
||||
await (formData.value?.id ? updateFileConfig(data) : createFileConfig(data));
|
||||
await (formData.value?.id
|
||||
? updateFileConfig(data)
|
||||
: createFileConfig(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -52,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraFileConfigApi.InfraFileConfig>();
|
||||
const data = modalApi.getData<InfraFileConfigApi.FileConfig>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
@@ -72,4 +80,4 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<Modal :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraJobApi } from '#/api/infra/job';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { InfraJobStatusEnum } from '#/utils/constants';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
@@ -75,7 +76,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入重试间隔,单位:毫秒。设置为 0 时,无需间隔',
|
||||
min: 0,
|
||||
class: 'w-full'
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -127,7 +128,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns<T = InfraJobApi.InfraJob>(
|
||||
export function useGridColumns<T = InfraJobApi.Job>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
@@ -147,7 +148,7 @@ export function useGridColumns<T = InfraJobApi.InfraJob>(
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.INFRA_JOB_STATUS, },
|
||||
props: { type: DICT_TYPE.INFRA_JOB_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -186,14 +187,16 @@ export function useGridColumns<T = InfraJobApi.InfraJob>(
|
||||
{
|
||||
code: 'update-status',
|
||||
text: '开启',
|
||||
show: (row: any) => hasAccessByCodes(['infra:job:update'])
|
||||
&& row.status === InfraJobStatusEnum.STOP,
|
||||
show: (row: any) =>
|
||||
hasAccessByCodes(['infra:job:update']) &&
|
||||
row.status === InfraJobStatusEnum.STOP,
|
||||
},
|
||||
{
|
||||
code: 'update-status',
|
||||
text: '暂停',
|
||||
show: (row: any) => hasAccessByCodes(['infra:job:update'])
|
||||
&& row.status == InfraJobStatusEnum.NORMAL,
|
||||
show: (row: any) =>
|
||||
hasAccessByCodes(['infra:job:update']) &&
|
||||
row.status == InfraJobStatusEnum.NORMAL,
|
||||
},
|
||||
{
|
||||
code: 'trigger',
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraJobApi } from '#/api/infra/job';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus, History } from '@vben/icons';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
import Form from './modules/form.vue';
|
||||
import Detail from './modules/detail.vue';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { InfraJobStatusEnum} from '#/utils/constants';
|
||||
import { deleteJob, exportJob, getJobPage, runJob, updateJobStatus } from '#/api/infra/job';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, History, Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteJob,
|
||||
exportJob,
|
||||
getJobPage,
|
||||
runJob,
|
||||
updateJobStatus,
|
||||
} from '#/api/infra/job';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { $t } from '#/locales';
|
||||
import { InfraJobStatusEnum } from '#/utils/constants';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
@@ -47,18 +58,21 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑任务 */
|
||||
function onEdit(row: InfraJobApi.InfraJob) {
|
||||
function onEdit(row: InfraJobApi.Job) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 查看任务详情 */
|
||||
function onDetail(row: InfraJobApi.InfraJob) {
|
||||
function onDetail(row: InfraJobApi.Job) {
|
||||
detailModalApi.setData({ id: row.id }).open();
|
||||
}
|
||||
|
||||
/** 更新任务状态 */
|
||||
async function onUpdateStatus(row: InfraJobApi.InfraJob) {
|
||||
const status = row.status === InfraJobStatusEnum.STOP ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP;
|
||||
async function onUpdateStatus(row: InfraJobApi.Job) {
|
||||
const status =
|
||||
row.status === InfraJobStatusEnum.STOP
|
||||
? InfraJobStatusEnum.NORMAL
|
||||
: InfraJobStatusEnum.STOP;
|
||||
const statusText = status === InfraJobStatusEnum.NORMAL ? '启用' : '停用';
|
||||
Modal.confirm({
|
||||
title: '确认操作',
|
||||
@@ -70,12 +84,12 @@ async function onUpdateStatus(row: InfraJobApi.InfraJob) {
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 执行一次任务 */
|
||||
async function onTrigger(row: InfraJobApi.InfraJob) {
|
||||
async function onTrigger(row: InfraJobApi.Job) {
|
||||
Modal.confirm({
|
||||
title: '确认操作',
|
||||
content: `确定执行一次 ${row.name} 吗?`,
|
||||
@@ -85,12 +99,12 @@ async function onTrigger(row: InfraJobApi.InfraJob) {
|
||||
content: $t('ui.actionMessage.operationSuccess'),
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 跳转到任务日志 */
|
||||
function onLog(row?: InfraJobApi.InfraJob) {
|
||||
function onLog(row?: InfraJobApi.Job) {
|
||||
push({
|
||||
name: 'InfraJobLog',
|
||||
query: row?.id ? { id: row.id } : {},
|
||||
@@ -98,7 +112,7 @@ function onLog(row?: InfraJobApi.InfraJob) {
|
||||
}
|
||||
|
||||
/** 删除任务 */
|
||||
async function onDelete(row: InfraJobApi.InfraJob) {
|
||||
async function onDelete(row: InfraJobApi.Job) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
@@ -117,35 +131,32 @@ async function onDelete(row: InfraJobApi.InfraJob) {
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraJobApi.InfraJob>) {
|
||||
function onActionClick({ code, row }: OnActionClickParams<InfraJobApi.Job>) {
|
||||
switch (code) {
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'update-status': {
|
||||
onUpdateStatus(row);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'trigger': {
|
||||
onTrigger(row);
|
||||
break;
|
||||
}
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'log': {
|
||||
onLog(row);
|
||||
break;
|
||||
}
|
||||
case 'trigger': {
|
||||
onTrigger(row);
|
||||
break;
|
||||
}
|
||||
case 'update-status': {
|
||||
onUpdateStatus(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +186,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraJobApi.InfraJob>,
|
||||
} as VxeTableGridOptions<InfraJobApi.Job>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -189,15 +200,29 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<DetailModal />
|
||||
<Grid table-title="定时任务列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onCreate" v-access:code="['infra:job:create']">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:job:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', ['任务']) }}
|
||||
</Button>
|
||||
<Button type="primary" class="ml-2" @click="onExport" v-access:code="['infra:job:export']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:job:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
<Button type="primary" class="ml-2" @click="onLog(undefined)" v-access:code="['infra:job:query']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onLog(undefined)"
|
||||
v-access:code="['infra:job:query']"
|
||||
>
|
||||
<History class="size-5" />
|
||||
执行日志
|
||||
</Button>
|
||||
|
||||
@@ -2,11 +2,13 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { InfraJobLogApi } from '#/api/infra/job-log';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { useAccess } from '@vben/access';
|
||||
import dayjs from 'dayjs';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
@@ -63,7 +65,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns<T = InfraJobLogApi.InfraJobLog>(
|
||||
export function useGridColumns<T = InfraJobLogApi.JobLog>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { InfraJobLogApi } from '#/api/infra/job-log';
|
||||
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download } from '@vben/icons';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import Detail from './modules/detail.vue';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { exportJobLog, getJobLogPage } from '#/api/infra/job-log';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { $t } from '#/locales';
|
||||
import { downloadByData } from '#/utils/download';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
|
||||
const { query } = useRoute();
|
||||
|
||||
@@ -30,7 +35,7 @@ async function onExport() {
|
||||
}
|
||||
|
||||
/** 查看日志详情 */
|
||||
function onDetail(row: InfraJobLogApi.InfraJobLog) {
|
||||
function onDetail(row: InfraJobLogApi.JobLog) {
|
||||
detailModalApi.setData({ id: row.id }).open();
|
||||
}
|
||||
|
||||
@@ -38,7 +43,7 @@ function onDetail(row: InfraJobLogApi.InfraJobLog) {
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraJobLogApi.InfraJobLog>) {
|
||||
}: OnActionClickParams<InfraJobLogApi.JobLog>) {
|
||||
switch (code) {
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
@@ -77,7 +82,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<InfraJobLogApi.InfraJobLog>,
|
||||
} as VxeTableGridOptions<InfraJobLogApi.JobLog>,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -90,7 +95,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<DetailModal />
|
||||
<Grid table-title="任务日志列表">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" class="ml-2" @click="onExport" v-access:code="['infra:job:export']">
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:job:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraJobLogApi } from '#/api/infra/job-log';
|
||||
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { getJobLog } from '#/api/infra/job-log';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const formData = ref<InfraJobLogApi.InfraJobLog>();
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
|
||||
import { getJobLog } from '#/api/infra/job-log';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const formData = ref<InfraJobLogApi.JobLog>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -33,8 +35,19 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="日志详情" class="w-1/2" :show-cancel-button="false" :show-confirm-button="false">
|
||||
<Descriptions :column="1" bordered size="middle" class="mx-4" :label-style="{ width: '140px' }">
|
||||
<Modal
|
||||
title="日志详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions
|
||||
:column="1"
|
||||
bordered
|
||||
size="middle"
|
||||
class="mx-4"
|
||||
:label-style="{ width: '140px' }"
|
||||
>
|
||||
<Descriptions.Item label="日志编号">
|
||||
{{ formData?.id }}
|
||||
</Descriptions.Item>
|
||||
@@ -55,10 +68,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||
{{ formData?.endTime ? formatDateTime(formData.endTime) : '' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="执行时长">
|
||||
{{ formData?.duration ? formData.duration + ' 毫秒' : '' }}
|
||||
{{ formData?.duration ? `${formData.duration} 毫秒` : '' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="任务状态">
|
||||
<DictTag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="formData?.status" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.INFRA_JOB_LOG_STATUS"
|
||||
:value="formData?.status"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="执行结果">
|
||||
{{ formData?.result }}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraJobApi } from '#/api/infra/job';
|
||||
|
||||
import { Descriptions, Timeline } from 'ant-design-vue';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { getJob, getJobNextTimes } from '#/api/infra/job';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const formData = ref<InfraJobApi.InfraJob>(); // 任务详情
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, Timeline } from 'ant-design-vue';
|
||||
|
||||
import { getJob, getJobNextTimes } from '#/api/infra/job';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const formData = ref<InfraJobApi.Job>(); // 任务详情
|
||||
const nextTimes = ref<Date[]>([]); // 下一次执行时间
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -36,8 +38,19 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="任务详情" class="w-1/2" :show-cancel-button="false" :show-confirm-button="false">
|
||||
<Descriptions :column="1" bordered size="middle" class="mx-4" :label-style="{ width: '140px' }">
|
||||
<Modal
|
||||
title="任务详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions
|
||||
:column="1"
|
||||
bordered
|
||||
size="middle"
|
||||
class="mx-4"
|
||||
:label-style="{ width: '140px' }"
|
||||
>
|
||||
<Descriptions.Item label="任务编号">
|
||||
{{ formData?.id }}
|
||||
</Descriptions.Item>
|
||||
@@ -60,14 +73,24 @@ const [Modal, modalApi] = useVbenModal({
|
||||
{{ formData?.retryCount }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="重试间隔">
|
||||
{{ formData?.retryInterval ? formData.retryInterval + ' 毫秒' : '无间隔' }}
|
||||
{{
|
||||
formData?.retryInterval ? `${formData.retryInterval} 毫秒` : '无间隔'
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="监控超时时间">
|
||||
{{ formData?.monitorTimeout && formData.monitorTimeout > 0 ? formData.monitorTimeout + ' 毫秒' : '未开启' }}
|
||||
{{
|
||||
formData?.monitorTimeout && formData.monitorTimeout > 0
|
||||
? `${formData.monitorTimeout} 毫秒`
|
||||
: '未开启'
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="后续执行时间">
|
||||
<Timeline class="h-[180px]">
|
||||
<Timeline.Item v-for="(nextTime, index) in nextTimes" :key="index" color="blue">
|
||||
<Timeline.Item
|
||||
v-for="(nextTime, index) in nextTimes"
|
||||
:key="index"
|
||||
color="blue"
|
||||
>
|
||||
第 {{ index + 1 }} 次:{{ formatDateTime(nextTime.toString()) }}
|
||||
</Timeline.Item>
|
||||
</Timeline>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraJobApi } from '#/api/infra/job';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createJob, getJob, updateJob } from '#/api/infra/job';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<InfraJobApi.InfraJob>();
|
||||
const formData = ref<InfraJobApi.Job>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['任务'])
|
||||
@@ -24,8 +26,8 @@ const [Form, formApi] = useVbenForm({
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
labelWidth: 140
|
||||
}
|
||||
labelWidth: 140,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -36,11 +38,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as InfraJobApi.InfraJob;
|
||||
const data = (await formApi.getValues()) as InfraJobApi.Job;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateJob(data)
|
||||
: createJob(data));
|
||||
await (formData.value?.id ? updateJob(data) : createJob(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
@@ -57,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<InfraJobApi.InfraJob>();
|
||||
const data = modalApi.getData<InfraJobApi.Job>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
@@ -77,4 +77,4 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<Modal :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import Memory from './modules/memory.vue';
|
||||
import Commands from './modules/commands.vue';
|
||||
import Info from './modules/info.vue';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
|
||||
import { getRedisMonitorInfo } from '#/api/infra/redis';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getRedisMonitorInfo } from '#/api/infra/redis';
|
||||
import Commands from './modules/commands.vue';
|
||||
import Info from './modules/info.vue';
|
||||
import Memory from './modules/memory.vue';
|
||||
|
||||
const redisData = ref<InfraRedisApi.InfraRedisMonitorInfo>();
|
||||
const redisData = ref<InfraRedisApi.RedisMonitorInfo>();
|
||||
|
||||
/** 统一加载 Redis 数据 */
|
||||
const loadRedisData = async () => {
|
||||
@@ -36,7 +39,7 @@ onMounted(() => {
|
||||
<Info :redis-data="redisData" />
|
||||
</Card>
|
||||
|
||||
<div class="mt-5 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="mt-5 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<Card title="内存使用">
|
||||
<Memory :redis-data="redisData" />
|
||||
</Card>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
const props = defineProps<{
|
||||
redisData?: InfraRedisApi.InfraRedisMonitorInfo;
|
||||
redisData?: InfraRedisApi.RedisMonitorInfo;
|
||||
}>();
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
@@ -25,7 +26,7 @@ const renderCommandStats = () => {
|
||||
props.redisData.commandStats.forEach((row) => {
|
||||
commandStats.push({
|
||||
name: row.command,
|
||||
value: row.calls
|
||||
value: row.calls,
|
||||
});
|
||||
nameList.push(row.command);
|
||||
});
|
||||
@@ -34,11 +35,11 @@ const renderCommandStats = () => {
|
||||
renderEcharts({
|
||||
title: {
|
||||
text: '命令统计',
|
||||
left: 'center'
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
@@ -48,8 +49,8 @@ const renderCommandStats = () => {
|
||||
bottom: 20,
|
||||
data: nameList,
|
||||
textStyle: {
|
||||
color: '#a1a1a1'
|
||||
}
|
||||
color: '#a1a1a1',
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -60,29 +61,33 @@ const renderCommandStats = () => {
|
||||
data: commandStats,
|
||||
roseType: 'radius',
|
||||
label: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
/** 监听数据变化,重新渲染图表 */
|
||||
watch(() => props.redisData, (newVal) => {
|
||||
if (newVal) {
|
||||
renderCommandStats();
|
||||
}
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.redisData,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
renderCommandStats();
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.redisData) {
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import { type InfraRedisApi } from '#/api/infra/redis';
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
|
||||
defineProps<{
|
||||
redisData?: InfraRedisApi.InfraRedisMonitorInfo;
|
||||
redisData?: InfraRedisApi.RedisMonitorInfo;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions :column="6" bordered size="middle" :label-style="{ width: '138px' }">
|
||||
<Descriptions
|
||||
:column="6"
|
||||
bordered
|
||||
size="middle"
|
||||
:label-style="{ width: '138px' }"
|
||||
>
|
||||
<Descriptions.Item label="Redis 版本">
|
||||
{{ redisData?.info?.redis_version }}
|
||||
</Descriptions.Item>
|
||||
@@ -29,7 +34,11 @@ defineProps<{
|
||||
{{ redisData?.info?.used_memory_human }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="使用 CPU">
|
||||
{{ redisData?.info ? parseFloat(redisData?.info?.used_cpu_user_children).toFixed(2) : '' }}
|
||||
{{
|
||||
redisData?.info
|
||||
? parseFloat(redisData?.info?.used_cpu_user_children).toFixed(2)
|
||||
: ''
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="内存配置">
|
||||
{{ redisData?.info?.maxmemory_human }}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import type { InfraRedisApi } from '#/api/infra/redis';
|
||||
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
const props = defineProps<{
|
||||
redisData?: InfraRedisApi.InfraRedisMonitorInfo;
|
||||
redisData?: InfraRedisApi.RedisMonitorInfo;
|
||||
}>();
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
@@ -22,8 +23,8 @@ const parseMemoryValue = (memStr: string | undefined): number => {
|
||||
// 从字符串中提取数字部分,例如 "1.2M" 中的 1.2
|
||||
const str = String(memStr); // 显式转换为字符串类型
|
||||
const match = str.match(/^([\d.]+)/);
|
||||
return match ? parseFloat(match[1] as string) : 0;
|
||||
} catch (e) {
|
||||
return match ? Number.parseFloat(match[1] as string) : 0;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -45,7 +46,7 @@ const renderMemoryChart = () => {
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '{b} <br/>{a} : ' + usedMemory
|
||||
formatter: `{b} <br/>{a} : ${usedMemory}`,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -64,59 +65,63 @@ const renderMemoryChart = () => {
|
||||
color: [
|
||||
[0.2, '#7FFF00'],
|
||||
[0.8, '#00FFFF'],
|
||||
[1, '#FF0000']
|
||||
[1, '#FF0000'],
|
||||
],
|
||||
width: 10
|
||||
}
|
||||
width: 10,
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
length: 5,
|
||||
lineStyle: {
|
||||
color: '#76D9D7'
|
||||
}
|
||||
color: '#76D9D7',
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
length: 20,
|
||||
lineStyle: {
|
||||
color: '#76D9D7'
|
||||
}
|
||||
color: '#76D9D7',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#76D9D7',
|
||||
distance: 15,
|
||||
fontSize: 15
|
||||
fontSize: 15,
|
||||
},
|
||||
pointer: {
|
||||
width: 7,
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
detail: {
|
||||
show: true,
|
||||
offsetCenter: [0, '50%'],
|
||||
color: 'auto',
|
||||
fontSize: 30,
|
||||
formatter: usedMemory
|
||||
formatter: usedMemory,
|
||||
},
|
||||
progress: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: memoryValue,
|
||||
name: '内存消耗'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
name: '内存消耗',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
/** 监听数据变化,重新渲染图表 */
|
||||
watch(() => props.redisData, (newVal) => {
|
||||
if (newVal) {
|
||||
renderMemoryChart();
|
||||
}
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.redisData,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
renderMemoryChart();
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.redisData) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui'
|
||||
import { IFrame } from '#/components/iframe'
|
||||
import { DocAlert } from '#/components/doc-alert'
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getConfigKey } from '#/api/infra/config'
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
const loading = ref(true) // 是否加载中
|
||||
const src = ref(import.meta.env.VITE_BASE_URL + '/admin/applications')
|
||||
import { getConfigKey } from '#/api/infra/config';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
const loading = ref(true); // 是否加载中
|
||||
const src = ref(`${import.meta.env.VITE_BASE_URL}/admin/applications`);
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
@@ -15,14 +16,14 @@ onMounted(async () => {
|
||||
// 友情提示:如果访问出现 404 问题:
|
||||
// 1)boot 参考 https://doc.iocoder.cn/server-monitor/ 解决;
|
||||
// 2)cloud 参考 https://cloud.iocoder.cn/server-monitor/ 解决
|
||||
const data = await getConfigKey('url.spring-boot-admin')
|
||||
const data = await getConfigKey('url.spring-boot-admin');
|
||||
if (data && data.length > 0) {
|
||||
src.value = data
|
||||
src.value = data;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -31,4 +32,4 @@ onMounted(async () => {
|
||||
|
||||
<IFrame v-if="!loading" v-loading="loading" :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui'
|
||||
import { DocAlert } from '#/components/doc-alert'
|
||||
import { IFrame } from '#/components/iframe'
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getConfigKey } from '#/api/infra/config'
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
const loading = ref(true) // 是否加载中
|
||||
const src = ref('http://skywalking.shop.iocoder.cn')
|
||||
import { getConfigKey } from '#/api/infra/config';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
const loading = ref(true); // 是否加载中
|
||||
const src = ref('http://skywalking.shop.iocoder.cn');
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await getConfigKey('url.skywalking')
|
||||
const data = await getConfigKey('url.skywalking');
|
||||
if (data && data.length > 0) {
|
||||
src.value = data
|
||||
src.value = data;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui'
|
||||
import { DocAlert } from '#/components/doc-alert'
|
||||
import { IFrame } from '#/components/iframe'
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getConfigKey } from '#/api/infra/config'
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
const loading = ref(true) // 是否加载中
|
||||
const src = ref(import.meta.env.VITE_BASE_URL + '/doc.html') // Knife4j UI
|
||||
import { getConfigKey } from '#/api/infra/config';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
const loading = ref(true); // 是否加载中
|
||||
const src = ref(`${import.meta.env.VITE_BASE_URL}/doc.html`); // Knife4j UI
|
||||
// const src = ref(import.meta.env.VITE_BASE_URL + '/swagger-ui') // Swagger UI
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await getConfigKey('url.swagger')
|
||||
const data = await getConfigKey('url.swagger');
|
||||
if (data && data.length > 0) {
|
||||
src.value = data
|
||||
src.value = data;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -29,4 +30,4 @@ onMounted(async () => {
|
||||
|
||||
<IFrame v-if="!loading" :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,41 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
import type { SystemUserApi } from '#/api/system/user'
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import { message } from 'ant-design-vue'
|
||||
import { Card, Tag, Divider, Input, Button, Select, Avatar, Empty, Badge } from 'ant-design-vue'
|
||||
import { Page } from '@vben/common-ui'
|
||||
import { DocAlert } from '#/components/doc-alert'
|
||||
import { computed, onMounted, ref, watchEffect } from 'vue';
|
||||
|
||||
import { formatDate } from '@vben/utils'
|
||||
import { useWebSocket } from '@vueuse/core'
|
||||
import { getSimpleUserList } from '#/api/system/user'
|
||||
import { ref, computed, watchEffect, onMounted } from 'vue'
|
||||
import { useAccessStore } from '@vben/stores'
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
const accessStore = useAccessStore()
|
||||
const refreshToken = accessStore.refreshToken as string
|
||||
import { useWebSocket } from '@vueuse/core';
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Empty,
|
||||
Input,
|
||||
message,
|
||||
Select,
|
||||
Tag,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
const refreshToken = accessStore.refreshToken as string;
|
||||
|
||||
const server = ref(
|
||||
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
||||
'?token=' +
|
||||
refreshToken // 使用 refreshToken,而不使用 accessToken 方法的原因:WebSocket 无法方便的刷新访问令牌
|
||||
) // WebSocket 服务地址
|
||||
const getIsOpen = computed(() => status.value === 'OPEN') // WebSocket 连接是否打开
|
||||
const getTagColor = computed(() => (getIsOpen.value ? 'success' : 'red')) // WebSocket 连接的展示颜色
|
||||
const getStatusText = computed(() => (getIsOpen.value ? '已连接' : '未连接')) // 连接状态文本
|
||||
`${`${import.meta.env.VITE_BASE_URL}/infra/ws`.replace(
|
||||
'http',
|
||||
'ws',
|
||||
)}?token=${refreshToken}`, // 使用 refreshToken,而不使用 accessToken 方法的原因:WebSocket 无法方便的刷新访问令牌
|
||||
); // WebSocket 服务地址
|
||||
const getIsOpen = computed(() => status.value === 'OPEN'); // WebSocket 连接是否打开
|
||||
const getTagColor = computed(() => (getIsOpen.value ? 'success' : 'red')); // WebSocket 连接的展示颜色
|
||||
const getStatusText = computed(() => (getIsOpen.value ? '已连接' : '未连接')); // 连接状态文本
|
||||
|
||||
/** 发起 WebSocket 连接 */
|
||||
const { status, data, send, close, open } = useWebSocket(server.value, {
|
||||
autoReconnect: true,
|
||||
heartbeat: true
|
||||
})
|
||||
heartbeat: true,
|
||||
});
|
||||
|
||||
/** 监听接收到的数据 */
|
||||
const messageList = ref([] as { time: number; text: string; type?: string; userId?: string }[]) // 消息列表
|
||||
const messageReverseList = computed(() => messageList.value.slice().reverse())
|
||||
const messageList = ref(
|
||||
[] as { text: string; time: number; type?: string; userId?: string }[],
|
||||
); // 消息列表
|
||||
const messageReverseList = computed(() => [...messageList.value].reverse());
|
||||
watchEffect(() => {
|
||||
if (!data.value) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 1. 收到心跳
|
||||
@@ -44,109 +59,128 @@ watchEffect(() => {
|
||||
// text: '【心跳】',
|
||||
// time: new Date().getTime()
|
||||
// })
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.1 解析 type 消息类型
|
||||
const jsonMessage = JSON.parse(data.value)
|
||||
const type = jsonMessage.type
|
||||
const content = JSON.parse(jsonMessage.content)
|
||||
const jsonMessage = JSON.parse(data.value);
|
||||
const type = jsonMessage.type;
|
||||
const content = JSON.parse(jsonMessage.content);
|
||||
if (!type) {
|
||||
message.error('未知的消息类型:' + data.value)
|
||||
return
|
||||
message.error(`未知的消息类型:${data.value}`);
|
||||
return;
|
||||
}
|
||||
// 2.2 消息类型:demo-message-receive
|
||||
if (type === 'demo-message-receive') {
|
||||
const single = content.single
|
||||
const single = content.single;
|
||||
messageList.value.push({
|
||||
text: content.text,
|
||||
time: new Date().getTime(),
|
||||
time: Date.now(),
|
||||
type: single ? 'single' : 'group',
|
||||
userId: content.fromUserId
|
||||
})
|
||||
return
|
||||
userId: content.fromUserId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 2.3 消息类型:notice-push
|
||||
if (type === 'notice-push') {
|
||||
messageList.value.push({
|
||||
text: content.title,
|
||||
time: new Date().getTime(),
|
||||
type: 'system'
|
||||
})
|
||||
return
|
||||
time: Date.now(),
|
||||
type: 'system',
|
||||
});
|
||||
return;
|
||||
}
|
||||
message.error('未处理消息:' + data.value)
|
||||
message.error(`未处理消息:${data.value}`);
|
||||
} catch (error) {
|
||||
message.error('处理消息发生异常:' + data.value)
|
||||
console.error(error)
|
||||
message.error(`处理消息发生异常:${data.value}`);
|
||||
console.error(error);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/** 发送消息 */
|
||||
const sendText = ref('') // 发送内容
|
||||
const sendUserId = ref('') // 发送人
|
||||
const sendText = ref(''); // 发送内容
|
||||
const sendUserId = ref(''); // 发送人
|
||||
const handlerSend = () => {
|
||||
if (!sendText.value.trim()) {
|
||||
message.warning('消息内容不能为空')
|
||||
return
|
||||
message.warning('消息内容不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 1.1 先 JSON 化 message 消息内容
|
||||
const messageContent = JSON.stringify({
|
||||
text: sendText.value,
|
||||
toUserId: sendUserId.value
|
||||
})
|
||||
toUserId: sendUserId.value,
|
||||
});
|
||||
// 1.2 再 JSON 化整个消息
|
||||
const jsonMessage = JSON.stringify({
|
||||
type: 'demo-message-send',
|
||||
content: messageContent
|
||||
})
|
||||
content: messageContent,
|
||||
});
|
||||
// 2. 最后发送消息
|
||||
send(jsonMessage)
|
||||
sendText.value = ''
|
||||
}
|
||||
send(jsonMessage);
|
||||
sendText.value = '';
|
||||
};
|
||||
|
||||
/** 切换 websocket 连接状态 */
|
||||
const toggleConnectStatus = () => {
|
||||
if (getIsOpen.value) {
|
||||
close()
|
||||
close();
|
||||
} else {
|
||||
open()
|
||||
open();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取消息类型的徽标颜色 */
|
||||
const getMessageBadgeColor = (type?: string) => {
|
||||
switch (type) {
|
||||
case 'single': return 'blue'
|
||||
case 'group': return 'green'
|
||||
case 'system': return 'red'
|
||||
default: return 'default'
|
||||
case 'group': {
|
||||
return 'green';
|
||||
}
|
||||
case 'single': {
|
||||
return 'blue';
|
||||
}
|
||||
case 'system': {
|
||||
return 'red';
|
||||
}
|
||||
default: {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取消息类型的文本 */
|
||||
const getMessageTypeText = (type?: string) => {
|
||||
switch (type) {
|
||||
case 'single': return '单发'
|
||||
case 'group': return '群发'
|
||||
case 'system': return '系统'
|
||||
default: return '未知'
|
||||
case 'group': {
|
||||
return '群发';
|
||||
}
|
||||
case 'single': {
|
||||
return '单发';
|
||||
}
|
||||
case 'system': {
|
||||
return '系统';
|
||||
}
|
||||
default: {
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 初始化 **/
|
||||
const userList = ref<SystemUserApi.SystemUser[]>([]) // 用户列表
|
||||
/** 初始化 */
|
||||
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
|
||||
onMounted(async () => {
|
||||
userList.value = await getSimpleUserList()
|
||||
})
|
||||
userList.value = await getSimpleUserList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="WebSocket 实时通信" url="https://doc.iocoder.cn/websocket/" />
|
||||
<DocAlert
|
||||
title="WebSocket 实时通信"
|
||||
url="https://doc.iocoder.cn/websocket/"
|
||||
/>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 mt-4">
|
||||
<div class="mt-4 flex flex-col gap-4 md:flex-row">
|
||||
<!-- 左侧:建立连接、发送消息 -->
|
||||
<Card :bordered="false" class="w-full md:w-1/2">
|
||||
<template #title>
|
||||
@@ -155,16 +189,17 @@ onMounted(async () => {
|
||||
<span class="ml-2 text-lg font-medium">连接管理</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center mb-4 bg-gray-50 p-3 rounded-lg">
|
||||
<div class="mb-4 flex items-center rounded-lg bg-gray-50 p-3">
|
||||
<span class="mr-4 font-medium">连接状态:</span>
|
||||
<Tag :color="getTagColor" class="px-3 py-1">{{ getStatusText }}</Tag>
|
||||
</div>
|
||||
<div class="flex space-x-2 mb-6">
|
||||
<Input
|
||||
v-model:value="server"
|
||||
<div class="mb-6 flex space-x-2">
|
||||
<Input
|
||||
v-model:value="server"
|
||||
disabled
|
||||
class="rounded-md"
|
||||
size="large">
|
||||
size="large"
|
||||
>
|
||||
<template #addonBefore>
|
||||
<span class="text-gray-600">服务地址</span>
|
||||
</template>
|
||||
@@ -179,17 +214,18 @@ onMounted(async () => {
|
||||
{{ getIsOpen ? '关闭连接' : '开启连接' }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
<Divider>
|
||||
<span class="text-gray-500">消息发送</span>
|
||||
</Divider>
|
||||
|
||||
<Select
|
||||
v-model:value="sendUserId"
|
||||
class="w-full mb-3"
|
||||
<Select
|
||||
v-model:value="sendUserId"
|
||||
class="mb-3 w-full"
|
||||
size="large"
|
||||
placeholder="请选择接收人"
|
||||
:disabled="!getIsOpen">
|
||||
:disabled="!getIsOpen"
|
||||
>
|
||||
<Select.Option key="" value="" label="所有人">
|
||||
<div class="flex items-center">
|
||||
<Avatar size="small" class="mr-2">全</Avatar>
|
||||
@@ -203,61 +239,72 @@ onMounted(async () => {
|
||||
:label="user.nickname"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<Avatar size="small" class="mr-2">{{ user.nickname.slice(0, 1) }}</Avatar>
|
||||
<Avatar size="small" class="mr-2">
|
||||
{{ user.nickname.slice(0, 1) }}
|
||||
</Avatar>
|
||||
<span>{{ user.nickname }}</span>
|
||||
</div>
|
||||
</Select.Option>
|
||||
</Select>
|
||||
|
||||
|
||||
<Input.TextArea
|
||||
v-model:value="sendText"
|
||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||
:disabled="!getIsOpen"
|
||||
class="rounded-lg border-1 border-gray-300"
|
||||
allowClear
|
||||
class="border-1 rounded-lg border-gray-300"
|
||||
allow-clear
|
||||
placeholder="请输入你要发送的消息..."
|
||||
/>
|
||||
|
||||
<Button
|
||||
:disabled="!getIsOpen"
|
||||
block
|
||||
class="mt-4"
|
||||
type="primary"
|
||||
|
||||
<Button
|
||||
:disabled="!getIsOpen"
|
||||
block
|
||||
class="mt-4"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="handlerSend">
|
||||
@click="handlerSend"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="i-ant-design:send-outlined mr-1" />
|
||||
<span class="i-ant-design:send-outlined mr-1"></span>
|
||||
</template>
|
||||
发送消息
|
||||
</Button>
|
||||
</Card>
|
||||
|
||||
|
||||
<!-- 右侧:消息记录 -->
|
||||
<Card :bordered="false" class="w-full md:w-1/2">
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<span class="i-ant-design:message-outlined mr-2 text-lg" />
|
||||
<span class="i-ant-design:message-outlined mr-2 text-lg"></span>
|
||||
<span class="text-lg font-medium">消息记录</span>
|
||||
<Tag v-if="messageList.length > 0" class="ml-2">{{ messageList.length }} 条</Tag>
|
||||
<Tag v-if="messageList.length > 0" class="ml-2">
|
||||
{{ messageList.length }} 条
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
<div class="h-96 overflow-auto p-2 bg-gray-50 rounded-lg">
|
||||
<div class="h-96 overflow-auto rounded-lg bg-gray-50 p-2">
|
||||
<Empty v-if="messageList.length === 0" description="暂无消息记录" />
|
||||
<div v-else class="space-y-3">
|
||||
<div
|
||||
v-for="msg in messageReverseList"
|
||||
:key="msg.time"
|
||||
class="p-3 bg-white rounded-lg shadow-sm"
|
||||
<div
|
||||
v-for="msg in messageReverseList"
|
||||
:key="msg.time"
|
||||
class="rounded-lg bg-white p-3 shadow-sm"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<div class="mb-1 flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Badge :color="getMessageBadgeColor(msg.type)" />
|
||||
<span class="ml-1 text-gray-600 font-medium">{{ getMessageTypeText(msg.type) }}</span>
|
||||
<span v-if="msg.userId" class="ml-2 text-gray-500">用户 ID: {{ msg.userId }}</span>
|
||||
<span class="ml-1 font-medium text-gray-600">{{
|
||||
getMessageTypeText(msg.type)
|
||||
}}</span>
|
||||
<span v-if="msg.userId" class="ml-2 text-gray-500"
|
||||
>用户 ID: {{ msg.userId }}</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400">{{ formatDate(msg.time) }}</span>
|
||||
<span class="text-xs text-gray-400">{{
|
||||
formatDate(msg.time)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="mt-2 text-gray-800 break-words">
|
||||
<div class="mt-2 break-words text-gray-800">
|
||||
{{ msg.text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user