初始版本提交

This commit is contained in:
yaoyn
2024-02-05 09:15:37 +08:00
parent b52d4414be
commit 445292105f
1848 changed files with 236859 additions and 75 deletions

View File

@ -0,0 +1,13 @@
<template>
<PageWrapper title="层级面包屑示例" content="子级页面面包屑会添加到当前层级后面">
<router-link to="/feat/breadcrumb/children/childrenDetail"> 进入子级详情页 </router-link>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { PageWrapper },
});
</script>

View File

@ -0,0 +1,10 @@
<template>
<PageWrapper title="子级详情页">
<div>子级详情页内容在此</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({ components: { PageWrapper } });
</script>

View File

@ -0,0 +1,13 @@
<template>
<PageWrapper title="平级面包屑示例" content="子级页面面包屑会覆盖当前层级">
<router-link to="/feat/breadcrumb/flatDetail"> 进入平级详情页 </router-link>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { PageWrapper },
});
</script>

View File

@ -0,0 +1,8 @@
<template>
<div>平级详情页</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>

View File

@ -0,0 +1,43 @@
<template>
<PageWrapper title="点内外部触发事件">
<ClickOutSide @click-outside="handleClickOutside" class="flex justify-center">
<div @click="innerClick" class="demo-box">
{{ text }}
</div>
</ClickOutSide>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { ClickOutSide } from '/@/components/ClickOutSide';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { ClickOutSide, PageWrapper },
setup() {
const text = ref('Click');
function handleClickOutside() {
text.value = 'Click Out Side';
}
function innerClick() {
text.value = 'Click Inner';
}
return { innerClick, handleClickOutside, text };
},
});
</script>
<style lang="less" scoped>
.demo-box {
display: flex;
width: 100%;
height: 300px;
font-size: 24px;
color: #fff;
background-color: #408ede;
border-radius: 10px;
justify-content: center;
align-items: center;
}
</style>

View File

@ -0,0 +1,85 @@
<template>
<PageWrapper title="右键菜单示例">
<CollapseContainer title="Simple">
<a-button type="primary" @contextmenu="handleContext"> Right Click on me </a-button>
</CollapseContainer>
<CollapseContainer title="Multiple" class="mt-4">
<a-button type="primary" @contextmenu="handleMultipleContext"> Right Click on me </a-button>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useContextMenu } from '/@/hooks/web/useContextMenu';
import { CollapseContainer } from '/@/components/Container';
import { useMessage } from '/@/hooks/web/useMessage';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { CollapseContainer, PageWrapper },
setup() {
const [createContextMenu] = useContextMenu();
const { createMessage } = useMessage();
function handleContext(e: MouseEvent) {
createContextMenu({
event: e,
items: [
{
label: 'New',
icon: 'bi:plus',
handler: () => {
createMessage.success('click new');
},
},
{
label: 'Open',
icon: 'bx:bxs-folder-open',
handler: () => {
createMessage.success('click open');
},
},
],
});
}
function handleMultipleContext(e: MouseEvent) {
createContextMenu({
event: e,
items: [
{
label: 'New',
icon: 'bi:plus',
children: [
{
label: 'New1-1',
icon: 'bi:plus',
divider: true,
children: [
{
label: 'New1-1-1',
handler: () => {
createMessage.success('click new');
},
},
{
label: 'New1-2-1',
disabled: true,
},
],
},
{
label: 'New1-2',
icon: 'bi:plus',
},
],
},
],
});
}
return { handleContext, handleMultipleContext };
},
});
</script>

View File

