Initial commit: One Pipe System

完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sexygoat
2026-01-22 16:35:33 +08:00
commit 222e5bb11a
495 changed files with 145440 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
<template>
<div class="page-content">
<ElButton @contextmenu.prevent="showMenu"> 右键触发菜单 </ElButton>
<!-- 右键菜单组件 -->
<ArtMenuRight
ref="menuRef"
:menu-items="menuItems"
:menu-width="160"
:submenu-width="200"
:border-radius="10"
@select="handleSelect"
@show="onMenuShow"
@hide="onMenuHide"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { ElMessage } from 'element-plus'
const menuRef = ref<InstanceType<typeof ArtMenuRight>>()
const lastAction = ref('')
// 右键菜单选项
const menuItems = computed((): MenuItemType[] => [
{
key: 'copy',
label: '复制',
icon: '&#xe7b2;'
},
{
key: 'paste',
label: '粘贴',
icon: '&#xe70b;'
},
{
key: 'cut',
label: '剪切',
icon: '&#xe7b8;',
showLine: true
},
{
key: 'export',
label: '导出选项',
icon: '&#xe78b;',
children: [
{
key: 'exportExcel',
label: '导出 Excel',
icon: '&#xe604;'
},
{
key: 'exportPdf',
label: '导出 PDF',
icon: '&#xe89e;'
}
]
},
{
key: 'edit',
label: '编辑选项',
icon: '&#xe706;',
children: [
{
key: 'rename',
label: '重命名',
icon: '&#xe607;'
},
{
key: 'duplicate',
label: '复制副本',
icon: '&#xe608;'
}
]
},
{
key: 'share',
label: '分享',
icon: '&#xe73b;',
showLine: true
},
{
key: 'delete',
label: '删除',
icon: '&#xe850;'
},
{
key: 'disabled',
label: '禁用选项',
icon: '&#xe619;',
disabled: true
}
])
const handleSelect = (item: MenuItemType) => {
lastAction.value = `${item.label} (${item.key})`
ElMessage.success(`执行操作: ${item.label}`)
console.log('选择了菜单项:', item)
}
const showMenu = (e: MouseEvent) => {
console.log('触发右键菜单', e)
// 确保阻止默认行为
e.preventDefault()
e.stopPropagation()
// 延迟一帧执行,确保事件处理完成
nextTick(() => {
menuRef.value?.show(e)
})
}
const onMenuShow = () => {
console.log('菜单显示')
}
const onMenuHide = () => {
console.log('菜单隐藏')
}
</script>

View File

@@ -0,0 +1,103 @@
<!-- 文档https://github.com/PanJiaChen/vue-countTo -->
<template>
<div class="page-content">
<!-- 基础用法 -->
<h2>基础用法</h2>
<CountTo :endVal="count1" :duration="1000"></CountTo>
<!-- 带前缀后缀 -->
<h2>带前缀后缀</h2>
<CountTo prefix="¥" suffix="元" :startVal="0" :endVal="count2" :duration="2000"></CountTo>
<!-- 小数点和分隔符 -->
<h2>小数点和分隔符</h2>
<CountTo
:startVal="0"
:endVal="count3"
:decimals="2"
decimal="."
separator=","
:duration="2500"
></CountTo>
<!-- 控制按钮 -->
<h2>控制按钮</h2>
<CountTo
ref="countTo"
:startVal="0"
:endVal="count4"
:duration="3000"
:autoplay="false"
></CountTo>
<div class="mt-4">
<ElButtonGroup>
<ElButton @click="start" v-ripple>开始</ElButton>
<ElButton @click="pause" v-ripple>暂停</ElButton>
<ElButton @click="reset" v-ripple>重置</ElButton>
</ElButtonGroup>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CountTo } from 'vue3-count-to'
const count1 = ref(1000)
const count2 = ref(19999.99)
const count3 = ref(2023.45)
const count4 = ref(5000)
const countTo = ref()
const isCounting = ref(false)
// 控制方法
const start = () => {
if (isCounting.value) return
try {
countTo.value?.reset()
countTo.value?.start()
isCounting.value = true
} catch (error) {
console.error('启动计数器失败:', error)
}
}
const pause = () => {
if (!isCounting.value) return
try {
countTo.value?.pause()
isCounting.value = false
} catch (error) {
console.error('暂停计数器失败:', error)
}
}
const reset = () => {
try {
countTo.value?.reset()
isCounting.value = false
} catch (error) {
console.error('重置计数器失败:', error)
}
}
</script>
<style scoped>
.page-content {
padding: 20px;
}
h2 {
margin: 20px 0;
font-size: 18px;
color: #333;
}
.mt-4 {
margin-top: 16px;
}
</style>

