物业代码生成
This commit is contained in:
1
apps/web-antd/src/views/演示使用自行删除/README.md
Normal file
1
apps/web-antd/src/views/演示使用自行删除/README.md
Normal file
@@ -0,0 +1 @@
|
||||
演示站专用目录 可直接删除该目录
|
26
apps/web-antd/src/views/演示使用自行删除/changelog/index.vue
Normal file
26
apps/web-antd/src/views/演示使用自行删除/changelog/index.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { MarkdownPreviewer, Page } from '@vben/common-ui';
|
||||
|
||||
import { Spin } from 'ant-design-vue';
|
||||
|
||||
import changelog from '../../../../../../CHANGELOG.md?raw';
|
||||
|
||||
const content = ref(changelog);
|
||||
|
||||
const loading = ref(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<Spin :spinning="loading" tip="加载markdown中...">
|
||||
<MarkdownPreviewer
|
||||
v-model:value="content"
|
||||
height="100%"
|
||||
class="min-h-[50vh]"
|
||||
@mounted="loading = false"
|
||||
/>
|
||||
</Spin>
|
||||
</Page>
|
||||
</template>
|
27
apps/web-antd/src/views/演示使用自行删除/menu/index.vue
Normal file
27
apps/web-antd/src/views/演示使用自行删除/menu/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { roleMenuTreeSelect } from '#/api/system/menu';
|
||||
import { MenuSelectTable } from '#/components/tree';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
|
||||
const checkedKeys = ref<number[]>([]);
|
||||
const menus = shallowRef<MenuOption[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
const resp = await roleMenuTreeSelect(3);
|
||||
menus.value = resp.menus;
|
||||
checkedKeys.value = resp.checkedKeys;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<MenuSelectTable
|
||||
:menus="menus"
|
||||
v-model:checked-keys="checkedKeys"
|
||||
:association="true"
|
||||
/>
|
||||
</Page>
|
||||
</template>
|
17
apps/web-antd/src/views/演示使用自行删除/query/index.vue
Normal file
17
apps/web-antd/src/views/演示使用自行删除/query/index.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { JsonPreview, Page } from '@vben/common-ui';
|
||||
|
||||
const route = useRoute();
|
||||
const query = route.query;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<div class="bg-background rounded-lg p-4">
|
||||
<span>当前参数:</span>
|
||||
<JsonPreview :data="query" />
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
26
apps/web-antd/src/views/演示使用自行删除/sse/api.ts
Normal file
26
apps/web-antd/src/views/演示使用自行删除/sse/api.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
enum Api {
|
||||
list = '/system/sse/list',
|
||||
send = '/system/sse/send',
|
||||
sendAll = '/system/sse/sendAll',
|
||||
status = '/system/sse/status',
|
||||
}
|
||||
|
||||
export function sseStatus() {
|
||||
return requestClient.get<boolean>(Api.status);
|
||||
}
|
||||
|
||||
export function sseSendAll(message: string) {
|
||||
return requestClient.postWithMsg<void>(`${Api.sendAll}?message=${message}`);
|
||||
}
|
||||
|
||||
export function sseSendByUserId(userId: string, message: string) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
`${Api.send}/${userId}?message=${message}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function sseList() {
|
||||
return requestClient.get<any>(Api.list);
|
||||
}
|
103
apps/web-antd/src/views/演示使用自行删除/sse/index.vue
Normal file
103
apps/web-antd/src/views/演示使用自行删除/sse/index.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import { sseList } from './api';
|
||||
import sendMsgModal from './send-msg-modal.vue';
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
columns: [
|
||||
{
|
||||
title: '用户ID',
|
||||
field: 'userId',
|
||||
},
|
||||
{
|
||||
title: '用户账号',
|
||||
field: 'userName',
|
||||
},
|
||||
{
|
||||
title: '用户昵称',
|
||||
field: 'nickName',
|
||||
},
|
||||
{
|
||||
title: '用户部门',
|
||||
field: 'deptName',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
resizable: false,
|
||||
width: 'auto',
|
||||
},
|
||||
],
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async () => {
|
||||
const list = await sseList();
|
||||
return {
|
||||
rows: list,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: false,
|
||||
keyField: 'userId',
|
||||
height: 48,
|
||||
},
|
||||
id: 'sse-index',
|
||||
};
|
||||
|
||||
const [BasicTable] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
const [SendMsgModal, modalApi] = useVbenModal({
|
||||
connectedComponent: sendMsgModal,
|
||||
});
|
||||
|
||||
function handleSendAll() {
|
||||
modalApi.setData({});
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
function handleSendSingle(userId: string) {
|
||||
modalApi.setData({ userId });
|
||||
modalApi.open();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
:auto-content-height="true"
|
||||
description="这这里可以进行[Server-sent events]测试 非官方功能"
|
||||
title="SSE测试"
|
||||
>
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<span class="pl-[7px] text-[16px]">在线用户列表</span>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="handleSendAll">发送全体消息</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<ghost-button @click="handleSendSingle(row.userId)">
|
||||
发送消息
|
||||
</ghost-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<SendMsgModal />
|
||||
</Page>
|
||||
</template>
|
77
apps/web-antd/src/views/演示使用自行删除/sse/send-msg-modal.vue
Normal file
77
apps/web-antd/src/views/演示使用自行删除/sse/send-msg-modal.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { sseSendAll, sseSendByUserId } from './api';
|
||||
|
||||
const currentUserId = ref<string | undefined>(undefined);
|
||||
const title = computed(() => {
|
||||
return currentUserId.value ? '发送指定消息' : '发送全体消息';
|
||||
});
|
||||
|
||||
const [BasicModal, modalApi] = useVbenModal({
|
||||
onConfirm: handleSubmit,
|
||||
onOpenChange: (isOpen) => {
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
const data = modalApi.getData() as { userId: string | undefined };
|
||||
currentUserId.value = data.userId;
|
||||
},
|
||||
});
|
||||
|
||||
const [BasicForm, formApi] = useVbenForm({
|
||||
layout: 'vertical',
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelWidth: 80,
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
component: 'Textarea',
|
||||
label: '消息内容',
|
||||
fieldName: 'content',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-2',
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
modalApi.modalLoading(true);
|
||||
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
const { content } = await formApi.getValues();
|
||||
|
||||
await (currentUserId.value
|
||||
? sseSendByUserId(currentUserId.value, content)
|
||||
: sseSendAll(content));
|
||||
modalApi.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
modalApi.modalLoading(false);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal
|
||||
:close-on-click-modal="false"
|
||||
:fullscreen-button="false"
|
||||
:title="title"
|
||||
>
|
||||
<BasicForm />
|
||||
</BasicModal>
|
||||
</template>
|
24
apps/web-antd/src/views/演示使用自行删除/tinymce/index.vue
Normal file
24
apps/web-antd/src/views/演示使用自行删除/tinymce/index.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Switch } from 'ant-design-vue';
|
||||
|
||||
import { Tinymce } from '#/components/tinymce';
|
||||
|
||||
const readonly = ref(false);
|
||||
const content = ref('');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page title="Tinymce富文本">
|
||||
<div class="flex flex-col gap-[16px]">
|
||||
<div class="flex items-center gap-[16px]">
|
||||
<span>禁用</span>
|
||||
<Switch v-model:checked="readonly" />
|
||||
</div>
|
||||
<Tinymce v-model="content" :height="800" :disabled="readonly" />
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
32
apps/web-antd/src/views/演示使用自行删除/upload/hook.ts
Normal file
32
apps/web-antd/src/views/演示使用自行删除/upload/hook.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export function useImageType() {
|
||||
const imageListTypes = ['text', 'picture', 'picture-card'] as const;
|
||||
const imageListOptions = imageListTypes.map((str) => ({
|
||||
label: str,
|
||||
value: str,
|
||||
}));
|
||||
|
||||
const currentImageListType =
|
||||
ref<(typeof imageListTypes)[number]>('picture-card');
|
||||
|
||||
return {
|
||||
imageListOptions,
|
||||
currentImageListType,
|
||||
};
|
||||
}
|
||||
|
||||
export function useFileType() {
|
||||
const fileListTypes = ['text', 'picture'] as const;
|
||||
const fileListOptions = fileListTypes.map((str) => ({
|
||||
label: str,
|
||||
value: str,
|
||||
}));
|
||||
|
||||
const currentFileListType = ref<(typeof fileListTypes)[number]>('picture');
|
||||
|
||||
return {
|
||||
fileListOptions,
|
||||
currentFileListType,
|
||||
};
|
||||
}
|
236
apps/web-antd/src/views/演示使用自行删除/upload/index.vue
Normal file
236
apps/web-antd/src/views/演示使用自行删除/upload/index.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadFile } from 'ant-design-vue/es/upload/interface';
|
||||
|
||||
import type { CustomGetter } from '#/components/upload/src/props';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { CodeMirror, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { Alert, Card, Modal, RadioGroup, Switch } from 'ant-design-vue';
|
||||
|
||||
import { FileUpload, ImageUpload } from '#/components/upload';
|
||||
|
||||
import { useFileType, useImageType } from './hook';
|
||||
import sql from './insert.sql?raw';
|
||||
import uploadModal from './upload-modal.vue';
|
||||
|
||||
const singleImageId = ref('1905537674682916865');
|
||||
const singleFileId = ref('1905191167882518529');
|
||||
const multipleImageId = ref<string[]>(['1905537674682916865']);
|
||||
const multipleFileId = ref<string[]>(['1905191167882518529']);
|
||||
|
||||
function handlePreview(file: UploadFile) {
|
||||
Modal.info({
|
||||
content: h('div', { class: 'break-all' }, JSON.stringify(file, null, 2)),
|
||||
maskClosable: true,
|
||||
});
|
||||
}
|
||||
|
||||
function customAccept(accept: string) {
|
||||
return accept
|
||||
.split(',')
|
||||
.map((str) => str.toUpperCase())
|
||||
.join(',');
|
||||
}
|
||||
|
||||
const showComponent = ref(true);
|
||||
|
||||
const { imageListOptions, currentImageListType } = useImageType();
|
||||
const { fileListOptions, currentFileListType } = useFileType();
|
||||
|
||||
const customName: CustomGetter<string> = (cb) => {
|
||||
if (cb.type === 'info') {
|
||||
return `加上自定义前缀显示 - ${cb.response.originalName.toUpperCase()}`;
|
||||
}
|
||||
return `加上自定义前缀显示 - ${cb.response.fileName.toUpperCase()}`;
|
||||
};
|
||||
|
||||
const customThumbnailUrl: CustomGetter<undefined> = () => {
|
||||
return 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp';
|
||||
};
|
||||
|
||||
const { copy } = useClipboard({ legacy: true });
|
||||
|
||||
const animationEnable = ref(false);
|
||||
|
||||
const [UploadModal, uploadModalApi] = useVbenModal({
|
||||
connectedComponent: uploadModal,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<Card class="mb-2" title="提示" size="small">
|
||||
本地想体验可以导入这个sql(mysql的 其他的自行处理或者手动从菜单添加)
|
||||
<a-button size="small" @click="copy(sql)">复制</a-button>
|
||||
<CodeMirror class="mt-2" v-model="sql" language="sql" readonly />
|
||||
</Card>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<Card title="表单上传">
|
||||
<a-button @click="uploadModalApi.open()">打开</a-button>
|
||||
<UploadModal />
|
||||
</Card>
|
||||
<Card title="单上传, 会绑定为string" size="small">
|
||||
<ImageUpload v-model:value="singleImageId" />
|
||||
当前绑定值: {{ singleImageId }}
|
||||
|
||||
<FileUpload class="mt-6" v-model:value="singleFileId" />
|
||||
当前绑定值: {{ singleFileId }}
|
||||
</Card>
|
||||
|
||||
<Card title="多上传, maxCount参数控制(开启深度监听)" size="small">
|
||||
<ImageUpload
|
||||
v-model:value="multipleImageId"
|
||||
:max-count="3"
|
||||
:deep-watch="true"
|
||||
/>
|
||||
当前绑定值: {{ multipleImageId }}
|
||||
|
||||
<FileUpload
|
||||
class="mt-6"
|
||||
v-model:value="multipleFileId"
|
||||
:max-count="3"
|
||||
:deep-watch="true"
|
||||
/>
|
||||
当前绑定值: {{ multipleFileId }}
|
||||
</Card>
|
||||
|
||||
<Card title="文件自定义预览逻辑" size="small">
|
||||
<Alert
|
||||
message="你可以自定义预览逻辑, 比如改为下载, 回调参数为文件信息"
|
||||
class="my-2"
|
||||
/>
|
||||
<FileUpload
|
||||
v-model:value="multipleFileId"
|
||||
:max-count="3"
|
||||
:preview="handlePreview"
|
||||
:help-message="false"
|
||||
/>
|
||||
<ImageUpload
|
||||
class="mt-6"
|
||||
v-model:value="multipleImageId"
|
||||
:max-count="3"
|
||||
:preview="handlePreview"
|
||||
:help-message="false"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="文件/图片拖拽上传" size="small">
|
||||
<FileUpload
|
||||
v-model:value="multipleFileId"
|
||||
:max-count="3"
|
||||
:enable-drag-upload="true"
|
||||
/>
|
||||
<ImageUpload
|
||||
class="mt-6"
|
||||
v-model:value="multipleImageId"
|
||||
:enable-drag-upload="true"
|
||||
:max-count="6"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="禁用上传" size="small">
|
||||
<ImageUpload :disabled="true" :max-count="3" :help-message="false" />
|
||||
<FileUpload
|
||||
class="mt-6"
|
||||
:disabled="true"
|
||||
:max-count="3"
|
||||
:help-message="false"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="文件夹上传/自定义helpMessage" size="small">
|
||||
<FileUpload
|
||||
v-model:value="multipleFileId"
|
||||
:max-count="3"
|
||||
:directory="true"
|
||||
accept="*"
|
||||
>
|
||||
<template #helpMessage="slotProps">
|
||||
<div class="mt-2 font-semibold text-green-500">
|
||||
自定义helpMessage: {{ JSON.stringify(slotProps) }}
|
||||
</div>
|
||||
</template>
|
||||
</FileUpload>
|
||||
</Card>
|
||||
|
||||
<Card title="自定义accept显示" size="small">
|
||||
<ImageUpload
|
||||
v-model:value="singleImageId"
|
||||
:accept-format="customAccept"
|
||||
/>
|
||||
<ImageUpload
|
||||
class="mt-6"
|
||||
v-model:value="singleImageId"
|
||||
accept-format="自定义显示允许的文件类型"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="默认在unMounted会取消上传" size="small">
|
||||
<div>将开发者工具调整网络为3G 切换挂载/卸载 可见请求在卸载被取消</div>
|
||||
挂载/卸载组件: <Switch v-model:checked="showComponent" />
|
||||
<FileUpload v-if="showComponent" v-model:value="singleFileId" />
|
||||
</Card>
|
||||
|
||||
<Card title="图片: listType控制上传样式" size="small">
|
||||
<RadioGroup
|
||||
v-model:value="currentImageListType"
|
||||
:options="imageListOptions"
|
||||
button-style="solid"
|
||||
option-type="button"
|
||||
/>
|
||||
<ImageUpload
|
||||
class="mt-2"
|
||||
v-model:value="singleImageId"
|
||||
:list-type="currentImageListType"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="文件: listType控制上传样式" size="small">
|
||||
<div class="mb-2 text-red-500">
|
||||
注意文件上传不支持`picture-card`类型
|
||||
</div>
|
||||
<div class="mb-2 text-red-500">
|
||||
注意不要中途切换list-type(应该仅作为初始化属性使用) 会导致样式计算问题
|
||||
helpMessage和文件会重叠
|
||||
</div>
|
||||
<RadioGroup
|
||||
v-model:value="currentFileListType"
|
||||
:options="fileListOptions"
|
||||
button-style="solid"
|
||||
option-type="button"
|
||||
/>
|
||||
<FileUpload
|
||||
class="mt-2"
|
||||
v-model:value="singleFileId"
|
||||
:list-type="currentFileListType"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="自定义缩略图和文件名" size="small">
|
||||
<FileUpload
|
||||
v-model:value="multipleFileId"
|
||||
:max-count="5"
|
||||
list-type="picture"
|
||||
:custom-filename="customName"
|
||||
:custom-thumb-url="customThumbnailUrl"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="图片上传的动画效果" size="small">
|
||||
<div class="my-2">
|
||||
是否启用
|
||||
<span class="font-semibold">list-type: picture-card</span> 的动画效果:
|
||||
<Switch v-model:checked="animationEnable" />
|
||||
</div>
|
||||
<ImageUpload
|
||||
v-model:value="singleImageId"
|
||||
:with-animation="animationEnable"
|
||||
/>
|
||||
当前绑定值: {{ singleImageId }}
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
1
apps/web-antd/src/views/演示使用自行删除/upload/insert.sql
Normal file
1
apps/web-antd/src/views/演示使用自行删除/upload/insert.sql
Normal file
@@ -0,0 +1 @@
|
||||
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1905430203187712002, '文件上传Demo', 0, 1000, 'upload_test', '演示使用自行删除/upload/index', NULL, 1, 0, 'C', '0', '0', NULL, '#', 103, 1, '2025-03-28 09:22:16', 1, '2025-03-28 09:22:16', '');
|
70
apps/web-antd/src/views/演示使用自行删除/upload/upload-modal.vue
Normal file
70
apps/web-antd/src/views/演示使用自行删除/upload/upload-modal.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue';
|
||||
|
||||
import { JsonPreview, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Modal, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
const [BasicForm, formApi] = useVbenForm({
|
||||
layout: 'vertical',
|
||||
schema: [
|
||||
{
|
||||
label: '图片上传多图',
|
||||
component: 'ImageUpload',
|
||||
fieldName: 'ossIds',
|
||||
componentProps: {
|
||||
maxCount: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '图片上传单图',
|
||||
component: 'ImageUpload',
|
||||
fieldName: 'ossId',
|
||||
componentProps: {
|
||||
maxCount: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
async function getValues() {
|
||||
try {
|
||||
const v = await formApi.getValues();
|
||||
console.log(v);
|
||||
|
||||
Modal.info({
|
||||
content: () => h(JsonPreview, { data: v }),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAssign() {
|
||||
const ids = ['1908761290673315841', '1907738568539332610'];
|
||||
await formApi.setValues({
|
||||
ossIds: ids,
|
||||
ossId: ids[0],
|
||||
});
|
||||
}
|
||||
|
||||
const [BasicModal] = useVbenModal({
|
||||
title: '上传',
|
||||
footer: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal>
|
||||
<div class="flex flex-col">
|
||||
<Space>
|
||||
<a-button @click="handleAssign">赋值</a-button>
|
||||
<a-button @click="getValues">获取值</a-button>
|
||||
</Space>
|
||||
<BasicForm />
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
32
apps/web-antd/src/views/演示使用自行删除/visit/api.ts
Normal file
32
apps/web-antd/src/views/演示使用自行删除/visit/api.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export interface Temp {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export function visitList() {
|
||||
return requestClient.get<Temp[]>('/monitor/logininfor/visitsMap');
|
||||
}
|
||||
|
||||
export function deviceInfoList() {
|
||||
return requestClient.get<Temp[]>('/monitor/logininfor/deviceInfoList');
|
||||
}
|
||||
|
||||
export function browserInfoList() {
|
||||
return requestClient.get<Temp[]>('/monitor/logininfor/browserInfoList');
|
||||
}
|
||||
|
||||
export function ispInfoList() {
|
||||
return requestClient.get<Temp[]>('/monitor/logininfor/ispInfoList');
|
||||
}
|
||||
|
||||
export interface LoginLineResp {
|
||||
date: string[];
|
||||
fail: number[];
|
||||
success: number[];
|
||||
}
|
||||
|
||||
export function loginLine() {
|
||||
return requestClient.get<LoginLineResp>('/monitor/logininfor/loginLine');
|
||||
}
|
103310
apps/web-antd/src/views/演示使用自行删除/visit/china.json
Normal file
103310
apps/web-antd/src/views/演示使用自行删除/visit/china.json
Normal file
File diff suppressed because it is too large
Load Diff
26
apps/web-antd/src/views/演示使用自行删除/visit/index.vue
Normal file
26
apps/web-antd/src/views/演示使用自行删除/visit/index.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Tabs } from 'ant-design-vue';
|
||||
|
||||
import Browser from './pages/browser.vue';
|
||||
import Device from './pages/device.vue';
|
||||
import Isp from './pages/isp.vue';
|
||||
import LoginLine from './pages/loginLine.vue';
|
||||
import VisitMap from './pages/map.vue';
|
||||
|
||||
const TabPane = Tabs.TabPane;
|
||||
|
||||
const activeKey = ref<number>(1);
|
||||
</script>
|
||||
<template>
|
||||
<div class="pt-[16px]">
|
||||
<Tabs v-model:activeKey="activeKey" class="h-full" tab-position="left">
|
||||
<TabPane :key="1" tab="访问量数据"> <VisitMap /> </TabPane>
|
||||
<TabPane :key="2" tab="使用设备"><Device /></TabPane>
|
||||
<TabPane :key="3" tab="使用浏览器"><Browser /></TabPane>
|
||||
<TabPane :key="4" tab="登录量"><LoginLine /></TabPane>
|
||||
<TabPane :key="5" tab="运营商占比"><Isp /></TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</template>
|
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/browser.vue
Normal file
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/browser.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
// import * as echarts from 'echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
|
||||
import { browserInfoList } from '../api';
|
||||
|
||||
defineOptions({ name: 'Browser' });
|
||||
|
||||
const browserRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(browserRef);
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await browserInfoList();
|
||||
const options: EChartsOption = {
|
||||
legend: {
|
||||
left: 'left',
|
||||
orient: 'vertical',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
shadowOffsetX: 0,
|
||||
},
|
||||
},
|
||||
// 百分比
|
||||
label: {
|
||||
formatter: '{b}: {c} - ({d}%)', // 自定义显示格式(b:name, c:value, d:百分比)
|
||||
show: true,
|
||||
},
|
||||
radius: '50%',
|
||||
type: 'pie',
|
||||
},
|
||||
],
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '使用浏览器占比',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
};
|
||||
renderEcharts(options);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EchartsUI ref="browserRef" height="720px" width="100%" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/device.vue
Normal file
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/device.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
// import * as echarts from 'echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
|
||||
import { deviceInfoList } from '../api';
|
||||
|
||||
defineOptions({ name: 'Device' });
|
||||
|
||||
const deviceRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(deviceRef);
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await deviceInfoList();
|
||||
const options: EChartsOption = {
|
||||
legend: {
|
||||
left: 'left',
|
||||
orient: 'vertical',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
shadowOffsetX: 0,
|
||||
},
|
||||
},
|
||||
// 百分比
|
||||
label: {
|
||||
formatter: '{b}: {c} - ({d}%)', // 自定义显示格式(b:name, c:value, d:百分比)
|
||||
show: true,
|
||||
},
|
||||
radius: '50%',
|
||||
type: 'pie',
|
||||
},
|
||||
],
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '使用设备占比',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
};
|
||||
renderEcharts(options);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EchartsUI ref="deviceRef" height="720px" width="100%" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/isp.vue
Normal file
62
apps/web-antd/src/views/演示使用自行删除/visit/pages/isp.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
// import * as echarts from 'echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
|
||||
import { ispInfoList } from '../api';
|
||||
|
||||
defineOptions({ name: 'Isp' });
|
||||
|
||||
const ispRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(ispRef);
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await ispInfoList();
|
||||
const options: EChartsOption = {
|
||||
legend: {
|
||||
left: 'left',
|
||||
orient: 'vertical',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
shadowOffsetX: 0,
|
||||
},
|
||||
},
|
||||
// 百分比
|
||||
label: {
|
||||
formatter: '{b}: {c} - ({d}%)', // 自定义显示格式(b:name, c:value, d:百分比)
|
||||
show: true,
|
||||
},
|
||||
radius: '50%',
|
||||
type: 'pie',
|
||||
},
|
||||
],
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '网络运营商占比',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
};
|
||||
renderEcharts(options);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EchartsUI ref="ispRef" height="720px" width="100%" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
83
apps/web-antd/src/views/演示使用自行删除/visit/pages/loginLine.vue
Normal file
83
apps/web-antd/src/views/演示使用自行删除/visit/pages/loginLine.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
// import * as echarts from 'echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
|
||||
import { loginLine } from '../api';
|
||||
|
||||
defineOptions({ name: 'LoginLine' });
|
||||
|
||||
const loginLineRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(loginLineRef);
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await loginLine();
|
||||
console.log(data);
|
||||
const options: EChartsOption = {
|
||||
legend: {},
|
||||
series: [
|
||||
{
|
||||
data: data.success,
|
||||
itemStyle: {
|
||||
color: '#3399CC',
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#3399CC',
|
||||
},
|
||||
name: '登录成功',
|
||||
type: 'line',
|
||||
},
|
||||
{
|
||||
data: data.fail,
|
||||
itemStyle: {
|
||||
color: '#CC6633',
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#CC6633',
|
||||
},
|
||||
name: '登录失败',
|
||||
type: 'line',
|
||||
},
|
||||
],
|
||||
title: {
|
||||
text: '近一月登录量统计',
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataView: { readOnly: true },
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none',
|
||||
},
|
||||
magicType: { type: ['line', 'bar'] },
|
||||
saveAsImage: {},
|
||||
},
|
||||
show: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
xAxis: {
|
||||
boundaryGap: false,
|
||||
data: data.date,
|
||||
type: 'category',
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
};
|
||||
renderEcharts(options);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EchartsUI ref="loginLineRef" height="720px" width="100%" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
111
apps/web-antd/src/views/演示使用自行删除/visit/pages/map.vue
Normal file
111
apps/web-antd/src/views/演示使用自行删除/visit/pages/map.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
// import * as echarts from 'echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
|
||||
import { type Temp, visitList } from '../api';
|
||||
import * as chinaMap from '../china.json';
|
||||
|
||||
defineOptions({ name: 'VisitMap' });
|
||||
|
||||
const mapRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(mapRef);
|
||||
|
||||
function transformData(data: Temp[]) {
|
||||
const nameList: string[] = chinaMap.features.map(
|
||||
(item) => item.properties.name,
|
||||
);
|
||||
// eslint-disable-next-line unicorn/prefer-set-has
|
||||
const dataNameList: string[] = data.map((item) => item.name);
|
||||
// 差集
|
||||
const diff = nameList.filter(
|
||||
(item) => !dataNameList.includes(item) && item.trim() !== '',
|
||||
);
|
||||
diff.forEach((name) => {
|
||||
data.push({
|
||||
name,
|
||||
value: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
echarts.registerMap('china', chinaMap as any);
|
||||
const data = await visitList();
|
||||
transformData(data);
|
||||
const max = Math.max.apply(
|
||||
null,
|
||||
data.map((item) => item.value),
|
||||
);
|
||||
const options: EChartsOption = {
|
||||
series: [
|
||||
{
|
||||
data,
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
label: {
|
||||
// formatter: '{b}\n{c}',
|
||||
formatter: '{c}',
|
||||
position: 'inside',
|
||||
show: true,
|
||||
},
|
||||
map: 'china',
|
||||
roam: true,
|
||||
// 由于缩放 这里加上偏移
|
||||
top: 200,
|
||||
type: 'map',
|
||||
zoom: 1.5,
|
||||
},
|
||||
],
|
||||
title: {
|
||||
left: 'right',
|
||||
text: '用户访问量数据',
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataView: { readOnly: true },
|
||||
saveAsImage: {},
|
||||
},
|
||||
// orient: 'vertical',
|
||||
left: 'left',
|
||||
show: true,
|
||||
top: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '{b}<br/>{c}',
|
||||
showDelay: 0,
|
||||
transitionDuration: 0.2,
|
||||
trigger: 'item',
|
||||
},
|
||||
visualMap: {
|
||||
calculable: true,
|
||||
inRange: {
|
||||
color: ['#ffffff', '#00FF66', '#00CCFF', '#CC6600'],
|
||||
},
|
||||
left: 'left',
|
||||
max,
|
||||
min: 0,
|
||||
text: ['最高', '最低'],
|
||||
},
|
||||
};
|
||||
renderEcharts(options);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EchartsUI ref="mapRef" height="720px" width="100%" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
229
apps/web-antd/src/views/演示使用自行删除/vxe/edit-table.vue
Normal file
229
apps/web-antd/src/views/演示使用自行删除/vxe/edit-table.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<script setup lang="tsx">
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
|
||||
import { JsonPreview } from '@vben/common-ui';
|
||||
import { getPopupContainer } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputNumber,
|
||||
message,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
editConfig: {
|
||||
// 触发编辑的方式
|
||||
trigger: 'click',
|
||||
// 触发编辑的模式
|
||||
mode: 'row',
|
||||
showStatus: true,
|
||||
},
|
||||
border: true,
|
||||
rowConfig: {
|
||||
drag: true,
|
||||
},
|
||||
checkboxConfig: {},
|
||||
editRules: {
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
age: [
|
||||
{ required: true, message: '请输入年龄', trigger: 'blur' },
|
||||
{ min: 0, max: 200, message: '年龄必须为1-200' },
|
||||
],
|
||||
job: [{ required: true, message: '请选择工作', trigger: 'blur' }],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
type: 'checkbox',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
dragSort: true,
|
||||
title: '排序',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '姓名',
|
||||
align: 'left',
|
||||
editRender: {},
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
if (!row.name) {
|
||||
return <span class="text-red-500">未填写</span>;
|
||||
}
|
||||
return <span>{row.name}</span>;
|
||||
},
|
||||
edit: ({ row }) => {
|
||||
return <Input placeholder={'请输入'} v-model:value={row.name} />;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'age',
|
||||
title: '年龄',
|
||||
align: 'left',
|
||||
editRender: {},
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
if (!row.age) {
|
||||
return <span class="text-red-500">未填写</span>;
|
||||
}
|
||||
return <span>{row.age}</span>;
|
||||
},
|
||||
edit: ({ row }) => {
|
||||
return (
|
||||
<InputNumber
|
||||
class="w-full"
|
||||
placeholder={'请输入'}
|
||||
v-model:value={row.age}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: '工作',
|
||||
title: 'job',
|
||||
align: 'left',
|
||||
editRender: {},
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
if (!row.job) {
|
||||
return <span class="text-red-500">未选择</span>;
|
||||
}
|
||||
return <span>{row.job}</span>;
|
||||
},
|
||||
edit: ({ row }) => {
|
||||
const options = ['前端佬', '后端佬', '组长'].map((item) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
}));
|
||||
return (
|
||||
<Select
|
||||
class="w-full"
|
||||
getPopupContainer={getPopupContainer}
|
||||
options={options}
|
||||
placeholder={'请选择'}
|
||||
v-model:value={row.job}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作',
|
||||
width: 100,
|
||||
slots: {
|
||||
default: ({ $table, row }) => {
|
||||
function handleDelete() {
|
||||
$table.remove(row);
|
||||
}
|
||||
return (
|
||||
<Button danger={true} onClick={handleDelete} size={'small'}>
|
||||
删除
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const data = [
|
||||
{
|
||||
name: '张三',
|
||||
age: 18,
|
||||
job: '前端佬',
|
||||
},
|
||||
{
|
||||
name: '李四',
|
||||
age: 19,
|
||||
job: '后端佬',
|
||||
},
|
||||
{
|
||||
name: '王五',
|
||||
age: 20,
|
||||
job: '组长',
|
||||
},
|
||||
];
|
||||
await nextTick();
|
||||
await tableApi.grid.loadData(data);
|
||||
});
|
||||
async function handleAdd() {
|
||||
const record = { name: '', age: undefined, job: undefined };
|
||||
const { row: newRow } = await tableApi.grid.insert(record);
|
||||
await tableApi.grid.setEditCell(newRow, 'name');
|
||||
}
|
||||
|
||||
async function handleRemove() {
|
||||
await tableApi.grid.removeCheckboxRow();
|
||||
}
|
||||
|
||||
async function handleValidate() {
|
||||
const result = await tableApi.grid.validate(true);
|
||||
if (result) {
|
||||
message.error('校验失败');
|
||||
} else {
|
||||
message.success('校验成功');
|
||||
}
|
||||
}
|
||||
|
||||
function getData() {
|
||||
const data = tableApi.grid.getTableData();
|
||||
const { fullData } = data;
|
||||
console.log(fullData);
|
||||
Modal.info({
|
||||
title: '提示',
|
||||
content: (
|
||||
<div class="max-h-[350px] overflow-y-auto">
|
||||
<JsonPreview data={fullData} />
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicTable>
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="getData">获取表格数据</a-button>
|
||||
<a-button @click="handleValidate">校验</a-button>
|
||||
<a-button danger @click="handleRemove"> 删除勾选 </a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-access:code="['system:config:add']"
|
||||
@click="handleAdd"
|
||||
>
|
||||
{{ $t('pages.common.add') }}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</template>
|
17
apps/web-antd/src/views/演示使用自行删除/vxe/index.vue
Normal file
17
apps/web-antd/src/views/演示使用自行删除/vxe/index.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
|
||||
import EditTable from './edit-table.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<div class="flex flex-col gap-4">
|
||||
<Card title="可编辑表格" size="small">
|
||||
<EditTable class="h-[500px]" />
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
1
apps/web-antd/src/views/演示使用自行删除/wechat/img-base64.txt
Normal file
1
apps/web-antd/src/views/演示使用自行删除/wechat/img-base64.txt
Normal file
File diff suppressed because one or more lines are too long
23
apps/web-antd/src/views/演示使用自行删除/wechat/index.vue
Normal file
23
apps/web-antd/src/views/演示使用自行删除/wechat/index.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Image } from 'ant-design-vue';
|
||||
|
||||
import wechatGroupImg from './img-base64.txt?raw';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<div class="flex w-[360px] flex-col gap-2">
|
||||
<!-- <Alert :show-icon="true" message="人数已满,需要手动拉人" type="info" /> -->
|
||||
<div>
|
||||
<Image
|
||||
:preview="false"
|
||||
:src="wechatGroupImg"
|
||||
:width="240"
|
||||
class="pointer-events-none select-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
Reference in New Issue
Block a user