@ -0,0 +1,40 @@
<template>
<PageWrapper title="文本复制示例">
<CollapseContainer class="w-full h-32 bg-white rounded-md" title="Copy Example">
<div class="flex justify-center">
<a-input placeholder="请输入" v-model:value="value" />
<a-button type="primary" @click="handleCopy"> Copy </a-button>
</div>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, unref, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
import { useMessage } from '/@/hooks/web/useMessage';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
name: 'Copy',
components: { CollapseContainer, PageWrapper },
setup() {
const valueRef = ref('');
const { createMessage } = useMessage();
const { clipboardRef, copiedRef } = useCopyToClipboard();
function handleCopy() {
const value = unref(valueRef);
if (!value) {
createMessage.warning('请输入要拷贝的内容!');
return;
}
clipboardRef.value = value;
if (unref(copiedRef)) {
createMessage.warning('copy success');
}
}
return { handleCopy, value: valueRef };
},
});
</script>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,66 @@
<template>
<PageWrapper title="文件下载示例">
<a-alert message="根据后台接口文件流下载" />
<a-button type="primary" class="my-4" @click="handleDownByData"> 文件流下载 </a-button>
<a-alert message="根据文件地址下载文件" />
<a-button type="primary" class="my-4" @click="handleDownloadByUrl"> 文件地址下载 </a-button>
<a-alert message="base64流下载" />
<a-button type="primary" class="my-4" @click="handleDownloadByBase64"> base64流下载 </a-button>
<a-alert message="图片Url下载,如果有跨域问题,需要处理图片跨域" />
<a-button type="primary" class="my-4" @click="handleDownloadByOnlineUrl">
图片Url下载
</a-button>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {
downloadByUrl,
downloadByData,
downloadByBase64,
downloadByOnlineUrl,
} from '/@/utils/file/download';
import imgBase64 from './imgBase64';
import { PageWrapper } from '/@/components/Page';
import { Alert } from 'ant-design-vue';
export default defineComponent({
components: { PageWrapper, [Alert.name]: Alert },
setup() {
function handleDownByData() {
downloadByData('text content', 'testName.txt');
}
function handleDownloadByUrl() {
downloadByUrl({
url: 'https://codeload.github.com/anncwb/vue-vben-admin-doc/zip/master',
target: '_self',
});
downloadByUrl({
url: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png',
target: '_self',
});
}
function handleDownloadByBase64() {
downloadByBase64(imgBase64, 'logo.png');
}
function handleDownloadByOnlineUrl() {
downloadByOnlineUrl(
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5944817f47b8408e9f1442ece49d68ca~tplv-k3u1fbpfcp-watermark.image',
'logo.png',
);
}
return {
handleDownloadByUrl,
handleDownByData,
handleDownloadByBase64,
handleDownloadByOnlineUrl,
};
},
});
</script>

View File

@ -0,0 +1,48 @@
<template>
<PageWrapper title="全屏示例">
<CollapseContainer class="w-full h-32 bg-white rounded-md" title="Window Full Screen">
<a-button type="primary" @click="enter" class="mr-2"> Enter Window Full Screen </a-button>
<a-button color="success" @click="toggle" class="mr-2"> Toggle Window Full Screen </a-button>
<a-button color="error" @click="exit" class="mr-2"> Exit Window Full Screen </a-button>
Current State: {{ isFullscreen }}
</CollapseContainer>
<CollapseContainer class="w-full mt-5 bg-white rounded-md" title="Dom Full Screen">
<a-button type="primary" @click="toggleDom" class="mr-2"> Enter Dom Full Screen </a-button>
</CollapseContainer>
<div
ref="domRef"
class="flex items-center justify-center w-1/2 h-64 mx-auto mt-10 bg-white rounded-md"
>
<a-button type="primary" @click="toggleDom" class="mr-2"> Exit Dom Full Screen </a-button>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useFullscreen } from '@vueuse/core';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { CollapseContainer, PageWrapper },
setup() {
const domRef = ref<Nullable<HTMLElement>>(null);
const { enter, toggle, exit, isFullscreen } = useFullscreen();
const { toggle: toggleDom } = useFullscreen(domRef);
return {
enter,
toggleDom,
toggle,
isFullscreen,
exit,
domRef,
};
},
});
</script>