View File

@@ -0,0 +1,125 @@
<!-- https://vue-draggable-plus.pages.dev/ -->
<template>
<div class="page-content">
<ElRow>
<ElCard shadow="never" style="width: 300px; margin-right: 20px">
<template #header>
<span class="card-header">基础示例</span>
</template>
<template #default>
<VueDraggable ref="el" v-model="userList">
<div class="demo1-item" v-for="item in userList" :key="item.name">
{{ item.name }}
</div>
</VueDraggable>
</template>
</ElCard>
<ElCard shadow="never" style="width: 300px">
<template #header>
<span class="card-header">过渡动画</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="demo1-item">
{{ item.name }}
</li>
</TransitionGroup>
</VueDraggable>
</template>
</ElCard>
</ElRow>
<ElCard shadow="never">
<template #header>
<span class="card-header">表格拖拽排序</span>
</template>
<template #default>
<VueDraggable target="tbody" v-model="userList" :animation="150">
<ArtTable :data="userList" :pagination="false">
<ElTableColumn label="姓名" prop="name" />
<ElTableColumn label="角色" prop="role" />
</ArtTable>
</VueDraggable>
</template>
</ElCard>
<ElCard shadow="never">
<template #header>
<span class="card-header">指定元素拖拽排序</span>
</template>
<template #default>
<VueDraggable target="tbody" handle=".handle" v-model="userList" :animation="150">
<ArtTable :data="userList" :pagination="false">
<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'
const userList = ref([
{
name: '孙悟空',
role: '斗战胜佛'
},
{
name: '猪八戒',
role: '净坛使者'
},
{
name: '沙僧',
role: '金身罗汉'
},
{
name: '唐僧',
role: '旃檀功德佛'
}
])
</script>
<style lang="scss" scoped>
.page-content {
.demo1-item {
padding: 10px;
margin-bottom: 10px;
cursor: move;
background-color: rgba(var(--art-gray-200-rgb), 0.8);
border-radius: 4px;
}
.el-card {
margin-bottom: 30px;
.card-header {
font-size: 16px;
font-weight: bold;
}
}
}
.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>

View File

@@ -0,0 +1,89 @@
<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="用户数据"
sheetName="用户列表"
type="success"
:headers="headers"
@export-success="handleExportSuccess"
@export-error="handleExportError"
>
导出 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">
interface TableData {
name: string
age: number
city: string
}
const handleImportSuccess = (data: any[]) => {
// 将导入的数据转换为正确的格式
const formattedData = data.map((item) => ({
name: item['姓名'],
age: Number(item['年龄']),
city: item['城市']
}))
tableData.value = formattedData
// tableData.value = data
}
const handleImportError = (error: Error) => {
// 处理导入错误
console.error('导入失败:', error)
}
// 使用类型化的ref
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: '西安' }
])
// 自定义表头映射
const headers = {
name: '姓名',
age: '年龄',
city: '城市'
}
const handleExportSuccess = () => {
ElMessage.success('导出成功')
}
const handleExportError = (error: Error) => {
ElMessage.error(`导出失败: ${error.message}`)
}
const handleClear = () => {
tableData.value = []
}
</script>

View File

