mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-11-14 20:53:41 +08:00
feat(projects): 新版重构完成
This commit is contained in:
15
src/components/custom/GithubLink/index.vue
Normal file
15
src/components/custom/GithubLink/index.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<web-site-link label="github地址:" :link="link" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WebSiteLink from '../WebSiteLink/index.vue';
|
||||
|
||||
interface Props {
|
||||
/** github链接 */
|
||||
link: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
<style scoped></style>
|
||||
77
src/components/custom/IconSelect/index.vue
Normal file
77
src/components/custom/IconSelect/index.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<n-popover placement="bottom-end" trigger="click">
|
||||
<template #trigger>
|
||||
<n-input v-model:value="modelValue" readonly placeholder="点击选择图标">
|
||||
<template #suffix>
|
||||
<Icon :icon="modelValue ? modelValue : emptyIcon" class="text-30px p-5px" />
|
||||
</template>
|
||||
</n-input>
|
||||
</template>
|
||||
<template #header>
|
||||
<n-input v-model:value="searchValue" placeholder="搜索图标"></n-input>
|
||||
</template>
|
||||
<div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
|
||||
<template v-for="iconItem in iconsList" :key="iconItem">
|
||||
<Icon
|
||||
:icon="iconItem"
|
||||
class="border-1px border-[#d9d9d9] text-30px m-2px p-5px"
|
||||
:style="{ 'border-color': modelValue === iconItem ? theme.themeColor : '' }"
|
||||
@click="handleChange(iconItem)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<n-empty v-else class="w-306px" description="你什么也找不到" />
|
||||
</n-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { NPopover, NInput, NEmpty } from 'naive-ui';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
interface Props {
|
||||
/** 选中的图标 */
|
||||
value: string;
|
||||
/** 图标列表 */
|
||||
icons: string[];
|
||||
/** 未选中图标 */
|
||||
emptyIcon?: string;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:value', val: string): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
emptyIcon: 'mdi:apps'
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
const searchValue = ref('');
|
||||
const iconsList = computed(() => props.icons.filter(v => v.includes(searchValue.value)));
|
||||
|
||||
const modelValue = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val: string) {
|
||||
emit('update:value', val);
|
||||
}
|
||||
});
|
||||
|
||||
function handleChange(iconItem: string) {
|
||||
modelValue.value = iconItem;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.n-input-wrapper) {
|
||||
padding-right: 0;
|
||||
}
|
||||
:deep(.n-input__suffix) {
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
</style>
|
||||
20
src/components/custom/WebSiteLink/index.vue
Normal file
20
src/components/custom/WebSiteLink/index.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<p>
|
||||
<span>{{ label }}</span>
|
||||
<a class="text-blue-500" :href="link" target="_blank">
|
||||
{{ link }}
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
/** 网址名称 */
|
||||
label: string;
|
||||
/** 网址链接 */
|
||||
link: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@@ -3,5 +3,8 @@ import ButtonTab from './ButtonTab/index.vue';
|
||||
import ChromeTab from './ChromeTab/index.vue';
|
||||
import CountTo from './CountTo/index.vue';
|
||||
import ImageVerify from './ImageVerify/index.vue';
|
||||
import WebSiteLink from './WebSiteLink/index.vue';
|
||||
import GithubLink from './GithubLink/index.vue';
|
||||
import IconSelect from './IconSelect/index.vue';
|
||||
|
||||
export { BetterScroll, ButtonTab, ChromeTab, CountTo, ImageVerify };
|
||||
export { BetterScroll, ButtonTab, ChromeTab, CountTo, ImageVerify, WebSiteLink, GithubLink, IconSelect };
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './service';
|
||||
export * from './regexp';
|
||||
export * from './map-sdk';
|
||||
|
||||
9
src/config/common/map-sdk.ts
Normal file
9
src/config/common/map-sdk.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/** 百度地图sdk地址 */
|
||||
export const BAIDU_MAP_SDK_URL =
|
||||
'https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1';
|
||||
|
||||
/** 高德地图sdk地址 */
|
||||
export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d';
|
||||
|
||||
/** 腾讯地图sdk地址 */
|
||||
export const TENCENT_MAP_SDK_URL = 'https://map.qq.com/api/gljs?v=1.exp&key=A6DBZ-KXPLW-JKSRY-ONZF4-CPHY3-K6BL7';
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'virtual:windi.css';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import 'swiper/css/pagination';
|
||||
import '../styles/css/global.css';
|
||||
|
||||
/** 引入静态资源(全局引入css、字体等) */
|
||||
|
||||
9
src/typings/common/map.d.ts
vendored
Normal file
9
src/typings/common/map.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="@amap/amap-jsapi-types" />
|
||||
/// <reference types="bmapgl" />
|
||||
|
||||
declare namespace BMap {
|
||||
class Map extends BMapGL.Map {}
|
||||
class Point extends BMapGL.Point {}
|
||||
}
|
||||
|
||||
declare const TMap: any;
|
||||
10
src/typings/common/route.d.ts
vendored
10
src/typings/common/route.d.ts
vendored
@@ -26,6 +26,16 @@ declare namespace AuthRoute {
|
||||
| 'component_button'
|
||||
| 'component_card'
|
||||
| 'component_table'
|
||||
| 'plugin'
|
||||
| 'plugin_map'
|
||||
| 'plugin_video'
|
||||
| 'plugin_editor'
|
||||
| 'plugin_editor_quill'
|
||||
| 'plugin_editor_markdown'
|
||||
| 'plugin_copy'
|
||||
| 'plugin_icon'
|
||||
| 'plugin_print'
|
||||
| 'plugin_swiper'
|
||||
| 'exception'
|
||||
| 'exception_403'
|
||||
| 'exception_404'
|
||||
|
||||
@@ -15,6 +15,14 @@ import {
|
||||
ComponentButton,
|
||||
ComponentCard,
|
||||
ComponentTable,
|
||||
PluginMap,
|
||||
PluginVideo,
|
||||
PluginEditorQuill,
|
||||
PluginEditorMarkdown,
|
||||
PluginSwiper,
|
||||
PluginCopy,
|
||||
PluginIcon,
|
||||
PluginPrint,
|
||||
MultiMenuFirstSecond,
|
||||
MultiMenuFirstSecondNewThird,
|
||||
About
|
||||
@@ -43,6 +51,8 @@ type ViewComponentKey = Exclude<
|
||||
| 'document'
|
||||
| 'document_project'
|
||||
| 'component'
|
||||
| 'plugin'
|
||||
| 'plugin_editor'
|
||||
| 'multi-menu'
|
||||
| 'multi-menu_first'
|
||||
| 'multi-menu_first_second-new'
|
||||
@@ -70,6 +80,14 @@ export function getViewComponent(routeKey: AuthRoute.RouteKey) {
|
||||
'component_button',
|
||||
'component_card',
|
||||
'component_table',
|
||||
'plugin_map',
|
||||
'plugin_video',
|
||||
'plugin_editor_quill',
|
||||
'plugin_editor_markdown',
|
||||
'plugin_copy',
|
||||
'plugin_icon',
|
||||
'plugin_print',
|
||||
'plugin_swiper',
|
||||
'exception_403',
|
||||
'exception_404',
|
||||
'exception_500',
|
||||
@@ -95,6 +113,14 @@ export function getViewComponent(routeKey: AuthRoute.RouteKey) {
|
||||
component_button: ComponentButton,
|
||||
component_card: ComponentCard,
|
||||
component_table: ComponentTable,
|
||||
plugin_map: PluginMap,
|
||||
plugin_video: PluginVideo,
|
||||
plugin_editor_quill: PluginEditorQuill,
|
||||
plugin_editor_markdown: PluginEditorMarkdown,
|
||||
plugin_copy: PluginCopy,
|
||||
plugin_icon: PluginIcon,
|
||||
plugin_print: PluginPrint,
|
||||
plugin_swiper: PluginSwiper,
|
||||
exception_403: NoPermission,
|
||||
exception_404: NotFound,
|
||||
exception_500: ServiceError,
|
||||
|
||||
@@ -2,5 +2,6 @@ export * from './system';
|
||||
export * from './dashboard';
|
||||
export * from './document';
|
||||
export * from './component';
|
||||
export * from './about';
|
||||
export * from './plugin';
|
||||
export * from './multi-menu';
|
||||
export * from './about';
|
||||
|
||||
34
src/views/plugin/copy/index.vue
Normal file
34
src/views/plugin/copy/index.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="文本复制" class="h-full shadow-sm rounded-16px">
|
||||
<n-input-group>
|
||||
<n-input v-model:value="source" placeholder="请输入要复制的内容吧" />
|
||||
<n-button type="primary" @click="handleCopy">复制</n-button>
|
||||
</n-input-group>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { NCard, NInputGroup, NInput, NButton, useMessage } from 'naive-ui';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
|
||||
const source = ref('');
|
||||
const message = useMessage();
|
||||
const { copy, isSupported } = useClipboard();
|
||||
|
||||
function handleCopy() {
|
||||
if (!isSupported) {
|
||||
message.error('您的浏览器不支持Clipboard API');
|
||||
return;
|
||||
}
|
||||
if (!source.value) {
|
||||
message.error('请输入要复制的内容');
|
||||
return;
|
||||
}
|
||||
copy(source.value);
|
||||
message.success(`复制成功:${source.value}`);
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
50
src/views/plugin/editor/markdown/index.vue
Normal file
50
src/views/plugin/editor/markdown/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="markdown插件" class="shadow-sm rounded-16px">
|
||||
<div ref="domRef"></div>
|
||||
<template #footer>
|
||||
<github-link link="https://github.com/Vanessa219/vditor" />
|
||||
</template>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { NCard } from 'naive-ui';
|
||||
import Vditor from 'vditor';
|
||||
import 'vditor/src/assets/scss/index.scss';
|
||||
import { GithubLink } from '@/components';
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
const vditor = ref<Vditor>();
|
||||
const domRef = ref<HTMLElement>();
|
||||
|
||||
function renderVditor() {
|
||||
vditor.value = new Vditor(domRef.value!, {
|
||||
minHeight: 400,
|
||||
theme: theme.darkMode ? 'dark' : 'classic',
|
||||
icon: 'material',
|
||||
cache: { enable: false }
|
||||
});
|
||||
}
|
||||
|
||||
const stopHandle = watch(
|
||||
() => theme.darkMode,
|
||||
newValue => {
|
||||
const themeMode = newValue ? 'dark' : 'classic';
|
||||
vditor.value?.setTheme(themeMode);
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
renderVditor();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopHandle();
|
||||
});
|
||||
</script>
|
||||
<style scoped></style>
|
||||
44
src/views/plugin/editor/quill/index.vue
Normal file
44
src/views/plugin/editor/quill/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="富文本插件" class="shadow-sm rounded-16px">
|
||||
<div ref="domRef" class="bg-white dark:bg-dark"></div>
|
||||
<template #footer>
|
||||
<github-link link="https://github.com/wangeditor-team/wangEditor" />
|
||||
</template>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { NCard } from 'naive-ui';
|
||||
import WangEditor from 'wangeditor';
|
||||
import { GithubLink } from '@/components';
|
||||
|
||||
const editor = ref<WangEditor>();
|
||||
const domRef = ref<HTMLElement>();
|
||||
|
||||
function renderWangEditor() {
|
||||
editor.value = new WangEditor(domRef.value);
|
||||
setEditorConfig();
|
||||
editor.value.create();
|
||||
}
|
||||
|
||||
function setEditorConfig() {
|
||||
editor.value!.config.zIndex = 10;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
renderWangEditor();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.w-e-toolbar) {
|
||||
background: inherit !important;
|
||||
border-color: #999 !important;
|
||||
}
|
||||
:deep(.w-e-text-container) {
|
||||
background: inherit;
|
||||
border-color: #999 !important;
|
||||
}
|
||||
</style>
|
||||
32
src/views/plugin/icon/icons.ts
Normal file
32
src/views/plugin/icon/icons.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export const icons = [
|
||||
'mdi:emoticon',
|
||||
'mdi:ab-testing',
|
||||
'ph:alarm',
|
||||
'ph:android-logo',
|
||||
'ph:align-bottom',
|
||||
'ph:archive-box-light',
|
||||
'uil:basketball',
|
||||
'uil:brightness-plus',
|
||||
'uil:capture',
|
||||
'mdi:apps-box',
|
||||
'mdi:alert',
|
||||
'mdi:airballoon',
|
||||
'mdi:airplane-edit',
|
||||
'mdi:alpha-f-box-outline',
|
||||
'mdi:arm-flex-outline',
|
||||
'ic:baseline-10mp',
|
||||
'ic:baseline-access-time',
|
||||
'ic:baseline-brightness-4',
|
||||
'ic:baseline-brightness-5',
|
||||
'ic:baseline-credit-card',
|
||||
'ic:baseline-filter-1',
|
||||
'ic:baseline-filter-2',
|
||||
'ic:baseline-filter-3',
|
||||
'ic:baseline-filter-4',
|
||||
'ic:baseline-filter-5',
|
||||
'ic:baseline-filter-6',
|
||||
'ic:baseline-filter-7',
|
||||
'ic:baseline-filter-8',
|
||||
'ic:baseline-filter-9',
|
||||
'ic:baseline-filter-9-plus'
|
||||
];
|
||||
31
src/views/plugin/icon/index.vue
Normal file
31
src/views/plugin/icon/index.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="Icon组件示例" class="shadow-sm rounded-16px">
|
||||
<div class="grid grid-cols-10">
|
||||
<template v-for="item in icons" :key="item">
|
||||
<div class="mt-5px flex-x-center">
|
||||
<Icon :icon="item" class="text-30px" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="mt-50px">
|
||||
<h1 class="mb-20px text-18px font-500">Icon图标选择器</h1>
|
||||
<icon-select v-model:value="selectVal" :icons="icons" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<web-site-link label="iconify地址:" link="https://icones.js.org/" class="mt-10px" />
|
||||
</template>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { NCard } from 'naive-ui';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { IconSelect, WebSiteLink } from '@/components';
|
||||
import { icons } from './icons';
|
||||
|
||||
const selectVal = ref('');
|
||||
</script>
|
||||
<style scoped></style>
|
||||
19
src/views/plugin/index.ts
Normal file
19
src/views/plugin/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
const PluginMap = () => import('./map/index.vue');
|
||||
const PluginVideo = () => import('./video/index.vue');
|
||||
const PluginEditorQuill = () => import('./editor/quill/index.vue');
|
||||
const PluginEditorMarkdown = () => import('./editor/markdown/index.vue');
|
||||
const PluginSwiper = () => import('./swiper/index.vue');
|
||||
const PluginCopy = () => import('./copy/index.vue');
|
||||
const PluginIcon = () => import('./icon/index.vue');
|
||||
const PluginPrint = () => import('./print/index.vue');
|
||||
|
||||
export {
|
||||
PluginMap,
|
||||
PluginVideo,
|
||||
PluginEditorQuill,
|
||||
PluginEditorMarkdown,
|
||||
PluginSwiper,
|
||||
PluginCopy,
|
||||
PluginIcon,
|
||||
PluginPrint
|
||||
};
|
||||
26
src/views/plugin/map/components/BaiduMap.vue
Normal file
26
src/views/plugin/map/components/BaiduMap.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-full"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useScriptTag } from '@vueuse/core';
|
||||
import { BAIDU_MAP_SDK_URL } from '@/config';
|
||||
|
||||
const { load } = useScriptTag(BAIDU_MAP_SDK_URL);
|
||||
|
||||
const domRef = ref<HTMLDivElement>();
|
||||
|
||||
async function renderBaiduMap() {
|
||||
await load(true);
|
||||
const map = new BMap.Map(domRef.value!);
|
||||
const point = new BMap.Point(114.05834626586915, 22.546789983033168);
|
||||
map.centerAndZoom(point, 15);
|
||||
map.enableScrollWheelZoom();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
renderBaiduMap();
|
||||
});
|
||||
</script>
|
||||
<style scoped></style>
|
||||
29
src/views/plugin/map/components/GaodeMap.vue
Normal file
29
src/views/plugin/map/components/GaodeMap.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div ref="domRef" class="w-full h-full"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useScriptTag } from '@vueuse/core';
|
||||
import { GAODE_MAP_SDK_URL } from '@/config';
|
||||
|
||||
const { load } = useScriptTag(GAODE_MAP_SDK_URL);
|
||||
|
||||
const domRef = ref<HTMLDivElement>();
|
||||
|
||||
async function renderBaiduMap() {
|
||||
await load(true);
|
||||
const map = new AMap.Map(domRef.value!, {
|
||||
zoom: 11,
|
||||
center: [114.05834626586915, 22.546789983033168],
|
||||
viewMode: '3D'
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
renderBaiduMap();
|
||||
});
|
||||
</script>
|
||||
<style scoped></style>
|
||||
29
src/views/plugin/map/components/TencentMap.vue
Normal file
29
src/views/plugin/map/components/TencentMap.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div ref="domRef"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useScriptTag } from '@vueuse/core';
|
||||
import { TENCENT_MAP_SDK_URL } from '@/config';
|
||||
|
||||
const { load } = useScriptTag(TENCENT_MAP_SDK_URL);
|
||||
|
||||
const domRef = ref<HTMLDivElement | null>(null);
|
||||
|
||||
async function renderBaiduMap() {
|
||||
await load(true);
|
||||
const map = new TMap.Map(domRef.value!, {
|
||||
center: new TMap.LatLng(39.98412, 116.307484),
|
||||
zoom: 11,
|
||||
viewMode: '3D'
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
renderBaiduMap();
|
||||
});
|
||||
</script>
|
||||
<style scoped></style>
|
||||
5
src/views/plugin/map/components/index.ts
Normal file
5
src/views/plugin/map/components/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import BaiduMap from './BaiduMap.vue';
|
||||
import GaodeMap from './GaodeMap.vue';
|
||||
import TencentMap from './TencentMap.vue';
|
||||
|
||||
export { BaiduMap, GaodeMap, TencentMap };
|
||||
29
src/views/plugin/map/index.vue
Normal file
29
src/views/plugin/map/index.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="地图插件" class="h-full shadow-sm rounded-16px" content-style="overflow:hidden">
|
||||
<n-tabs type="line" class="flex-col-stretch h-full" pane-class="flex-1-hidden">
|
||||
<n-tab-pane v-for="item in maps" :key="item.id" :name="item.id" :tab="item.label">
|
||||
<component :is="item.component" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue';
|
||||
import { NCard, NTabs, NTabPane } from 'naive-ui';
|
||||
import { GaodeMap, TencentMap } from './components';
|
||||
|
||||
interface Map {
|
||||
id: string;
|
||||
label: string;
|
||||
component: Component;
|
||||
}
|
||||
|
||||
const maps: Map[] = [
|
||||
{ id: 'gaode', label: '高德地图', component: GaodeMap },
|
||||
{ id: 'tencent', label: '腾讯地图', component: TencentMap }
|
||||
];
|
||||
</script>
|
||||
<style scoped></style>
|
||||
40
src/views/plugin/print/index.vue
Normal file
40
src/views/plugin/print/index.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="打印" class="shadow-sm rounded-16px">
|
||||
<n-button type="primary" class="mr-10px" @click="printTable">打印表格</n-button>
|
||||
<n-button type="primary" @click="printImage">打印图片</n-button>
|
||||
<template #footer>
|
||||
<github-link label="printJS:" link="https://github.com/crabbly/Print.js" class="mt-10px" />
|
||||
</template>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { NCard, NButton } from 'naive-ui';
|
||||
import printJS from 'print-js';
|
||||
import { GithubLink } from '@/components';
|
||||
|
||||
function printTable() {
|
||||
printJS({
|
||||
printable: [
|
||||
{ name: 'soybean', wechat: 'honghuangdc', remark: '欢迎来技术交流' },
|
||||
{ name: 'soybean', wechat: 'honghuangdc', remark: '欢迎来技术交流' }
|
||||
],
|
||||
properties: ['name', 'wechat', 'remark'],
|
||||
type: 'json'
|
||||
});
|
||||
}
|
||||
function printImage() {
|
||||
printJS({
|
||||
printable: [
|
||||
'https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/qq_qrcode.JPG',
|
||||
'https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/qq_qrcode.JPG'
|
||||
],
|
||||
type: 'image',
|
||||
header: 'Multiple Images',
|
||||
imageStyle: 'width:100%;'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
118
src/views/plugin/swiper/index.vue
Normal file
118
src/views/plugin/swiper/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card title="Swiper插件" class="shadow-sm rounded-16px">
|
||||
<n-space :vertical="true">
|
||||
<github-link link="https://github.com/nolimits4web/swiper" />
|
||||
<web-site-link label="vue3版文档地址:" link="https://swiperjs.com/vue" />
|
||||
<web-site-link label="插件demo地址:" link="https://swiperjs.com/demos" />
|
||||
</n-space>
|
||||
<n-space :vertical="true">
|
||||
<div v-for="item in swiperExample" :key="item.id">
|
||||
<h3 class="py-24px text-24px font-bold">{{ item.label }}</h3>
|
||||
<swiper v-bind="item.options">
|
||||
<swiper-slide v-for="i in 5" :key="i">
|
||||
<div class="flex-center h-240px border-1px border-[#999] text-18px font-bold">Slide{{ i }}</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NCard, NSpace } from 'naive-ui';
|
||||
import SwiperCore, { Navigation, Pagination } from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import type { SwiperOptions } from 'swiper';
|
||||
import { WebSiteLink, GithubLink } from '@/components';
|
||||
|
||||
type SwiperExampleOptions = Pick<
|
||||
SwiperOptions,
|
||||
| 'navigation'
|
||||
| 'pagination'
|
||||
| 'scrollbar'
|
||||
| 'slidesPerView'
|
||||
| 'slidesPerGroup'
|
||||
| 'spaceBetween'
|
||||
| 'direction'
|
||||
| 'loop'
|
||||
| 'loopFillGroupWithBlank'
|
||||
>;
|
||||
|
||||
interface SwiperExample {
|
||||
id: number;
|
||||
label: string;
|
||||
options: Partial<SwiperExampleOptions>;
|
||||
}
|
||||
|
||||
SwiperCore.use([Navigation, Pagination]);
|
||||
|
||||
const swiperExample: SwiperExample[] = [
|
||||
{ id: 0, label: 'Default', options: {} },
|
||||
{
|
||||
id: 1,
|
||||
label: 'Navigation',
|
||||
options: {
|
||||
navigation: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: 'Pagination',
|
||||
options: {
|
||||
pagination: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: 'Pagination dynamic',
|
||||
options: {
|
||||
pagination: { dynamicBullets: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: 'Pagination progress',
|
||||
options: {
|
||||
navigation: true,
|
||||
pagination: {
|
||||
type: 'progressbar'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: 'Pagination fraction',
|
||||
options: {
|
||||
navigation: true,
|
||||
pagination: {
|
||||
type: 'fraction'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: 'Slides per view',
|
||||
options: {
|
||||
pagination: {
|
||||
clickable: true
|
||||
},
|
||||
slidesPerView: 3,
|
||||
spaceBetween: 30
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: 'Infinite loop',
|
||||
options: {
|
||||
navigation: true,
|
||||
pagination: {
|
||||
clickable: true
|
||||
},
|
||||
loop: true
|
||||
}
|
||||
}
|
||||
];
|
||||
</script>
|
||||
<style scoped></style>
|
||||
37
src/views/plugin/video/index.vue
Normal file
37
src/views/plugin/video/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="视频播放器插件" class="h-full shadow-sm rounded-16px">
|
||||
<div ref="domRef"></div>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { NCard } from 'naive-ui';
|
||||
import Player from 'xgplayer';
|
||||
|
||||
const domRef = ref<HTMLElement>();
|
||||
const player = ref<Player>();
|
||||
|
||||
function renderXgPlayer() {
|
||||
const url = 'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo.mp4';
|
||||
player.value = new Player({
|
||||
el: domRef.value!,
|
||||
url,
|
||||
playbackRate: [0.5, 0.75, 1, 1.5, 2]
|
||||
});
|
||||
}
|
||||
function destroyXgPlayer() {
|
||||
player.value?.destroy();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
renderXgPlayer();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
destroyXgPlayer();
|
||||
});
|
||||
</script>
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user