View File

@ -0,0 +1,96 @@
<template>
<PageWrapper title="Icon组件示例">
<CollapseContainer title="Antv Icon使用 (直接按需引入相应组件即可)">
<div class="flex justify-around">
<GithubFilled :style="{ fontSize: '30px' }" />
<QqCircleFilled :style="{ fontSize: '30px' }" />
<WechatFilled :style="{ fontSize: '30px' }" />
<AlipayCircleFilled :style="{ fontSize: '30px' }" />
<IeCircleFilled :style="{ fontSize: '30px' }" />
<TaobaoCircleFilled :style="{ fontSize: '30px' }" />
<CodepenCircleFilled :style="{ fontSize: '30px' }" />
</div>
</CollapseContainer>
<CollapseContainer title="IconIfy 组件使用" class="my-5">
<div class="flex justify-around flex-wrap">
<Icon icon="ion:layers-outline" :size="30" />
<Icon icon="ion:bar-chart-outline" :size="30" />
<Icon icon="ion:tv-outline" :size="30" />
<Icon icon="ion:settings-outline" :size="30" />
</div>
</CollapseContainer>
<CollapseContainer title="svg 雪碧图" class="my-5">
<div class="flex justify-around flex-wrap">
<SvgIcon name="test" size="32" />
<template v-for="item in 6" :key="item">
<SvgIcon :name="`dynamic-avatar-${item}`" size="32" />
</template>
</div>
</CollapseContainer>
<CollapseContainer title="图标选择器(Iconify)" class="my-5">
<div class="flex justify-around flex-wrap">
<IconPicker />
</div>
</CollapseContainer>
<CollapseContainer title="图标选择器(Svg)" class="my-5">
<div class="flex justify-around flex-wrap">
<IconPicker mode="svg" />
</div>
</CollapseContainer>
<Alert
show-icon
message="推荐使用Iconify组件"
description="Icon组件基本包含所有的图标,在下面网址内你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。"
/>
<a-button type="link" @click="toIconify"> Iconify 图标大全 </a-button>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { Alert } from 'ant-design-vue';
import {
QqCircleFilled,
GithubFilled,
WechatFilled,
AlipayCircleFilled,
IeCircleFilled,
TaobaoCircleFilled,
CodepenCircleFilled,
} from '@ant-design/icons-vue';
import { Icon, IconPicker, SvgIcon } from '/@/components/Icon/index';
import { openWindow } from '/@/utils';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: {
PageWrapper,
CollapseContainer,
GithubFilled,
QqCircleFilled,
WechatFilled,
AlipayCircleFilled,
IeCircleFilled,
TaobaoCircleFilled,
CodepenCircleFilled,
Icon,
Alert,
IconPicker,
SvgIcon,
},
setup() {
return {
toIconify: () => {
openWindow('https://iconify.design/');
},
};
},
});
</script>

View File

@ -0,0 +1,31 @@
<template>
<PageWrapper title="图片预览示例">
<ImagePreview :imageList="imgList" />
<a-button @click="openImg" type="primary">无预览图</a-button>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { createImgPreview, ImagePreview } from '/@/components/Preview/index';
import { PageWrapper } from '/@/components/Page';
// import { PreviewActions } from '/@/components/Preview/src/typing';
const imgList: string[] = [
'https://picsum.photos/id/66/346/216',
'https://picsum.photos/id/67/346/216',
'https://picsum.photos/id/68/346/216',
];
export default defineComponent({
components: { PageWrapper, ImagePreview },
setup() {
function openImg() {
const onImgLoad = ({ index, url, dom }) => {
console.log(`${index + 1}张图片已加载URL为${url}`, dom);
};
// 可以使用createImgPreview返回的 PreviewActions 来控制预览逻辑,实现类似幻灯片、自动旋转之类的骚操作
createImgPreview({ imageList: imgList, defaultWidth: 700, rememberState: true, onImgLoad });
}
return { imgList, openImg };
},
});
</script>