@@ -0,0 +1,102 @@
<template>
<div class="page-content">
<div class="action-buttons">
<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
>
</div>
<ElDescriptions
title="礼花组件说明"
direction="vertical"
:column="1"
border
style="margin-top: 50px"
>
<ElDescriptionsItem label="显示时机">
礼花效果组件全局注册了在节假日的时候会自动显示你可以通过配置文件来控制显示时机
</ElDescriptionsItem>
<ElDescriptionsItem label="礼花样式">
默认显示几何图形可以配置图片图片需要提前在 components/Ceremony/Fireworks 文件预先定义
</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'
const timerRef = ref<ReturnType<typeof setInterval> | null>(null)
const isLaunching = ref(false)
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')
}
const handleMultipleLaunch = (src: string) => {
triggerFireworks(10, src)
}
const handleImageLaunch = (src: string) => {
mittBus.emit('triggerFireworks', src)
}
// 组件卸载时清理定时器
onUnmounted(() => {
if (timerRef.value) {
clearInterval(timerRef.value)
timerRef.value = null
}
})
</script>
<style lang="scss" scoped>
.action-buttons {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,175 @@
<template>
<div class="page-content">
<div class="form">
<ElSelect v-model="iconType" placeholder="Select" style="width: 240px">
<ElOption
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</ElSelect>
<div class="colors-icon">
<ElCheckbox v-model="isColorsIcon" label="彩色图标" />
</div>
</div>
<div class="list">
<ul class="icon-list">
<li v-for="icon in systemIconClasses" :key="icon.className" @click="copyIcon(icon)">
<i
class="iconfont-sys"
v-if="iconType === 'unicode'"
v-html="icon.unicode"
:style="getIconStyle()"
></i>
<i :class="`iconfont-sys ${icon.className}`" v-else :style="getIconStyle()"></i>
<span>{{ iconType === 'unicode' ? icon.unicode : icon.className }}</span>
</li>
</ul>
</div>
</div>
</template>
<script lang="ts" setup>
import { extractIconClasses, IconfontType } from '@/utils/constants'
import { ElMessage } from 'element-plus'
const iconType = ref('unicode')
const options = [
{
value: 'unicode',
label: 'Unicode'
},
{
value: 'fontClass',
label: 'Font class'
}
]
const systemIconClasses = ref<IconfontType[]>([])
const isColorsIcon = ref(false)
onMounted(() => {
systemIconClasses.value = extractIconClasses()
})
const copyIcon = (text: IconfontType) => {
if (!text) return
let copyipt = document.createElement('input')
copyipt.setAttribute(
'value',
(iconType.value === 'unicode' ? text.unicode : text.className) || ''
)
document.body.appendChild(copyipt)
copyipt.select()
document.execCommand('copy')
document.body.removeChild(copyipt)
ElMessage.success(`已复制`)
}
const getRandomColor = () => {
const colors = ['#2d8cf0', '#19be6b', '#ff9900', '#f24965', '#9463f7']
return colors[Math.floor(Math.random() * colors.length)]
}
const getIconStyle = () => {
return isColorsIcon.value ? { color: getRandomColor() } : { color: 'var(--art-text-gray-700)' }
}
</script>
<style lang="scss" scoped>
.page-content {
width: 100%;
height: 100%;
$border-color: #eee;
.form {
display: flex;
align-items: center;
.colors-icon {
box-sizing: border-box;
height: var(--el-component-custom-height);
padding: 0 30px;
margin-left: 10px;
border: 1px solid var(--art-border-dashed-color);
border-radius: calc(var(--custom-radius) / 3 + 2px) !important;
}
}
.list {
margin-top: 20px;
.icon-list {
display: grid;
grid-template-columns: repeat(12, 1fr);
width: calc(100% + 16px);
li {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
aspect-ratio: 1 / 1;
padding: 0 8px;
margin: 0 16px 16px 0;
overflow: hidden;
color: rgba(#fff, 0.8);
text-align: center;
border: 1px solid rgb(var(--art-gray-300-rgb), 0.8);
border-radius: 12px !important;
&:hover {
cursor: pointer;
background: var(--art-gray-100);
}
i {
font-size: 26px;
transition: color 0.3s ease;
}
span {
display: block;
margin-top: 10px;
font-size: 12px;
color: var(--art-text-gray-600);
}
}
}
}
}
@media screen and (max-width: $device-notebook) {
.page-content {
.list {
.icon-list {
grid-template-columns: repeat(8, 1fr);
}
}
}
}
@media screen and (max-width: $device-ipad-vertical) {
.page-content {
.list {
.icon-list {
grid-template-columns: repeat(5, 1fr);
}
}
}
}
@media screen and (max-width: $device-phone) {
.page-content {
.list {
.icon-list {
grid-template-columns: repeat(3, 1fr);
}
}
}
}
</style>

View File

@@ -0,0 +1,56 @@
<template>
<div class="page-content">
<div class="select">
<div class="item">
<h3>Unicode</h3>
<ArtIconSelector
:iconType="IconTypeEnum.UNICODE"
@getIcon="getIcon"
defaultIcon="&#xe6b5;"
/>
</div>
<div class="item">
<h3>ClassName</h3>
<ArtIconSelector
:iconType="IconTypeEnum.CLASS_NAME"
@getIcon="getIcon"
width="260px"
defaultIcon="iconsys-baitianmoshi3"
/>
</div>
<div class="item">
<h3>禁用</h3>
<ArtIconSelector
:iconType="IconTypeEnum.CLASS_NAME"
@getIcon="getIcon"
width="260px"
defaultIcon="iconsys-baitianmoshi3"
disabled
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { IconTypeEnum } from '@/enums/appEnum'
// 获取选择的图标
const getIcon = (icon: string) => {
console.log(icon)
}
</script>
<style scoped lang="scss">
.select {
.item {
margin-bottom: 30px;
h3 {
padding-bottom: 10px;
font-size: 16px;
font-weight: 500;
}
}
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
<div class="page-content">
<ArtCutterImg
style="margin-top: 20px"
v-model:imgUrl="imageUrl"
:boxWidth="540"
:boxHeight="300"
:cutWidth="360"
:cutHeight="200"
:quality="1"
:tool="true"
:watermarkText="'My Watermark'"
watermarkColor="#ff0000"
:showPreview="true"
:originalGraph="false"
:previewTitle="'预览效果'"
@error="handleError"
@imageLoadComplete="handleLoadComplete"
@imageLoadError="handleLoadError"
/>
</div>
</template>
<script setup lang="ts">
import lockImg from '@imgs/lock/lock_screen_1.webp'
const imageUrl = ref(lockImg)
const handleError = (error: any) => {
console.error('裁剪错误:', error)
}
const handleLoadComplete = (result: any) => {
console.log('图片加载完成:', result)
}
const handleLoadError = (error: any) => {
console.error('图片加载失败:', error)
}
</script>

View File

@@ -0,0 +1,136 @@
<template>
<div class="page-content">
<ElRow :gutter="20">
<ElCol :span="6" v-for="preset in qrcodePresets" :key="preset.title">
<ElCard class="qrcode-card" shadow="never">
<template #header>
<div class="card-header">
<span>{{ preset.title }}</span>
</div>
</template>
<div class="qrcode-preview">
<QrcodeVue :value="qrValue" v-bind="preset.config" />
</div>
</ElCard>
</ElCol>
</ElRow>
</div>
</template>
<script setup lang="ts">
import QrcodeVue from 'qrcode.vue'
import { ref, reactive, watch } from 'vue'
import type { Level, RenderAs, ImageSettings } from 'qrcode.vue'
// 二维码内容
const qrValue = ref('https://www.lingchen.kim')
const isShowLogo = ref(false)
// 预设二维码样式配置
const qrcodePresets = [
{
title: '渲染成 img 标签',
config: {
size: 160,
level: 'H' as Level,
renderAs: 'canvas' 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.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.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.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.png',
width: 40,
height: 40,
excavate: true
} as ImageSettings
})
// 监听是否显示 logo
watch(isShowLogo, (val) => {
if (!val) {
qrcodeConfig.imageSettings = {} as ImageSettings
} else {
qrcodeConfig.imageSettings = {
src: 'https://www.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.png',
width: 40,
height: 40,
excavate: true
}
}
})
</script>
<style lang="scss" scoped>
.page-content {
padding: 20px;
.qrcode-card {
margin-bottom: 20px;
.card-header {
font-size: 16px;
font-weight: bold;
}
.qrcode-preview {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 4px;
}
}
}
</style>

View File

@@ -0,0 +1,28 @@
<template>
<div class="page-content">
<!-- 基础用法 -->
<ArtTextScroll
text="Art Design Pro 是一款专注于用户体验和视觉设计的后台管理系统模版 <a target='_blank' href='https://www.lingchen.kim/art-design-pro/docs/'>点击我 </a>访问官方文档"
/>
<!-- 使用不同的类型 -->
<ArtTextScroll type="success" text="这是一条成功类型的滚动公告" />
<ArtTextScroll type="warning" text="这是一条警告类型的滚动公告" />
<ArtTextScroll type="danger" text="这是一条危险类型的滚动公告" />
<ArtTextScroll type="info" text="这是一条信息类型的滚动公告" />
<!-- 自定义速度和方向 -->
<ArtTextScroll text="这是一条速度较慢、向右滚动的公告" :speed="30" direction="right" />
</div>
</template>
<style lang="scss" scoped>
.page-content {
:deep(.text-scroll-container) {
margin-bottom: 20px;
}
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<div class="page-content">
<div class="video-container">
<ArtVideoPlayer
playerId="my-video-1"
:videoUrl="videoUrl"
:posterUrl="posterUrl"
:autoplay="false"
:volume="0.5"
:playbackRates="[0.5, 1, 1.5, 2]"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import lockImg from '@imgs/lock/lock_screen_1.webp'
// 视频源和封面图片URL
const videoUrl = ref(
'//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo.mp4'
)
const posterUrl = ref(lockImg)
</script>
<style scoped>
.video-container {
max-width: 600px;
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<div class="page-content">
<ArtWangEditor v-model="editorHtml" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const editorHtml = ref(`<h1>欢迎使用富文本编辑器</h1>
<p>这是一个段落示例,下面展示了一些常用的富文本格式:</p>
<h2>文本样式</h2>
<p><strong>这是加粗的文字</strong></p>
<p><em>这是斜体文字</em></p>
<h2>列表示例</h2>
<ul>
<li>无序列表项 1</li>
<li>无序列表项 2</li>
<li>无序列表项 3</li>
</ul>
<ol>
<li>有序列表项 1</li>
<li>有序列表项 2</li>
<li>有序列表项 3</li>
</ol>
<h2>引用示例</h2>
<blockquote style="border-left: 4px solid #ccc; margin-left: 0; padding-left: 1em;">
这是一段引用文字,可以用来突出显示重要内容。
</blockquote>
<h2>表格示例</h2>
<table border="1" cellpadding="5">
<tr>
<th>表头 1</th>
<th>表头 2</th>
</tr>
<tr>
<td>单元格 1</td>
<td>单元格 2</td>
</tr>
</table>
<h2>代码示例</h2>
<pre><code class="language-javascript">// JavaScript 代码示例
function greeting(name) {
return \`Hello, \${name}!\`;
}
console.log(greeting('世界'));</code></pre>
<pre><code class="language-python"># Python 代码示例
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))</code></pre>
<h2>链接示例</h2>
<p><a href="https://www.lingchen.kim/art-design-pro/docs/">这是一个超链接</a></p>
`)
</script>

View File

@@ -0,0 +1,77 @@
<template>
<div class="page-content">
<!-- 基础文字水印 -->
<ElCard class="card" 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="card" 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="card" shadow="never">
<template #header>图片水印</template>
<ElWatermark :image="watermarkImage" :opacity="0.2" :width="80" :height="20">
<div style="height: 200px"></div>
</ElWatermark>
</ElCard>
<!-- 自定义样式水印 -->
<ElCard class="card" 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'
const settingStore = useSettingStore()
// 这里替换成你的实际logo图片地址
const watermarkImage = ref('https://element-plus.org/images/element-plus-logo.svg')
const handleWatermarkVisible = () => {
useSettingStore().setWatermarkVisible(!settingStore.watermarkVisible)
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 20px;
.el-card {
margin-bottom: 30px;
}
}
</style>