初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
@@ -0,0 +1,136 @@
<template>
<div class="page-content">
<ElButton @contextmenu.prevent="showMenu"> 右键触发菜单 </ElButton>
<!-- 右键菜单组件 -->
<ArtMenuRight
ref="menuRef"
:menu-items="menuItems"
:menu-width="180"
:submenu-width="140"
:border-radius="10"
@select="handleSelect"
@show="onMenuShow"
@hide="onMenuHide"
/>
</div>
</template>
<script setup lang="ts">
import { computed, nextTick } from 'vue'
import ArtMenuRight from '@/components/core/others/art-menu-right/index.vue'
import type { MenuItemType } from '@/components/core/others/art-menu-right/index.vue'
defineOptions({ name: 'TemplateContextMenu' })
const menuRef = ref<InstanceType<typeof ArtMenuRight>>()
const lastAction = ref('')
/**
* 右键菜单选项配置
*/
const menuItems = computed((): MenuItemType[] => [
{
key: 'copy',
label: '复制',
icon: 'ri:file-copy-line'
},
{
key: 'paste',
label: '粘贴',
icon: 'ri:capsule-line'
},
{
key: 'cut',
label: '剪切',
icon: 'ri:clipboard-line',
showLine: true
},
{
key: 'export',
label: '导出选项',
icon: 'ri:export-line',
children: [
{
key: 'exportExcel',
label: '导出 Excel',
icon: 'ri:file-excel-2-line'
},
{
key: 'exportPdf',
label: '导出 PDF',
icon: 'ri:file-pdf-2-line'
}
]
},
{
key: 'edit',
label: '编辑选项',
icon: 'ri:edit-2-line',
children: [
{
key: 'rename',
label: '重命名'
},
{
key: 'duplicate',
label: '复制副本'
}
]
},
{
key: 'share',
label: '分享',
icon: 'ri:share-forward-line',
showLine: true
},
{
key: 'delete',
label: '删除',
icon: 'ri:delete-bin-line'
},
{
key: 'disabled',
label: '禁用选项',
icon: 'ri:close-circle-line',
disabled: true
}
])
/**
* 处理菜单项选择
* @param item 选中的菜单项
*/
const handleSelect = (item: MenuItemType) => {
lastAction.value = `${item.label} (${item.key})`
ElMessage.success(`执行操作: ${item.label}`)
console.log('选择了菜单项:', item)
}
/**
* 显示右键菜单
* @param e 鼠标事件
*/
const showMenu = (e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
nextTick(() => {
menuRef.value?.show(e)
})
}
/**
* 菜单显示回调
*/
const onMenuShow = () => {
console.log('菜单显示')
}
/**
* 菜单隐藏回调
*/
const onMenuHide = () => {
console.log('菜单隐藏')
}
</script>
@@ -0,0 +1,175 @@
<template>
<div class="page-content mb-5">
<div class="mb-15 text-center">
<h1 class="my-4 text-2xl font-semibold leading-tight"
>基于 VueUse useTransition 的 Count-To 组件</h1
>
<p class="m-0 text-base leading-relaxed text-g-700"
>高性能数字滚动动画组件,支持完整的动画控制和事件监听</p
>
</div>
<!-- 基础用法 -->
<div class="mb-15">
<h2 class="m-0 mb-6 text-xl font-medium">基础用法</h2>
<div class="count">
<ArtCountTo :target="1000" :duration="2000" />
</div>
</div>
<!-- 带前缀后缀 -->
<div class="mb-15">
<h2 class="m-0 mb-6 text-xl font-medium">带前缀后缀</h2>
<div class="count">
<ArtCountTo :target="20000" :duration="2500" prefix="¥" suffix="元" :decimals="2" />
</div>
</div>
<!-- 小数点和分隔符 -->
<div class="mb-15">
<h2 class="m-0 mb-6 text-xl font-medium">小数点和分隔符</h2>
<div class="count">
<ArtCountTo :target="2023.45" :duration="3000" :decimals="2" separator="," />
</div>
</div>
<!-- 动画效果对比 -->
<div class="mb-15">
<h2 class="m-0 mb-6 text-xl font-medium">动画效果对比</h2>
<div class="grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-6 mb-8">
<div class="text-center" v-for="easing in easingTypes" :key="easing.type">
<div class="mb-3 text-sm font-medium text-g-700">{{ easing.name }}</div>
<div class="count">
<ArtCountTo :target="easingTarget" :duration="3000" :easing="easing.type" />
</div>
</div>
</div>
<div class="text-center">
<ElButton @click="triggerEasing">触发所有动画</ElButton>
</div>
</div>
<!-- 控制按钮 -->
<div class="mb-15">
<h2 class="m-0 mb-6 text-xl font-medium">控制按钮</h2>
<div class="count">
<ArtCountTo
ref="countToRef"
:target="controlTarget"
:duration="2000"
@started="handleAnimationStarted"
@finished="handleAnimationFinished"
@paused="handleAnimationPaused"
@reset="handleAnimationReset"
/>
</div>
<div class="flex gap-3 justify-center">
<ElButton @click="startCount">开始</ElButton>
<ElButton @click="pauseCount">暂停</ElButton>
<ElButton @click="resetCount">重置</ElButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ArtCountTo from '@/components/core/text-effect/art-count-to/index.vue'
defineOptions({ name: 'TemplateCountTo' })
const controlTarget = ref(0)
const countToRef = ref()
const easingTarget = ref(0)
/**
* 缓动动画类型配置
*/
const easingTypes = [
{ name: 'Linear', type: 'linear' },
{ name: 'Ease Out Cubic', type: 'easeOutCubic' },
{ name: 'Ease Out Expo', type: 'easeOutExpo' },
{ name: 'Ease Out Sine', type: 'easeOutSine' },
{ name: 'Ease In Out', type: 'easeInOutCubic' },
{ name: 'Ease In Quad', type: 'easeInQuad' }
] as const
/**
* 开始计数动画
*/
const startCount = () => {
const newTarget = 5000
controlTarget.value = newTarget
countToRef.value?.start(newTarget)
}
/**
* 暂停计数动画
*/
const pauseCount = () => {
countToRef.value?.pause()
}
/**
* 重置计数动画
*/
const resetCount = () => {
countToRef.value?.reset()
controlTarget.value = 0
}
/**
* 触发缓动效果演示
* 在 0 和 1000 之间切换
*/
const triggerEasing = () => {
easingTarget.value = easingTarget.value === 0 ? 1000 : 0
}
/**
* 动画开始回调
* @param value 目标值
*/
const handleAnimationStarted = (value: number) => {
console.log('动画开始,目标值:', value)
}
/**
* 动画完成回调
* @param value 最终值
*/
const handleAnimationFinished = (value: number) => {
console.log('动画完成,最终值:', value)
}
/**
* 动画暂停回调
* @param value 当前值
*/
const handleAnimationPaused = (value: number) => {
console.log('动画暂停,当前值:', value)
}
/**
* 动画重置回调
*/
const handleAnimationReset = () => {
console.log('动画已重置')
}
</script>
<style scoped>
@reference '@styles/core/tailwind.css';
.count {
@apply p-5
mb-5
text-2xl
font-semibold
text-center
bg-g-100
rounded-lg
tabular-nums
border border-g-300/60;
}
</style>
@@ -0,0 +1,108 @@
<!-- https://vue-draggable-plus.pages.dev/ -->
<template>
<div class="page-content mb-5">
<ElRow>
<ElCard shadow="never" class="w-75 mr-5 mb-7.5">
<template #header>
<span class="text-base font-bold">基础示例</span>
</template>
<template #default>
<VueDraggable ref="el" v-model="userList">
<div
class="p-2.5 mb-2.5 cursor-move bg-active-color rounded"
v-for="item in userList"
:key="item.name"
>
{{ item.name }}
</div>
</VueDraggable>
</template>
</ElCard>
<ElCard shadow="never" class="w-75 mb-7.5">
<template #header>
<span class="text-base font-bold">过渡动画</span>
</template>
<template #default>
<VueDraggable v-model="userList" target=".sort-target" :scroll="true">
<TransitionGroup type="transition" tag="ul" name="fade" class="sort-target">
<li
v-for="item in userList"
:key="item.name"
class="p-2.5 mb-2.5 cursor-move bg-active-color rounded"
>
{{ item.name }}
</li>
</TransitionGroup>
</VueDraggable>
</template>
</ElCard>
</ElRow>
<ElCard shadow="never" class="mb-7.5">
<template #header>
<span class="text-base font-bold">表格拖拽排序</span>
</template>
<template #default>
<VueDraggable target="tbody" v-model="userList" :animation="150">
<ArtTable :data="userList">
<ElTableColumn label="姓名" prop="name" />
<ElTableColumn label="角色" prop="role" />
</ArtTable>
</VueDraggable>
</template>
</ElCard>
<ElCard shadow="never" class="mb-7.5">
<template #header>
<span class="text-base font-bold">指定元素拖拽排序</span>
</template>
<template #default>
<VueDraggable target="tbody" handle=".handle" v-model="userList" :animation="150">
<ArtTable :data="userList">
<ElTableColumn label="姓名" prop="name" />
<ElTableColumn label="角色" prop="role" />
<ElTableColumn label="操作" width="100">
<ElButton size="default" class="handle"> 移动 </ElButton>
</ElTableColumn>
</ArtTable>
</VueDraggable>
</template>
</ElCard>
</div>
</template>
<script setup lang="ts">
import { VueDraggable } from 'vue-draggable-plus'
defineOptions({ name: 'TemplateDrag' })
/**
* 用户列表数据
* 用于演示拖拽排序功能
*/
const userList = ref([
{ name: '孙悟空', role: '斗战胜佛' },
{ name: '猪八戒', role: '净坛使者' },
{ name: '沙僧', role: '金身罗汉' },
{ name: '唐僧', role: '旃檀功德佛' }
])
</script>
<style scoped>
.fade-move,
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: scaleY(0.01) translate(30px, 0);
}
.fade-leave-active {
position: absolute;
}
</style>
@@ -0,0 +1,151 @@
<template>
<div class="page-content">
<ArtExcelImport @import-success="handleImportSuccess" @import-error="handleImportError">
<template #import-text> 上传 Excel </template>
</ArtExcelImport>
<ArtExcelExport
style="margin-left: 10px"
:data="tableData"
filename="用户数据-1"
sheetName="用户列表"
type="success"
:headers="headers"
auto-index
:columns="columnConfig"
@export-success="handleExportSuccess"
@export-error="handleExportError"
@export-progress="handleProgress"
>
导出 Excel
</ArtExcelExport>
<ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton>
<ArtTable :data="tableData" style="margin-top: 10px">
<ElTableColumn
v-for="key in Object.keys(headers)"
:key="key"
:prop="key"
:label="headers[key as keyof typeof headers]"
/>
</ArtTable>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'WidgetsExcel' })
/**
* 表格数据类型定义
*/
interface TableData {
name: string
age: number
city: string
}
/**
* 表格数据
*/
const tableData = ref<TableData[]>([
{ name: '李四', age: 20, city: '上海' },
{ name: '张三', age: 25, city: '北京' },
{ name: '王五', age: 30, city: '广州' },
{ name: '赵六', age: 35, city: '深圳' },
{ name: '孙七', age: 28, city: '杭州' },
{ name: '周八', age: 32, city: '成都' },
{ name: '吴九', age: 27, city: '武汉' },
{ name: '郑十', age: 40, city: '南京' },
{ name: '刘一', age: 22, city: '重庆' },
{ name: '陈二', age: 33, city: '西安' }
])
/**
* 表头映射配置
* 用于 Excel 导入导出时的字段映射
*/
const headers = {
name: '姓名',
age: '年龄',
city: '城市'
}
/**
* 列配置
* 用于 Excel 导出时的列宽和格式化
*/
const columnConfig = {
name: {
title: '姓名',
width: 20,
formatter: (value: unknown) => (value ? String(value) : '未知')
},
age: {
title: '年龄',
width: 10,
formatter: (value: unknown) => (value ? `${value}岁` : '0岁')
},
city: {
title: '城市',
width: 12,
formatter: (value: unknown) => (value ? `${value}市` : '未知')
}
}
/**
* 处理 Excel 导入成功
* 将导入的数据转换为表格数据格式
* @param data 导入的原始数据
*/
const handleImportSuccess = (data: Array<Record<string, unknown>>) => {
const formattedData: TableData[] = data.map((item) => ({
name: String(item['姓名'] || ''),
age: Number(item['年龄']) || 0,
city: String(item['城市'] || '')
}))
tableData.value = formattedData
ElMessage.success(`成功导入 ${formattedData.length} 条数据`)
}
/**
* 处理 Excel 导入错误
* @param error 错误对象
*/
const handleImportError = (error: Error) => {
console.error('导入失败:', error)
ElMessage.error(`导入失败: ${error.message}`)
}
/**
* 处理 Excel 导出成功
*/
const handleExportSuccess = () => {
console.log('导出成功')
ElMessage.success('Excel 导出成功')
}
/**
* 处理 Excel 导出错误
* @param error 错误对象
*/
const handleExportError = (error: Error) => {
ElMessage.error(`导出失败: ${error.message}`)
}
/**
* 处理导出进度
* @param progress 导出进度百分比
*/
const handleProgress = (progress: number) => {
console.log('导出进度:', progress)
}
/**
* 清空表格数据
*/
const handleClear = () => {
tableData.value = []
ElMessage.info('已清空数据')
}
</script>
@@ -0,0 +1,116 @@
<template>
<div class="page-content">
<div class="mb-5">
<ElSpace wrap>
<ElButton :disabled="isLaunching" v-ripple @click="handleSingleLaunch"
>✨ 放个小礼花</ElButton
>
<ElButton :disabled="isLaunching" v-ripple @click="handleImageLaunch(bp)"
>🎉 打开幸运红包</ElButton
>
<ElButton :disabled="isLaunching" v-ripple @click="handleMultipleLaunch('')"
>🎆 璀璨烟火秀</ElButton
>
<ElButton :disabled="isLaunching" v-ripple @click="handleImageLaunch(sd)"
>❄️ 飘点小雪花</ElButton
>
<ElButton :disabled="isLaunching" v-ripple @click="handleMultipleLaunch(sd)"
>❄️ 浪漫暴风雪</ElButton
>
</ElSpace>
</div>
<ElDescriptions
title="礼花组件说明"
direction="vertical"
:column="1"
border
style="margin-top: 50px"
>
<ElDescriptionsItem label="显示时机">
礼花效果组件已全局注册,触发时机由配置文件控制。默认配置中的日期已过,不会在你使用过程中再次触发,无需担心。
</ElDescriptionsItem>
<ElDescriptionsItem label="礼花样式">
默认显示几何图形,可以配置图片,图片需要提前在
src/components/core/layouts/art-fireworks-effect/index.vue 文件预先定义
</ElDescriptionsItem>
<ElDescriptionsItem label="配置文件">
在 src/config/festival.ts 文件中,可以配置节日和对应的礼花样式
</ElDescriptionsItem>
<ElDescriptionsItem label="快捷键">
command + shift + p 或者 ctrl + shift + p
</ElDescriptionsItem>
</ElDescriptions>
</div>
</template>
<script setup lang="ts">
import { mittBus } from '@/utils/sys'
import bp from '@imgs/ceremony/hb.png'
import sd from '@imgs/ceremony/sd.png'
defineOptions({ name: 'WidgetsFireworks' })
const timerRef = ref<ReturnType<typeof setInterval> | null>(null)
const isLaunching = ref(false)
/**
* 触发连续礼花效果
* @param count 发射次数
* @param src 图片资源路径
*/
const triggerFireworks = (count: number, src: string) => {
// 清除之前的定时器
if (timerRef.value) {
clearInterval(timerRef.value)
timerRef.value = null
}
isLaunching.value = true
let fired = 0
timerRef.value = setInterval(() => {
mittBus.emit('triggerFireworks', src)
fired++
if (fired >= count) {
clearInterval(timerRef.value!)
timerRef.value = null
isLaunching.value = false
}
}, 1000)
}
/**
* 发射单个礼花
*/
const handleSingleLaunch = () => {
mittBus.emit('triggerFireworks')
}
/**
* 发射多个礼花
* @param src 图片资源路径
*/
const handleMultipleLaunch = (src: string) => {
triggerFireworks(10, src)
}
/**
* 发射带图片的礼花
* @param src 图片资源路径
*/
const handleImageLaunch = (src: string) => {
mittBus.emit('triggerFireworks', src)
}
/**
* 组件卸载时清理定时器
*/
onUnmounted(() => {
if (timerRef.value) {
clearInterval(timerRef.value)
timerRef.value = null
}
})
</script>
@@ -0,0 +1,110 @@
<template>
<div class="space-y-5 mb-5">
<div class="text-2xl font-medium mt-5 max-sm:text-2xl max-sm:mt-3">图标</div>
<div class="text-g-800">
v.3.0 版本图标库升级为 iconify,可在
<a href="https://icones.js.org/" target="_blank" class="text-theme hover:underline"
>Iconify</a
>
中查找,支持多种图标库,如 Remix Icon, Solar, Tabler Icons 等。
</div>
<div class="text-g-800">
为确保系统图标风格统一,项目全部采用 Remix Icon 图标库,可在
<a
href="https://icones.js.org/collection/ri"
target="_blank"
class="text-theme hover:underline"
>Iconify</a
>
或<a href="https://remixicon.com/" target="_blank" class="text-theme hover:underline">
Remix Icon 官网 </a
>搜索使用。
</div>
<!-- Iconify 图标 -->
<div class="art-card-sm p-5">
<div class="text-lg font-semibold mb-4">Iconify</div>
<div class="flex items-center gap-6">
<ArtSvgIcon icon="ri:github-fill" class="text-2xl" />
<ArtSvgIcon icon="ri:copilot-line" class="text-2xl text-theme" />
<ArtSvgIcon icon="ri:edge-line" class="text-2xl text-secondary" />
<ArtSvgIcon icon="ri:planet-line" class="text-2xl text-warning" />
<ArtSvgIcon icon="ri:windows-line" class="text-2xl text-info" />
<ArtSvgIcon icon="ri:thumb-up-line" class="text-2xl text-danger" />
<ArtSvgIcon icon="ri:gift-2-line" class="text-2xl text-success" />
<ArtSvgIcon icon="ri:apple-line" class="text-2xl text-secondary" />
</div>
</div>
<!-- Svg Icons -->
<div class="art-card-sm p-5">
<div class="text-lg font-semibold mb-4">Svg Icons</div>
<div class="flex items-center gap-6">
<ArtSvgIcon icon="svg-spinners:3-dots-fade" class="text-2xl text-red-400" />
<ArtSvgIcon icon="svg-spinners:3-dots-bounce" class="text-2xl text-blue-400" />
<ArtSvgIcon icon="svg-spinners:3-dots-move" class="text-2xl text-orange-400" />
<ArtSvgIcon icon="svg-spinners:3-dots-rotate" class="text-2xl text-purple-400" />
<ArtSvgIcon icon="svg-spinners:blocks-shuffle-2" class="text-2xl text-pink-300" />
<ArtSvgIcon icon="svg-spinners:clock" class="text-2xl text-yellow-500" />
<ArtSvgIcon icon="svg-spinners:tadpole" class="text-2xl text-orange-500" />
<ArtSvgIcon icon="svg-spinners:blocks-wave" class="text-2xl text-blue-500" />
</div>
</div>
<!-- Svg Icons -->
<div class="art-card-sm p-5">
<div class="text-lg font-semibold mb-4">Material Line Icons</div>
<div class="flex items-center gap-6">
<ArtSvgIcon icon="line-md:phone-call-twotone-loop" class="text-2xl text-blue-500" />
<ArtSvgIcon icon="line-md:switch-off" class="text-2xl text-green-500" />
<ArtSvgIcon icon="line-md:sun-rising-filled-loop" class="text-2xl text-yellow-400" />
<ArtSvgIcon icon="line-md:volume-high-filled" class="text-2xl text-purple-500" />
<ArtSvgIcon icon="line-md:github-twotone" class="text-2xl text-gray-700" />
<ArtSvgIcon icon="line-md:telegram" class="text-2xl text-sky-500" />
<ArtSvgIcon icon="line-md:reddit-loop" class="text-2xl text-orange-400" />
<ArtSvgIcon
icon="line-md:coffee-half-empty-filled-loop"
class="text-2xl text-emerald-500"
/>
</div>
</div>
<!-- 使用示例 -->
<div class="art-card-sm p-5">
<div class="text-lg font-semibold mb-4">使用示例</div>
<div class="space-y-4">
<div>
<div class="text-sm text-g-600 mb-2">基础使用</div>
<div class="bg-g-200 dark:bg-g-300/30 p-4 rounded font-mono text-sm text-g-800">
&lt;ArtSvgIcon icon="ri:home-line" /&gt;
</div>
</div>
<div>
<div class="text-sm text-g-600 mb-2">自定义大小</div>
<div class="bg-g-200 dark:bg-g-300/30 p-4 rounded font-mono text-sm text-g-800">
&lt;ArtSvgIcon icon="ri:user-line" class="text-2xl" /&gt;
</div>
</div>
<div>
<div class="text-sm text-g-600 mb-2">自定义颜色</div>
<div class="bg-g-200 dark:bg-g-300/30 p-4 rounded font-mono text-sm text-g-800">
&lt;ArtSvgIcon icon="ri:heart-fill" class="text-red-500" /&gt;
</div>
</div>
<div>
<div class="text-sm text-g-600 mb-2">组合使用</div>
<div class="bg-g-200 dark:bg-g-300/30 p-4 rounded font-mono text-sm text-g-800">
&lt;ArtSvgIcon icon="ri:star-fill" class="text-4xl text-yellow-500" /&gt;
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ArtSvgIcon from '@/components/core/base/art-svg-icon/index.vue'
defineOptions({ name: 'IconPage' })
</script>
@@ -0,0 +1,59 @@
<template>
<div class="page-content">
<ArtCutterImg
v-model:imgUrl="imageUrl"
:boxWidth="530"
:boxHeight="300"
:cutWidth="360"
:cutHeight="200"
:quality="1"
:tool="true"
:watermarkText="'My Watermark'"
watermarkColor="#ff0000"
:showPreview="true"
:originalGraph="false"
:title="'图片裁剪'"
:previewTitle="'预览效果'"
@error="handleError"
@imageLoadComplete="handleLoadComplete"
@imageLoadError="handleLoadError"
/>
</div>
</template>
<script setup lang="ts">
import lockImg from '@imgs/lock/bg_dark.webp'
defineOptions({ name: 'WidgetsImageCrop' })
/**
* 图片 URL
*/
const imageUrl = ref(lockImg)
/**
* 处理裁剪错误
* @param error 错误对象
*/
const handleError = (error: Error) => {
console.error('裁剪错误:', error)
ElMessage.error('图片裁剪失败')
}
/**
* 处理图片加载完成
* @param result 加载结果
*/
const handleLoadComplete = (result: { url: string; width: number; height: number }) => {
console.log('图片加载完成:', result)
}
/**
* 处理图片加载错误
* @param error 错误对象
*/
const handleLoadError = (error: Error) => {
console.error('图片加载失败:', error)
ElMessage.error('图片加载失败')
}
</script>
@@ -0,0 +1,136 @@
<template>
<div class="test-page p-5">
<h1 class="text-2xl font-bold mb-5">图片选择器组件测试</h1>
<!-- 单选测试 -->
<div class="mb-8 p-5 bg-white rounded-lg shadow-sm">
<h2 class="text-xl font-semibold mb-3 text-g-800">1. 单选模式</h2>
<div class="flex items-center gap-5">
<ImageSelector
v-model="singleImage"
:multiple="false"
shape="circle"
size="medium"
@confirm="handleSingleConfirm"
/>
<div class="text-sm">
<div class="font-medium">已选择:</div>
<div class="text-g-600 mt-1 break-all">{{ singleImage || '未选择' }}</div>
</div>
</div>
</div>
<!-- 多选测试 -->
<div class="mb-8 p-5 bg-white rounded-lg shadow-sm">
<h2 class="text-xl font-semibold mb-3 text-g-800">2. 多选模式</h2>
<ImageSelector
v-model="multipleImages"
:multiple="true"
shape="square"
size="small"
@confirm="handleMultipleConfirm"
/>
<div class="mt-3 text-sm">
<div class="font-medium">已选择 ({{ multipleImages.length }} 张):</div>
<div class="text-g-600 mt-1 break-all">{{ multipleImages.length > 0 ? multipleImages.join(', ') : '未选择' }}</div>
</div>
</div>
<!-- 大尺寸单选 -->
<div class="mb-8 p-5 bg-white rounded-lg shadow-sm">
<h2 class="text-xl font-semibold mb-3 text-g-800">3. 大尺寸单选(带默认值)</h2>
<div class="flex items-center gap-5">
<ImageSelector
v-model="largeImage"
:multiple="false"
shape="square"
size="large"
:default-selected="'/upload/default-avatar.webp'"
@confirm="handleLargeConfirm"
/>
<div class="text-sm">
<div class="font-medium">已选择:</div>
<div class="text-g-600 mt-1 break-all">{{ largeImage || '未选择' }}</div>
</div>
</div>
</div>
<!-- 禁用状态 -->
<div class="mb-8 p-5 bg-white rounded-lg shadow-sm">
<h2 class="text-xl font-semibold mb-3 text-g-800">4. 禁用状态</h2>
<ImageSelector
v-model="disabledImage"
:multiple="false"
shape="circle"
size="medium"
:disabled="true"
/>
<div class="mt-2 text-sm text-g-600">
禁用状态下的组件不可点击
</div>
</div>
<!-- 代码示例 -->
<div class="mb-8 p-5 bg-white rounded-lg shadow-sm">
<h2 class="text-xl font-semibold mb-3 text-g-800">5. 代码示例</h2>
<ElTabs>
<ElTabPane label="单选">
<pre class="bg-g-100 p-3 rounded text-sm overflow-auto">{{ singleCode }}</pre>
</ElTabPane>
<ElTabPane label="多选">
<pre class="bg-g-100 p-3 rounded text-sm overflow-auto">{{ multipleCode }}</pre>
</ElTabPane>
</ElTabs>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ImageSelector from '@/components/form/components/image-selector/index.vue'
import { ElMessage } from 'element-plus'
import { $t } from '@/locales'
// 测试数据
const singleImage = ref<string>('')
const multipleImages = ref<string[]>([])
const largeImage = ref<string>('/upload/default-avatar.webp')
const disabledImage = ref<string>('/upload/default-avatar.webp')
// 事件处理
const handleSingleConfirm = (image: string) => {
ElMessage.success(`单选确认: ${image}`)
}
const handleMultipleConfirm = (images: string[]) => {
ElMessage.success(`多选确认: 选择了 ${images.length} 张图片`)
}
const handleLargeConfirm = (image: string) => {
ElMessage.success(`大尺寸确认: ${image}`)
}
// 代码示例
const singleCode = `<ImageSelector
v-model="singleImage"
:multiple="false"
shape="circle"
size="medium"
@confirm="handleConfirm"
/>`
const multipleCode = `<ImageSelector
v-model="multipleImages"
:multiple="true"
shape="square"
size="small"
@confirm="handleConfirm"
/>`
</script>
<style lang="scss" scoped>
.test-page {
max-width: 1200px;
margin: 0 auto;
}
</style>
@@ -0,0 +1,130 @@
<template>
<div class="page-content">
<ElRow :gutter="20">
<ElCol
:span="6"
:md="12"
:sm="12"
:xs="24"
v-for="preset in qrcodePresets"
:key="preset.title"
>
<ElCard class="mb-5" shadow="never">
<template #header>
<div>
<span class="text-base font-bold">{{ preset.title }}</span>
</div>
</template>
<div class="flex-cc p-5 rounded">
<QrcodeVue :value="qrValue" v-bind="preset.config" />
</div>
</ElCard>
</ElCol>
</ElRow>
</div>
</template>
<script setup lang="ts">
import QrcodeVue from 'qrcode.vue'
import type { Level, RenderAs, ImageSettings } from 'qrcode.vue'
defineOptions({ name: 'WidgetsQrcode' })
/**
* 二维码内容
*/
const qrValue = ref('https://www.artd.pro')
const isShowLogo = ref(false)
/**
* 预设二维码样式配置
*/
const qrcodePresets = [
{
title: '渲染成 svg 标签',
config: {
size: 160,
level: 'H' as Level,
renderAs: 'svg' as RenderAs,
margin: 0,
background: '#ffffff',
foreground: '#000000'
}
},
{
title: '渲染成 canvas 标签',
config: {
size: 160,
level: 'H' as Level,
renderAs: 'canvas' as RenderAs,
margin: 0,
background: '#ffffff',
foreground: '#000000'
}
},
{
title: '自定义颜色',
config: {
size: 160,
level: 'H' as Level,
renderAs: 'canvas' as RenderAs,
margin: 0,
background: '#f0f0f0',
foreground: '#4080ff'
}
},
{
title: '带有Logo',
config: {
size: 160,
level: 'H' as Level,
renderAs: 'canvas' as RenderAs,
margin: 0,
background: '#ffffff',
foreground: '#000000',
imageSettings: {
src: 'https://www.artd.pro/assets/draw1-Ce1WF34i.png',
width: 40,
height: 40,
excavate: true
}
}
}
]
/**
* 二维码配置
*/
const qrcodeConfig = reactive({
size: 160,
level: 'H' as Level,
renderAs: 'canvas' as RenderAs,
margin: 0,
background: '#ffffff',
foreground: '#000000',
imageSettings: {
src: 'https://www.artd.pro/assets/draw1-Ce1WF34i.png',
width: 40,
height: 40,
excavate: true
} as ImageSettings
})
/**
* 监听是否显示 logo
* 根据状态动态设置二维码中心的 logo 图片
*/
watch(isShowLogo, (val) => {
if (!val) {
qrcodeConfig.imageSettings = {} as ImageSettings
} else {
qrcodeConfig.imageSettings = {
src: 'https://www.artd.pro/assets/draw1-Ce1WF34i.png',
width: 40,
height: 40,
excavate: true
}
}
})
</script>
@@ -0,0 +1,51 @@
<template>
<div class="page-content space-y-5">
<!-- 基础用法 -->
<ArtTextScroll
text="Art Design Pro 是一款兼具设计美学与高效开发的后台系统 <a target='_blank' href='https://www.artd.pro/docs/'>点击我 </a>访问官方文档"
showClose
/>
<!-- 使用不同的类型 -->
<ArtTextScroll type="success" text="这是一条成功类型的滚动公告" />
<ArtTextScroll type="warning" text="这是一条警告类型的滚动公告" />
<ArtTextScroll type="danger" text="这是一条危险类型的滚动公告" />
<ArtTextScroll type="info" text="这是一条信息类型的滚动公告" />
<!-- 可关闭 -->
<ArtTextScroll text="这是一条可关闭的滚动公告" @close="handleClose" />
<!-- 自定义速度和方向 -->
<ArtTextScroll
type="warning"
text="这是一条速度较慢、向右滚动的公告"
:speed="30"
direction="right"
/>
<ArtTextScroll
text="这是一条文字溢出才会滚动的公告,当文本内容超出容器宽度时才会开始滚动显示,否则保持静止状态"
@close="handleClose"
:alwaysScroll="false"
/>
<ArtTextScroll type="danger" direction="up" :speed="30" text="这是一条向上滚动的公告" />
<ArtTextScroll type="info" direction="down" :speed="30" text="这是一条向下滚动的公告" />
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'WidgetsTextScroll' })
/**
* 处理关闭事件
*/
const handleClose = () => {
console.log('文本滚动组件已关闭')
ElMessage.info('已关闭')
}
</script>
@@ -0,0 +1,32 @@
<template>
<div class="page-content">
<div class="max-w-150">
<ArtVideoPlayer
playerId="my-video-1"
:videoUrl="videoUrl"
:posterUrl="posterUrl"
:autoplay="false"
:volume="1"
:playbackRates="[0.5, 1, 1.5, 2]"
/>
</div>
</div>
</template>
<script setup lang="ts">
import lockImg from '@imgs/lock/bg_dark.webp'
defineOptions({ name: 'WidgetsVideo' })
/**
* 视频源 URL
*/
const videoUrl = ref(
'//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo.mp4'
)
/**
* 视频封面图片 URL
*/
const posterUrl = ref(lockImg)
</script>
@@ -0,0 +1,537 @@
<template>
<div class="page-content mb-5">
<!-- 完整工具栏编辑器 -->
<ElCard class="editor-card" shadow="never">
<template #header>
<div class="card-header">
<span>🛠️ 完整工具栏编辑器</span>
<div class="header-buttons">
<ElButton size="small" @click="clearFullEditor">清空</ElButton>
<ElButton size="small" @click="getFullEditorContent">获取内容</ElButton>
<ElButton size="small" @click="setFullEditorDemo">设置示例</ElButton>
</div>
</div>
</template>
<ArtWangEditor
ref="fullEditorRef"
v-model="fullEditorHtml"
height="400px"
placeholder="请输入内容,体验完整的编辑功能..."
:exclude-keys="[]"
/>
</ElCard>
<!-- 简化工具栏编辑器 -->
<ElCard class="editor-card" shadow="never">
<template #header>
<div class="card-header">
<span>✨ 简化工具栏编辑器</span>
<div class="header-buttons">
<ElButton size="small" @click="clearSimpleEditor">清空</ElButton>
<ElButton size="small" @click="getSimpleEditorContent">获取内容</ElButton>
<ElButton size="small" @click="setSimpleEditorDemo">设置示例</ElButton>
</div>
</div>
</template>
<ArtWangEditor
ref="simpleEditorRef"
v-model="simpleEditorHtml"
height="400px"
placeholder="请输入内容,体验简化的编辑功能..."
:toolbar-keys="simpleToolbarKeys"
/>
</ElCard>
<!-- 内容对比预览 -->
<ElCard class="preview-card" shadow="never">
<template #header>
<span>📖 内容预览对比</span>
</template>
<ElRow :gutter="20">
<ElCol :span="12">
<h3>完整编辑器内容</h3>
<ElTabs v-model="fullActiveTab">
<ElTabPane label="渲染效果" name="preview">
<div class="content-preview" v-html="fullEditorHtml"></div>
</ElTabPane>
<ElTabPane label="HTML源码" name="html">
<ElInput
v-model="fullEditorHtml"
type="textarea"
:rows="8"
placeholder="HTML源码"
readonly
/>
</ElTabPane>
</ElTabs>
</ElCol>
<ElCol :span="12">
<h3>简化编辑器内容</h3>
<ElTabs v-model="simpleActiveTab">
<ElTabPane label="渲染效果" name="preview">
<div class="content-preview" v-html="simpleEditorHtml"></div>
</ElTabPane>
<ElTabPane label="HTML源码" name="html">
<ElInput
v-model="simpleEditorHtml"
type="textarea"
:rows="8"
placeholder="HTML源码"
readonly
/>
</ElTabPane>
</ElTabs>
</ElCol>
</ElRow>
</ElCard>
<!-- 使用说明 -->
<ElCard class="usage-card" shadow="never">
<template #header>
<span>📚 使用说明</span>
</template>
<ElCollapse v-model="activeCollapse">
<ElCollapseItem title="基础用法" name="basic">
<pre><code class="language-vue">&lt;template&gt;
&lt;ArtWangEditor v-model="content" /&gt;
&lt;/template&gt;
&lt;script setup lang="ts"&gt;
import { ref } from 'vue'
const content = ref('&lt;p&gt;初始内容&lt;/p&gt;')
&lt;/script&gt;</code></pre>
</ElCollapseItem>
<ElCollapseItem title="完整工具栏配置" name="full">
<pre><code class="language-vue">&lt;template&gt;
&lt;!-- 显示所有工具,不排除任何功能 --&gt;
&lt;ArtWangEditor
v-model="content"
:exclude-keys="[]"
/&gt;
&lt;/template&gt;</code></pre>
</ElCollapseItem>
<ElCollapseItem title="简化工具栏配置" name="simple">
<pre><code class="language-vue">&lt;template&gt;
&lt;!-- 只显示基础编辑工具 --&gt;
&lt;ArtWangEditor
v-model="content"
:toolbar-keys="[
'bold', 'italic', 'underline', '|',
'bulletedList', 'numberedList', '|',
'insertLink', 'insertImage', '|',
'undo', 'redo'
]"
/&gt;
&lt;/template&gt;</code></pre>
</ElCollapseItem>
<ElCollapseItem title="自定义配置" name="config">
<pre><code class="language-vue">&lt;template&gt;
&lt;ArtWangEditor
v-model="content"
height="600px"
placeholder="请输入您的内容..."
:exclude-keys="['fontFamily', 'fontSize']"
:upload-config="{
maxFileSize: 5 * 1024 * 1024,
maxNumberOfFiles: 5
}"
/&gt;
&lt;/template&gt;</code></pre>
</ElCollapseItem>
<ElCollapseItem title="组件方法调用" name="methods">
<pre><code class="language-vue">&lt;template&gt;
&lt;ArtWangEditor ref="editorRef" v-model="content" /&gt;
&lt;el-button @click="handleClear"&gt;清空&lt;/el-button&gt;
&lt;el-button @click="handleFocus"&gt;聚焦&lt;/el-button&gt;
&lt;el-button @click="handleGetContent"&gt;获取内容&lt;/el-button&gt;
&lt;/template&gt;
&lt;script setup lang="ts"&gt;
import { ref } from 'vue'
const editorRef = ref()
const content = ref('')
const handleClear = () =&gt; {
editorRef.value?.clear()
}
const handleFocus = () =&gt; {
editorRef.value?.focus()
}
const handleGetContent = () =&gt; {
const html = editorRef.value?.getHtml()
console.log('编辑器内容:', html)
}
&lt;/script&gt;</code></pre>
</ElCollapseItem>
<ElCollapseItem title="工具栏配置说明" name="toolbar-config">
<div class="toolbar-explanation">
<h4>完整工具栏 vs 简化工具栏</h4>
<ElRow :gutter="16">
<ElCol :span="12">
<h5>✅ 完整工具栏包含:</h5>
<ul>
<li>文本格式:加粗、斜体、下划线、字体颜色、背景色</li>
<li>段落格式:标题、引用、对齐方式、缩进</li>
<li>列表:有序列表、无序列表、待办事项</li>
<li>插入:链接、图片、表格、分割线、表情</li>
<li>代码:代码块、行内代码</li>
<li>操作:撤销、重做、全屏、清除格式</li>
</ul>
</ElCol>
<ElCol :span="12">
<h5>⚡ 简化工具栏包含:</h5>
<ul>
<li>基础格式:加粗、斜体、下划线</li>
<li>列表:有序列表、无序列表</li>
<li>插入:链接、图片</li>
<li>操作:撤销、重做</li>
</ul>
<p class="note">适用于简单的文本编辑场景,界面更清爽。</p>
</ElCol>
</ElRow>
</div>
</ElCollapseItem>
</ElCollapse>
</ElCard>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'WidgetsWangEditor' })
const fullEditorRef = ref()
const simpleEditorRef = ref()
const fullActiveTab = ref('preview')
const simpleActiveTab = ref('preview')
const activeCollapse = ref(['basic'])
/**
* 简化工具栏配置
* 只包含基础的编辑功能
*/
const simpleToolbarKeys = [
'bold',
'italic',
'underline',
'|',
'bulletedList',
'numberedList',
'|',
'insertLink',
'insertImage',
'|',
'undo',
'redo'
]
// 完整编辑器内容
const fullEditorHtml = ref(`<h1>🎨 完整工具栏编辑器示例</h1>
<p>这个编辑器包含所有功能,您可以体验丰富的格式编辑功能。</p>
<h2>✨ 文本样式</h2>
<p><strong>这是加粗的文字</strong></p>
<p><em>这是斜体文字</em></p>
<p><u>这是下划线文字</u></p>
<p><span style="color: rgb(194, 79, 74);">这是彩色文字</span></p>
<h2>📝 列表和待办</h2>
<ul>
<li>无序列表项 1</li>
<li>无序列表项 2</li>
</ul>
<ol>
<li>有序列表项 1</li>
<li>有序列表项 2</li>
</ol>
<ul class="w-e-todo">
<li class="w-e-todo-item"><input type="checkbox" checked="true" readonly="true" disabled="disabled"><span>已完成的任务</span></li>
<li class="w-e-todo-item"><input type="checkbox" readonly="true" disabled="disabled"><span>待完成的任务</span></li>
</ul>
<h2>💬 引用和表格</h2>
<blockquote>
这是一段引用文字,展示引用格式的效果。
</blockquote>
<table style="border-collapse: collapse; width: 100%;" border="1">
<thead>
<tr><th>功能</th><th>描述</th></tr>
</thead>
<tbody>
<tr><td>完整工具栏</td><td>包含所有编辑功能</td></tr>
<tr><td>自定义配置</td><td>支持灵活的工具栏配置</td></tr>
</tbody>
</table>
<h2>💻 代码块</h2>
<pre><code class="language-javascript">// 完整编辑器支持代码高亮
function createEditor() {
return new WangEditor({
container: '#editor',
toolbar: 'full' // 完整工具栏
});
}</code></pre>
<p>🔗 <a href="https://www.wangeditor.com/" target="_blank">访问官网了解更多</a></p>`)
// 简化编辑器内容
const simpleEditorHtml = ref(`<h1>✨ 简化工具栏编辑器示例</h1>
<p>这个编辑器只包含基础的编辑功能,界面更加简洁。</p>
<h2>基础文本格式</h2>
<p><strong>加粗文字</strong></p>
<p><em>斜体文字</em></p>
<p><u>下划线文字</u></p>
<h2>列表功能</h2>
<ul>
<li>无序列表项 1</li>
<li>无序列表项 2</li>
</ul>
<ol>
<li>有序列表项 1</li>
<li>有序列表项 2</li>
</ol>
<h2>链接和图片</h2>
<p>支持插入 <a href="https://www.wangeditor.com/" target="_blank">链接</a> 和图片。</p>
<p>简化版编辑器专注于基础功能,适合简单的内容编辑需求。</p>`)
/**
* 清空完整编辑器内容
*/
const clearFullEditor = () => {
fullEditorRef.value?.clear()
ElMessage.success('完整编辑器已清空')
}
/**
* 获取完整编辑器内容
*/
const getFullEditorContent = () => {
const content = fullEditorRef.value?.getHtml()
console.log('完整编辑器内容:', content)
ElMessage.success('完整编辑器内容已输出到控制台')
}
/**
* 设置完整编辑器演示内容
*/
const setFullEditorDemo = () => {
const demoContent = `<h2>🎉 完整编辑器演示内容</h2>
<p>这是通过方法设置的演示内容,展示完整编辑器的强大功能。</p>
<ul>
<li>支持丰富的文本格式</li>
<li>包含表格、代码块等高级功能</li>
<li>提供完整的编辑体验</li>
</ul>
<table style="border-collapse: collapse; width: 100%;" border="1">
<tr><th>特性</th><th>状态</th></tr>
<tr><td>完整工具栏</td><td>✅ 已启用</td></tr>
<tr><td>高级功能</td><td>✅ 已启用</td></tr>
</table>`
fullEditorRef.value?.setHtml(demoContent)
ElMessage.success('已设置完整编辑器演示内容')
}
/**
* 清空简化编辑器内容
*/
const clearSimpleEditor = () => {
simpleEditorRef.value?.clear()
ElMessage.success('简化编辑器已清空')
}
/**
* 获取简化编辑器内容
*/
const getSimpleEditorContent = () => {
const content = simpleEditorRef.value?.getHtml()
console.log('简化编辑器内容:', content)
ElMessage.success('简化编辑器内容已输出到控制台')
}
/**
* 设置简化编辑器演示内容
*/
const setSimpleEditorDemo = () => {
const demoContent = `<h2>⚡ 简化编辑器演示内容</h2>
<p>这是通过方法设置的演示内容,展示简化编辑器的核心功能。</p>
<ul>
<li><strong>基础格式</strong>:加粗、斜体、下划线</li>
<li><em>列表支持</em>:有序和无序列表</li>
<li><u>媒体插入</u>:链接和图片</li>
</ul>
<ol>
<li>界面简洁清爽</li>
<li>功能专注实用</li>
<li>适合快速编辑</li>
</ol>
<p>🔗 <a href="https://example.com" target="_blank">这是一个链接示例</a></p>`
simpleEditorRef.value?.setHtml(demoContent)
ElMessage.success('已设置简化编辑器演示内容')
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 20px;
}
.editor-card {
margin-bottom: 24px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.header-buttons {
display: flex;
gap: 8px;
}
.preview-card {
margin-bottom: 24px;
}
.preview-card h3 {
margin: 0 0 16px;
font-size: 16px;
color: var(--el-text-color-primary);
}
.content-preview {
min-height: 200px;
max-height: 300px;
padding: 16px;
overflow-y: auto;
background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color);
border-radius: 6px;
}
.content-preview :deep(h1),
.content-preview :deep(h2),
.content-preview :deep(h3) {
margin: 16px 0 8px;
}
.content-preview :deep(p) {
margin: 8px 0;
line-height: 1.6;
}
.content-preview :deep(table) {
margin: 16px 0;
}
.content-preview :deep(table th),
.content-preview :deep(table td) {
padding: 8px 12px;
}
.content-preview :deep(pre) {
padding: 12px;
margin: 16px 0;
overflow-x: auto;
background-color: var(--el-fill-color-light);
border-radius: 4px;
}
.content-preview :deep(blockquote) {
padding-left: 16px;
margin: 16px 0;
color: var(--el-text-color-regular);
border-left: 4px solid var(--el-color-primary);
}
.usage-card :deep(.el-collapse-item__content) {
padding-bottom: 16px;
}
.usage-card pre {
padding: 16px;
margin: 0;
overflow-x: auto;
background-color: var(--el-fill-color-light);
border-radius: 6px;
}
.usage-card pre code {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
}
.toolbar-explanation h4 {
margin: 0 0 16px;
color: var(--el-text-color-primary);
}
.toolbar-explanation h5 {
margin: 0 0 8px;
font-size: 14px;
color: var(--el-text-color-regular);
}
.toolbar-explanation ul {
padding-left: 20px;
margin: 8px 0 16px;
}
.toolbar-explanation ul li {
margin: 4px 0;
font-size: 13px;
color: var(--el-text-color-regular);
}
.toolbar-explanation .note {
margin: 8px 0 0;
font-size: 12px;
font-style: italic;
color: var(--el-text-color-placeholder);
}
@media (width <= 768px) {
.page-content {
padding: 12px;
}
.card-header {
flex-direction: column;
gap: 12px;
align-items: stretch !important;
}
.header-buttons {
justify-content: center;
}
.preview-card :deep(.el-col) {
margin-bottom: 16px;
}
}
</style>
@@ -0,0 +1,75 @@
<template>
<div class="page-content mb-5">
<!-- 基础文字水印 -->
<ElCard class="mb-7.5" shadow="never">
<template #header>基础文字水印</template>
<ElWatermark content="Art Design Pro" :font="{ color: 'rgba(128, 128, 128, 0.2)' }">
<div style="height: 200px"></div>
</ElWatermark>
</ElCard>
<!-- 多行文字水印 -->
<ElCard class="mb-7.5" shadow="never">
<template #header>多行文字水印</template>
<ElWatermark
:content="['Art Design Pro', '专注用户体验,视觉设计']"
:font="{ fontSize: 16, color: 'rgba(128, 128, 128, 0.2)' }"
>
<div style="height: 200px"></div>
</ElWatermark>
</ElCard>
<!-- 图片水印 -->
<ElCard class="mb-7.5" shadow="never">
<template #header>图片水印</template>
<ElWatermark :image="watermarkImage" :opacity="0.2" :width="80" :height="20">
<div style="height: 200px"></div>
</ElWatermark>
</ElCard>
<!-- 自定义样式水印 -->
<ElCard class="mb-7.5" shadow="never">
<template #header>自定义样式水印</template>
<ElWatermark
content="Art Design Pro"
:font="{
fontSize: 20,
fontFamily: 'Arial',
color: 'rgba(255, 0, 0, 0.3)'
}"
:rotate="-22"
:gap="[100, 100]"
>
<div style="height: 200px"></div>
</ElWatermark>
</ElCard>
<ElButton
:type="settingStore.watermarkVisible ? 'danger' : 'primary'"
@click="handleWatermarkVisible"
>
{{ settingStore.watermarkVisible ? '隐藏全局水印' : '显示全局水印' }}
</ElButton>
</div>
</template>
<script setup lang="ts">
import { useSettingStore } from '@/store/modules/setting'
defineOptions({ name: 'Watermark' })
const settingStore = useSettingStore()
/**
* 水印图片 URL
*/
const watermarkImage = ref('https://element-plus.org/images/element-plus-logo.svg')
/**
* 切换全局水印显示状态
*/
const handleWatermarkVisible = () => {
useSettingStore().setWatermarkVisible(!settingStore.watermarkVisible)
ElMessage.success(settingStore.watermarkVisible ? '已显示全局水印' : '已隐藏全局水印')
}
</script>