View File

@ -0,0 +1,42 @@
<template>
<PageWrapper title="带参数菜单(路由)" content="支持多级参数">
当前参数{{ params }}
<br />
输入参数切换路由
<Input v-model:value="value" placeholder="建议为url标准字符输入后点击切换" />
<a-button type="primary" @click="handleClickGo">切换路由</a-button>
<br />
切换路由后
<ul>
<li>可刷新页面测试路由参数情况是否正常</li>
<li>可于左侧菜单中展开子菜单点击测试参数是否携带正常</li>
</ul>
</PageWrapper>
</template>
<script lang="ts">
import { Input } from 'ant-design-vue';
import { computed, defineComponent, ref, unref } from 'vue';
import { useRouter } from 'vue-router';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
name: 'TestMenu',
components: { PageWrapper, Input },
setup() {
const { currentRoute, replace } = useRouter();
const value = ref<string>('');
const handleClickGo = () => {
const { name } = unref(currentRoute);
replace({ name: name!, params: { id: unref(value) } });
};
return {
value,
handleClickGo,
params: computed(() => {
return unref(currentRoute).params;
}),
};
},
});
</script>

View File

@ -0,0 +1,100 @@
<template>
<PageWrapper title="消息示例">
<CollapseContainer class="w-full h-32 bg-white rounded-md" title="Message">
<a-button @click="infoMsg('Info message')" class="mr-2"> Info </a-button>
<a-button @click="successMsg('Success message')" class="mr-2" color="success">
Success
</a-button>
<a-button @click="warningMsg('Warning message')" class="mr-2" color="warning">
Warning
</a-button>
<a-button @click="errorMsg('Error message')" class="mr-2" color="error"> Error </a-button>
<a-button @click="handleLoading" class="mr-2" type="primary"> Loading </a-button>
</CollapseContainer>
<CollapseContainer class="w-full h-32 mt-5 bg-white rounded-md" title="Comfirm">
<a-button @click="handleConfirm('info')" class="mr-2"> Info </a-button>
<a-button @click="handleConfirm('warning')" color="warning" class="mr-2"> Warning </a-button>
<a-button @click="handleConfirm('success')" color="success" class="mr-2"> Success </a-button>
<a-button @click="handleConfirm('error')" color="error" class="mr-2"> Error </a-button>
</CollapseContainer>
<CollapseContainer class="w-full h-32 mt-5 bg-white rounded-md" title="Modal">
<a-button @click="handleInfoModal" class="mr-2"> Info </a-button>
<a-button @click="handleSuccessModal" color="success" class="mr-2"> Success </a-button>
<a-button @click="handleErrorModal" color="error" class="mr-2"> Error </a-button>
<a-button @click="handleWarningModal" color="warning" class="mr-2"> Warning </a-button>
</CollapseContainer>
<CollapseContainer
class="w-full h-32 mt-5 bg-white rounded-md"
title="Notification 用法与上面一致"
>
<a-button @click="handleNotify" color="success" class="mr-2"> Success </a-button>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { CollapseContainer, PageWrapper },
setup() {
const {
createMessage,
createConfirm,
createSuccessModal,
createInfoModal,
createErrorModal,
createWarningModal,
notification,
} = useMessage();
const { info, success, warning, error } = createMessage;
function handleLoading() {
createMessage.loading('Loading...');
}
function handleConfirm(type: 'warning' | 'error' | 'success' | 'info') {
createConfirm({
iconType: type,
title: 'Tip',
content: 'content message...',
});
}
function handleSuccessModal() {
createSuccessModal({ title: 'Tip', content: 'content message...' });
}
function handleErrorModal() {
createErrorModal({ title: 'Tip', content: 'content message...' });
}
function handleWarningModal() {
createWarningModal({ title: 'Tip', content: 'content message...' });
}
function handleInfoModal() {
createInfoModal({ title: 'Tip', content: 'content message...' });
}
function handleNotify() {
notification.success({
message: 'Tip',
description: 'content message...',
});
}
return {
infoMsg: info,
successMsg: success,
warningMsg: warning,
errorMsg: error,
handleLoading,
handleConfirm,
handleSuccessModal,
handleErrorModal,
handleWarningModal,
handleInfoModal,
handleNotify,
};
},
});
</script>

View File

@ -0,0 +1,49 @@
<template>
<PageWrapper title="打印示例">
<CollapseContainer title="json打印表格">
<a-button type="primary" @click="jsonPrint">打印</a-button>
</CollapseContainer>
<a-button type="primary" class="mt-5" @click="imagePrint">Image Print</a-button>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { CollapseContainer } from '/@/components/Container/index';
import printJS from 'print-js';
export default defineComponent({
name: 'AppLogo',
components: { PageWrapper, CollapseContainer },
setup() {
function jsonPrint() {
printJS({
printable: [
{ name: 'll', email: '123@gmail.com', phone: '123' },
{ name: 'qq', email: '456@gmail.com', phone: '456' },
],
properties: ['name', 'email', 'phone'],
type: 'json',
});
}
function imagePrint() {
printJS({
printable: [
'https://anncwb.github.io/anncwb/images/preview1.png',
'https://anncwb.github.io/anncwb/images/preview2.png',
],
type: 'image',
header: 'Multiple Images',
imageStyle: 'width:100%;',
});
}
return {
jsonPrint,
imagePrint,
};
},
});
</script>

View File

@ -0,0 +1,23 @@
<template>
<div class="request-box">
<a-button @click="handleClick" type="primary"> 点击会重新发起请求5次 </a-button>
<p>打开浏览器的network面板可以看到发出了六次请求</p>
</div>
</template>
<script lang="ts" setup>
import { testRetry } from '/@/api/system/login';
// @ts-ignore
const handleClick = async () => {
await testRetry();
};
</script>
<style lang="less">
.request-box {
margin: 50px;
}
p {
margin-top: 10px;
}
</style>

View File

@ -0,0 +1,31 @@
<template>
<PageWrapper title="Ripple示例">
<div class="demo-box" v-ripple>content</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import RippleDirective from '/@/directives/ripple';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { PageWrapper },
directives: {
Ripple: RippleDirective,
},
});
</script>
<style lang="less" scoped>
.demo-box {
display: flex;
width: 300px;
height: 300px;
font-size: 24px;
color: #fff;
background-color: #408ede;
border-radius: 10px;
justify-content: center;
align-items: center;
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<PageWrapper
title="登录过期示例"
content="用户登录过期示例,不再跳转登录页,直接生成页面覆盖当前页面,方便保持过期前的用户状态!"
>
<a-card title="请点击下面的按钮访问测试接口" extra="所访问的接口会返回Token过期响应">
<a-card-grid style="width: 50%; text-align: center">
<a-button type="primary" @click="test1">HttpStatus == 401</a-button>
</a-card-grid>
<a-card-grid style="width: 50%; text-align: center">
<span></span>
<a-button class="ml-4" type="primary" @click="test2">Response.code == 401</a-button>
</a-card-grid>
</a-card>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { useUserStore } from '/@/store/modules/user';
import { sessionTimeoutApi, tokenExpiredApi } from '/@/api/demo/account';
import { Card } from 'ant-design-vue';
export default defineComponent({
name: 'TestSessionTimeout',
components: { ACardGrid: Card.Grid, ACard: Card, PageWrapper },
setup() {
const userStore = useUserStore();
async function test1() {
// 示例网站生产环境用的是mock数据不能返回Http状态码
// 所以在生产环境直接改变状态来达到测试效果
if (import.meta.env.PROD) {
userStore.setToken(undefined);
userStore.setSessionTimeout(true);
} else {
// 这个api会返回状态码为401的响应
await sessionTimeoutApi();
}
}
async function test2() {
// 这个api会返回code为401的json数据Http状态码为200
try {
await tokenExpiredApi();
} catch (err) {
console.log('接口访问错误:', (err as Error).message || '错误');
}
}
return { test1, test2 };
},
});
</script>

View File

@ -0,0 +1,27 @@
<template>
<PageWrapper title="带参数标签页" content="支持带参数多tab缓存">
Current Param : {{ params }}
<br />
Keep Alive
<Input />
</PageWrapper>
</template>
<script lang="ts">
import { computed, defineComponent, unref } from 'vue';
import { useRouter } from 'vue-router';
import { PageWrapper } from '/@/components/Page';
import { Input } from 'ant-design-vue';
export default defineComponent({
name: 'TestTab',
components: { PageWrapper, Input },
setup() {
const { currentRoute } = useRouter();
return {
params: computed(() => {
return unref(currentRoute).params;
}),
};
},
});
</script>

View File

@ -0,0 +1,28 @@
<template>
<PageWrapper title="Tab详情页面">
<div>{{ index }} - 详情页内容在此</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { useTabs } from '/@/hooks/web/useTabs';
import { useRoute } from 'vue-router';
export default defineComponent({
name: 'TabDetail',
components: { PageWrapper },
setup() {
const route = useRoute();
const index = route.params?.id ?? -1;
const { setTitle } = useTabs();
// 设置标识
setTitle(`No.${index} - 详情信息`);
return {
index,
};
},
});
</script>

View File

@ -0,0 +1,69 @@
<template>
<PageWrapper title="标签页操作示例">
<CollapseContainer title="在下面输入框输入文本,切换后回来内容会保存">
<a-alert banner message="该操作不会影响页面标题仅修改Tab标题" />
<div class="mt-2 flex flex-grow-0">
<a-button class="mr-2" @click="setTabTitle" type="primary"> 设置Tab标题 </a-button>
<a-input placeholder="请输入" v-model:value="title" class="mr-4 w-12" />
</div>
</CollapseContainer>
<CollapseContainer class="mt-4" title="标签页操作">
<a-button class="mr-2" @click="closeAll"> 关闭所有 </a-button>
<a-button class="mr-2" @click="closeLeft"> 关闭左侧 </a-button>
<a-button class="mr-2" @click="closeRight"> 关闭右侧 </a-button>
<a-button class="mr-2" @click="closeOther"> 关闭其他 </a-button>
<a-button class="mr-2" @click="closeCurrent"> 关闭当前 </a-button>
<a-button class="mr-2" @click="refreshPage"> 刷新当前 </a-button>
</CollapseContainer>
<CollapseContainer class="mt-4" title="标签页复用超出限制自动关闭(使用场景: 动态路由)">
<a-button v-for="index in 6" :key="index" class="mr-2" @click="toDetail(index)">
打开{{ index }}详情页
</a-button>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container';
import { useTabs } from '/@/hooks/web/useTabs';
import { PageWrapper } from '/@/components/Page';
import { Input, Alert } from 'ant-design-vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { useGo } from '/@/hooks/web/usePage';
export default defineComponent({
name: 'TabsDemo',
components: { CollapseContainer, PageWrapper, [Input.name]: Input, [Alert.name]: Alert },
setup() {
const go = useGo();
const title = ref<string>('');
const { closeAll, closeLeft, closeRight, closeOther, closeCurrent, refreshPage, setTitle } =
useTabs();
const { createMessage } = useMessage();
function setTabTitle() {
if (title.value) {
setTitle(title.value);
} else {
createMessage.error('请输入要设置的Tab标题');
}
}
function toDetail(index: number) {
go(`/feat/tabs/detail/${index}`);
}
return {
closeAll,
closeLeft,
closeRight,
closeOther,
closeCurrent,
toDetail,
refreshPage,
setTabTitle,
title,
};
},
});
</script>

View File

@ -0,0 +1,32 @@
<template>
<PageWrapper title="水印示例">
<CollapseContainer class="w-full h-32 bg-white rounded-md" title="Global WaterMark">
<a-button type="primary" class="mr-2" @click="setWatermark('WaterMark Info')">
Create
</a-button>
<a-button color="error" class="mr-2" @click="clear"> Clear </a-button>
<a-button color="warning" class="mr-2" @click="setWatermark('WaterMark Info New')">
Reset
</a-button>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import { useWatermark } from '/@/hooks/web/useWatermark';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { CollapseContainer, PageWrapper },
setup() {
const areaRef = ref<Nullable<HTMLElement>>(null);
const { setWatermark, clear } = useWatermark();
return {
setWatermark,
clear,
areaRef,
};
},
});
</script>

View File

@ -0,0 +1,127 @@
<template>
<PageWrapper title="WebSocket 示例">
<div class="flex">
<div class="w-1/3 bg-white p-4">
<div class="flex items-center">
<span class="text-lg font-medium mr-4"> 连接状态: </span>
<Tag :color="getTagColor">{{ status }}</Tag>
</div>
<hr class="my-4" />
<div class="flex">
<a-input v-model:value="server" disabled>
<template #addonBefore> 服务地址 </template>
</a-input>
<a-button :type="getIsOpen ? 'danger' : 'primary'" @click="toggle">
{{ getIsOpen ? '关闭连接' : '开启连接' }}
</a-button>
</div>
<p class="text-lg font-medium mt-4">设置</p>
<hr class="my-4" />
<InputTextArea
placeholder="需要发送到服务器的内容"
:disabled="!getIsOpen"
v-model:value="sendValue"
allowClear
/>
<a-button type="primary" block class="mt-4" :disabled="!getIsOpen" @click="handlerSend">
发送
</a-button>
</div>
<div class="w-2/3 bg-white ml-4 p-4">
<span class="text-lg font-medium mr-4"> 消息记录: </span>
<hr class="my-4" />
<div class="max-h-80 overflow-auto">
<ul>
<li v-for="item in getList" class="mt-2" :key="item.time">
<div class="flex items-center">
<span class="mr-2 text-primary font-medium">收到消息:</span>
<span>{{ formatToDateTime(item.time) }}</span>
</div>
<div>
{{ item.res }}
</div>
</li>
</ul>
</div>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, reactive, watchEffect, computed, toRefs } from 'vue';
import { Tag, Input } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
import { useWebSocket } from '@vueuse/core';
import { formatToDateTime } from '/@/utils/dateUtil';
export default defineComponent({
components: {
PageWrapper,
[Input.name]: Input,
InputTextArea: Input.TextArea,
Tag,
},
setup() {
const state = reactive({
server: 'ws://localhost:3300/test',
sendValue: '',
recordList: [] as { id: number; time: number; res: string }[],
});
const { status, data, send, close, open } = useWebSocket(state.server, {
autoReconnect: false,
heartbeat: true,
});
watchEffect(() => {
if (data.value) {
try {
const res = JSON.parse(data.value);
state.recordList.push(res);
} catch (error) {
state.recordList.push({
res: data.value,
id: Math.ceil(Math.random() * 1000),
time: new Date().getTime(),
});
}
}
});
const getIsOpen = computed(() => status.value === 'OPEN');
const getTagColor = computed(() => (getIsOpen.value ? 'success' : 'red'));
const getList = computed(() => {
return [...state.recordList].reverse();
});
function handlerSend() {
send(state.sendValue);
state.sendValue = '';
}
function toggle() {
if (getIsOpen.value) {
close();
} else {
open();
}
}
return {
status,
formatToDateTime,
...toRefs(state),
handlerSend,
getList,
toggle,
getIsOpen,
getTagColor,
};
},
});
</script>