This commit is contained in:
孟帅
2024-03-07 20:08:56 +08:00
parent 6dd8cbadad
commit 0fbc1ad47c
246 changed files with 9441 additions and 2293 deletions

View File

@@ -0,0 +1,42 @@
<template>
<div class="n-layout-page-header">
<n-card :bordered="false" title="日历"> 一个普通的日历 </n-card>
</div>
<n-card :bordered="false" class="mt-4 proCard">
<n-calendar
v-model:value="value"
#="{ year, month, date }"
:is-date-disabled="isDateDisabled"
@update:value="handleUpdateValue"
>
{{ year }}-{{ month }}-{{ date }}
</n-calendar>
</n-card>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useMessage } from 'naive-ui';
import { isYesterday, addDays } from 'date-fns/esm';
export default defineComponent({
setup() {
const message = useMessage();
return {
value: ref(addDays(Date.now(), 1).valueOf()),
handleUpdateValue(
_: number,
{ year, month, date }: { year: number; month: number; date: number }
) {
message.success(`${year}-${month}-${date}`);
},
isDateDisabled(timestamp: number) {
if (isYesterday(timestamp)) {
return true;
}
return false;
},
};
},
});
</script>

View File

@@ -0,0 +1,89 @@
<template>
<n-space vertical>
<n-card>
<template #header>基础用法</template>
<n-descriptions label-placement="top" title="描述">
<n-descriptions-item>
<template #label> 早餐 </template>
苹果
</n-descriptions-item>
<n-descriptions-item label="早午餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="午餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="晚餐" :span="2">
两个<br />
苹果
</n-descriptions-item>
<n-descriptions-item label="夜宵"> 苹果 </n-descriptions-item>
</n-descriptions>
</n-card>
<n-card>
<template #header>每个描述项都可以设定跨度</template>
<n-descriptions label-placement="top" bordered :column="6">
<n-descriptions-item>
<template #label> 早餐 </template>
苹果
</n-descriptions-item>
<n-descriptions-item label="早午餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="午餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="晚餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="正餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="夜宵"> 苹果 </n-descriptions-item>
<n-descriptions-item label="苹果"> 苹果 </n-descriptions-item>
<n-descriptions-item label="苹果" :span="2"> 苹果 </n-descriptions-item>
<n-descriptions-item label="苹果" :span="3"> 苹果 </n-descriptions-item>
</n-descriptions>
</n-card>
<n-card>
<template #header>边框</template>
<template #header-extra> 如果有很多多行的信息你可以把它设为 bordered </template>
<n-descriptions bordered>
<n-descriptions-item>
<template #label> 早餐 </template>
苹果
</n-descriptions-item>
<n-descriptions-item label="午餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="晚餐"> 苹果 </n-descriptions-item>
<n-descriptions-item label="为什么长">
为什么<br /><br /><br /><br /><br />
</n-descriptions-item>
<n-descriptions-item label="为什么长">
为什么<br /><br /><br /><br /><br />
</n-descriptions-item>
<n-descriptions-item label="为什么长">
为什么<br /><br /><br /><br /><br />
</n-descriptions-item>
</n-descriptions>
</n-card>
<n-card>
<n-list hoverable clickable>
<n-list-item>
<n-thing title="相见恨晚" content-style="margin-top: 10px;">
<template #description>
<n-space size="small" style="margin-top: 4px">
<n-tag :bordered="false" type="info" size="small"> 暑夜 </n-tag>
<n-tag :bordered="false" type="info" size="small"> 晚春 </n-tag>
</n-space>
</template>
奋勇呀然后休息呀<br />
完成你伟大的人生
</n-thing>
</n-list-item>
<n-list-item>
<n-thing title="他在时间门外" content-style="margin-top: 10px;">
<template #description>
<n-space size="small" style="margin-top: 4px">
<n-tag :bordered="false" type="info" size="small"> 环形公路 </n-tag>
<n-tag :bordered="false" type="info" size="small"> 潜水艇司机 </n-tag>
</n-space>
</template>
最新的打印机<br />
复制着彩色傀儡<br />
早上好<br />
让他带你去被工厂敲击
</n-thing>
</n-list-item>
</n-list>
</n-card>
</n-space>
</template>
<script setup></script>

View File

@@ -0,0 +1,58 @@
<template>
<div class="card content-box">
<n-card :bordered="false" title="复制指令">
<n-space>
<n-input placeholder="输入内容试试" v-model:value="data" style="width: 350px" />
<n-button v-copy="data" type="primary" @click="a"> </n-button>
</n-space>
</n-card>
<n-card :bordered="false" title="防抖指令" class="mt-3">
<n-button type="primary" v-debounce="b">防抖测试</n-button>
</n-card>
<n-card :bordered="false" title="节流指令" class="mt-3">
<n-button type="primary" v-throttle="c">节流测试</n-button>
</n-card>
<n-card :bordered="false" title="拖拽指令" class="mt-3"> 鼠标放到矩形上面拖拽试试 </n-card>
<div class="box" v-draggable> </div>
</div>
</template>
<script setup lang="ts" name="copyDirect">
import { ref } from 'vue';
import { useMessage } from 'naive-ui';
const data = ref<string>();
const message = useMessage();
const a = () => {
message.success('复制成功:' + data.value);
};
const b = () => {
message.success('防抖');
console.log(data.value);
};
const c = () => {
message.success('节流');
console.log(data.value);
};
</script>
<style scoped lang="less">
body {
width: 100px;
height: 100px;
background-color: #ccc;
position: relative;
}
.content-box {
height: 100vh;
.box {
width: 100px;
height: 100px;
background-color: #2d8cf0;
position: absolute;
z-index: 10000000;
border-radius: 10px;
margin: 20px 5px;
}
}
</style>

View File

@@ -0,0 +1,162 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="拖拽"> 常用于卡片事项预约流程计划等 </n-card>
</div>
<n-alert title="花式拖拽演示" type="info" class="mt-4">
每个卡片都可以上下拖拽顺序另外不同卡片也可以拖拽过去拖拽过来都不在话下呢快试试O(_)O哈哈~
</n-alert>
<n-grid
cols="1 s:2 m:3 l:4 xl:4 2xl:4"
class="mt-4 proCard"
responsive="screen"
:x-gap="12"
:y-gap="8"
>
<n-grid-item>
<NCard
title="需求池"
:segmented="{ content: true, footer: true }"
size="small"
:bordered="false"
>
<template #header-extra>
<n-tag type="info"></n-tag>
</template>
<Draggable
class="draggable-ul"
animation="300"
:list="demandList"
group="people"
itemKey="name"
>
<template #item="{ element }">
<div class="cursor-move draggable-li">
<n-tag type="info">需求</n-tag><span class="ml-2">{{ element.name }}</span>
</div>
</template>
</Draggable>
</NCard>
</n-grid-item>
<n-grid-item>
<NCard
title="开发中"
:segmented="{ content: true, footer: true }"
size="small"
:bordered="false"
>
<template #header-extra>
<n-tag type="info"></n-tag>
</template>
<Draggable
class="draggable-ul"
animation="300"
:list="exploitList"
group="people"
itemKey="name"
>
<template #item="{ element }">
<div class="cursor-move draggable-li">
<n-tag type="warning">开发中</n-tag><span class="ml-2">{{ element.name }}</span>
</div>
</template>
</Draggable>
</NCard>
</n-grid-item>
<n-grid-item>
<NCard
title="已完成"
:segmented="{ content: true, footer: true }"
size="small"
:bordered="false"
>
<template #header-extra>
<n-tag type="info"></n-tag>
</template>
<Draggable
class="draggable-ul"
animation="300"
:list="completeList"
group="people"
itemKey="name"
>
<template #item="{ element }">
<div class="cursor-move draggable-li">
<n-tag type="error">已完成</n-tag><span class="ml-2">{{ element.name }}</span>
</div>
</template>
</Draggable>
</NCard>
</n-grid-item>
<n-grid-item>
<NCard
title="已验收"
:segmented="{ content: true, footer: true }"
size="small"
:bordered="false"
>
<template #header-extra>
<n-tag type="info"></n-tag>
</template>
<Draggable
class="draggable-ul"
animation="300"
:list="approvedList"
group="people"
itemKey="name"
>
<template #item="{ element }">
<div class="cursor-move draggable-li">
<n-tag type="success">已验收</n-tag><span class="ml-2">{{ element.name }}</span>
</div>
</template>
</Draggable>
</NCard>
</n-grid-item>
</n-grid>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import Draggable from 'vuedraggable';
const demandList = reactive([
{ name: '预约表单页面,能填写预约相关信息', id: 1 },
{ name: '促销活动页面,包含促销广告展示', id: 2 },
{ name: '商品列表,需要一个到货提醒功能', id: 3 },
{ name: '商品需要一个评价功能', id: 4 },
{ name: '商品图片需要提供放大镜', id: 5 },
{ name: '订单需要提供删除到回收站', id: 6 },
{ name: '用户头像上传,需要支持裁剪', id: 7 },
{ name: '据说Vue3.2发布了setup啥时候支持', id: 8 },
]);
const exploitList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
const completeList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
const approvedList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
</script>
<style lang="less" scoped>
.draggable-ul {
width: 100%;
overflow: hidden;
margin-top: -16px;
.draggable-li {
width: 100%;
padding: 16px 10px;
color: #333;
border-bottom: 1px solid #efeff5;
}
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<n-card :segmented="{ content: true, footer: true }" footer-style="padding:10px">
<template #header>
通过设备浏览器信息获取浏览器指纹的插件(官方宣称其识别精度达到99.5%)
</template>
<div>
指纹ID:
<n-text type="info">
{{ compData.murmur }}
</n-text>
</div>
</n-card>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import Fingerprint2 from 'fingerprintjs2';
const compData = reactive({
values: {},
murmur: '',
});
const createFingerprint = () => {
Fingerprint2.get((components) => {
compData.values = components.map((component) => component.value); // 配置的值的数组
compData.murmur = Fingerprint2.x64hash128(compData.values.join(''), 31).toUpperCase(); // 生成浏览器指纹
});
};
if (window.requestIdleCallback) {
requestIdleCallback(() => {
createFingerprint();
});
} else {
setTimeout(() => {
createFingerprint();
}, 600);
}
</script>

View File

@@ -0,0 +1,175 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="基础表单"> 基础表单用于向用户收集表单信息 </n-card>
</div>
<n-card :bordered="false" class="mt-4 proCard">
<div class="BasicForm">
<BasicForm
submitButtonText="提交预约"
layout="horizontal"
:gridProps="{ cols: 1 }"
:schemas="schemas"
@submit="handleSubmit"
@reset="handleReset"
>
<template #statusSlot="{ model, field }">
<n-input v-model:value="model[field]" />
</template>
</BasicForm>
</div>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { BasicForm, FormSchema } from '@/components/Form/index';
import { useMessage } from 'naive-ui';
const schemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '姓名',
labelMessage: '这是一个提示',
componentProps: {
placeholder: '请输入姓名',
onInput: (e: any) => {
console.log(e);
},
},
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
},
{
field: 'mobile',
component: 'NInputNumber',
label: '手机',
componentProps: {
placeholder: '请输入手机号码',
showButton: false,
onInput: (e: any) => {
console.log(e);
},
},
},
{
field: 'type',
component: 'NSelect',
label: '类型',
componentProps: {
placeholder: '请选择类型',
options: [
{
label: '舒适性',
value: 1,
},
{
label: '经济性',
value: 2,
},
],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeDate',
component: 'NDatePicker',
label: '预约时间',
defaultValue: 1183135260000,
componentProps: {
type: 'date',
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeTime',
component: 'NTimePicker',
label: '停留时间',
componentProps: {
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeProject',
component: 'NCheckbox',
label: '预约项目',
componentProps: {
placeholder: '请选择预约项目',
options: [
{
label: '种牙',
value: 1,
},
{
label: '补牙',
value: 2,
},
{
label: '根管',
value: 3,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeSource',
component: 'NRadioGroup',
label: '来源',
componentProps: {
options: [
{
label: '网上',
value: 1,
},
{
label: '门店',
value: 2,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'status',
label: '状态',
//插槽
slot: 'statusSlot',
},
];
const message = useMessage();
function handleSubmit(values: Recordable) {
if (!values) {
return message.error('请填写完整信息');
}
console.log(values);
message.success(JSON.stringify(values));
}
function handleReset(values: Recordable) {
console.log(values);
}
</script>
<style lang="less" scoped>
.BasicForm {
width: 550px;
margin: 0 auto;
overflow: hidden;
padding-top: 20px;
}
</style>

View File

@@ -0,0 +1,198 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="基础表单"> useForm 表单用于向用户收集表单信息 </n-card>
</div>
<n-card :bordered="false" class="mt-4 proCard">
<div class="BasicForm">
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
<template #statusSlot="{ model, field }">
<n-input v-model:value="model[field]" />
</template>
</BasicForm>
</div>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
import { useMessage } from 'naive-ui';
const schemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '姓名',
labelMessage: '这是一个提示',
giProps: {
span: 1,
},
componentProps: {
placeholder: '请输入姓名',
onInput: (e: any) => {
console.log(e);
},
},
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
},
{
field: 'mobile',
component: 'NInputNumber',
label: '手机',
componentProps: {
placeholder: '请输入手机号码',
showButton: false,
onInput: (e: any) => {
console.log(e);
},
},
},
{
field: 'type',
component: 'NSelect',
label: '类型',
giProps: {
//span: 24,
},
componentProps: {
placeholder: '请选择类型',
options: [
{
label: '舒适性',
value: 1,
},
{
label: '经济性',
value: 2,
},
],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeDate',
component: 'NDatePicker',
label: '预约时间',
giProps: {
//span: 24,
},
defaultValue: 1183135260000,
componentProps: {
type: 'date',
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeTime',
component: 'NTimePicker',
label: '停留时间',
giProps: {
//span: 24,
},
componentProps: {
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeProject',
component: 'NCheckbox',
label: '预约项目',
giProps: {
//span: 24,
},
componentProps: {
placeholder: '请选择预约项目',
options: [
{
label: '种牙',
value: 1,
},
{
label: '补牙',
value: 2,
},
{
label: '根管',
value: 3,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeSource',
component: 'NRadioGroup',
label: '来源',
giProps: {
//span: 24,
},
componentProps: {
options: [
{
label: '网上',
value: 1,
},
{
label: '门店',
value: 2,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'status',
label: '状态',
giProps: {
//span: 24,
},
//插槽
slot: 'statusSlot',
},
];
const message = useMessage();
const [register, {}] = useForm({
gridProps: { cols: 1 },
collapsedRows: 3,
labelWidth: 120,
layout: 'horizontal',
submitButtonText: '提交预约',
schemas,
});
function handleSubmit(values: Recordable) {
if (!values) {
return message.error('请填写完整信息');
}
console.log(values);
message.success(JSON.stringify(values));
}
function handleReset(values: Recordable) {
console.log(values);
}
</script>
<style lang="less" scoped>
.BasicForm {
width: 550px;
margin: 0 auto;
overflow: hidden;
padding-top: 20px;
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<div>
<n-spin :show="loading" description="请稍候..">
<Icons ref="iconsRef" />
</n-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Icons from './icons.vue';
const iconsRef = ref();
const loading = ref(true);
onMounted(() => {
setTimeout(() => {
iconsRef.value?.open('antd');
loading.value = false;
}, 10);
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,136 @@
<template>
<div>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
content-style=""
>
<template #header>
<n-grid y-gap="20" x-gap="10" cols="24" item-responsive responsive="screen">
<n-grid-item span="24 m:12 l:6">
<n-input-group>
<n-button>图标大小</n-button>
<n-input-number style="width: 100%" v-model:value="compData.size" />
</n-input-group>
</n-grid-item>
<n-grid-item span="24 m:12 l:6">
<n-color-picker
:modes="['hex']"
style="width: 100%"
v-model:value="compData.color"
:swatches="['#FFFFFF', '#18A058', '#2080F0', '#F0A020', '#D03050','#000000']"
>
<template #label>
<div style="color: white">图标颜色 {{ compData.color }}</div>
</template>
</n-color-picker>
</n-grid-item>
</n-grid>
</template>
<div class="icons">
<n-grid y-gap="20" x-gap="0" cols="24" item-responsive responsive="screen">
<n-grid-item v-for="(item, idx) in icons" :key="idx" span="12 m:4 l:3 xl:2">
<div class="icons-item">
<div class="icons-item_content">
<n-icon class="icon" :color="compData.color" :size="compData.size">
<component :is="item" />
</n-icon>
<span class="copy" v-copy="item.name" @click="handleCopy(item)">复制</span>
</div>
</div>
</n-grid-item>
</n-grid>
</div>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as AntdIcons from '@vicons/antd';
import * as Ionicons5 from '@vicons/ionicons5';
import { useMessage } from 'naive-ui';
import { useDesignSettingStore } from '@/store/modules/designSetting';
const designStore = useDesignSettingStore();
const message = useMessage();
const icons = ref<any>([]);
const compData = ref({
color: designStore.appTheme,
size: 32,
});
const color = computed(() => {
return compData.value.color;
});
function handleCopy(icon) {
message.success(`已复制,${icon.name}`);
}
function open(type: string) {
if (type === 'antd') {
icons.value = AntdIcons;
}
if (type === 'ionicons5') {
icons.value = Ionicons5;
}
}
defineExpose({
open,
});
</script>
<style lang="less" scoped>
:deep(.n-color-picker-trigger .n-color-picker-trigger__fill) {
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.icons {
&-item {
width: 100%;
text-align: center;
padding: 0 10px;
position: relative;
overflow: hidden;
cursor: pointer;
&_content {
padding: 20px 10px;
border: 1px solid rgb(240 240 245);
box-sizing: border-box;
}
.icon {
transition: top 0.3s;
position: relative;
top: 0;
}
&:hover {
.icon {
top: -10px;
}
.copy {
bottom: 0;
}
}
.copy {
position: absolute;
bottom: -30px;
height: 30px;
left: 10px;
right: 10px;
line-height: 30px;
background-color: v-bind(color);
transition: bottom 0.3s;
color: rgb(240 240 245);
}
}
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<div>
<n-spin :show="loading" description="请稍候..">
<Icons ref="iconsRef" />
</n-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Icons from './icons.vue';
const iconsRef = ref();
const loading = ref(true);
onMounted(() => {
setTimeout(() => {
iconsRef.value?.open('ionicons5');
loading.value = false;
}, 10);
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,29 @@
<template>
<div>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="图标选择器">
图标选择器用于表单选择图标支持直接选定图标和输入图标名称来选择
</n-card>
</div>
<n-space>
<n-card :bordered="false" class="mt-4 proCard" title="Antd">
<IconSelector style="width: 100%" v-model:value="antdValue" option="antd" />
</n-card>
<n-card :bordered="false" class="mt-4 proCard" title="Ionicons5">
<IconSelector style="width: 100%" v-model:value="ionicons5Value" option="ionicons5" />
</n-card>
</n-space>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import IconSelector from '@/components/IconSelector/index.vue';
const antdValue = ref('');
const ionicons5Value = ref('');
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,68 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="导入excel">
将excel表格数据导入可解析出完整的表格内容包括所有的sheet和行列数据
</n-card>
</div>
<n-card :bordered="false" class="mt-4 proCard">
<n-upload
directory-dnd
:custom-request="handleUpload"
name="file"
type="file"
accept=".xlsx, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
>
<n-upload-dragger>
<div style="margin-bottom: 12px">
<n-icon size="48" :depth="3">
<DownloadOutlined />
</n-icon>
</div>
<n-text style="font-size: 16px"> 点击或者拖动.xlsx文件到该区域来上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0"> 单次上传数据最大不建议超过5000条</n-p>
</n-upload-dragger>
</n-upload>
</n-card>
<n-card
:bordered="false"
class="proCard mt-4"
size="small"
:segmented="{ content: true }"
title="表格数据"
>
<n-scrollbar style="max-height: 520px">
<JsonViewer :value="response" :expand-depth="5" copyable boxed sort class="json-width" />
</n-scrollbar>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { UploadCustomRequestOptions, useMessage } from 'naive-ui';
import { DownloadOutlined } from '@vicons/antd';
import { ImportExcel } from '@/api/addons/hgexample/comp';
import type { UploadFileParams } from '@/utils/http/axios/types';
import { JsonViewer } from 'vue3-json-viewer';
import 'vue3-json-viewer/dist/index.css';
const message = useMessage();
const response = ref<any>({});
function handleUpload(options: UploadCustomRequestOptions) {
message.loading('正在导入,请稍候...', { duration: 1200 });
const params: UploadFileParams = {
file: options.file.file as File,
};
ImportExcel(params).then((res) => {
response.value = res;
message.destroyAll();
message.success('解析成功');
});
}
</script>
<style lang="less"></style>

View File

@@ -0,0 +1,369 @@
<template>
<div>
<n-card :bordered="false" class="proCard">
<n-p class="title">组件示例</n-p>
<n-space vertical>
<n-layout has-sider>
<n-scrollbar :style="scrollbarStyle">
<n-layout-sider
bordered
collapse-mode="width"
:collapsed-width="64"
:width="180"
:collapsed="collapsed"
show-trigger
@collapse="collapsed = true"
@expand="collapsed = false"
>
<n-menu
v-model:value="activeKey"
:collapsed="collapsed"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
/>
</n-layout-sider>
</n-scrollbar>
<n-layout>
<n-card :bordered="false" class="proCard">
<component :is="currentComponent" />
</n-card>
</n-layout>
</n-layout>
</n-space>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import type { MenuOption } from 'naive-ui';
import {
ProfileOutlined,
BorderOutlined,
NumberOutlined,
BorderOuterOutlined,
CheckCircleOutlined,
ExclamationCircleOutlined,
FundProjectionScreenOutlined,
ReadOutlined,
StarOutlined,
PushpinOutlined,
LayoutOutlined,
PrinterOutlined,
DownloadOutlined,
} from '@vicons/antd';
import {
HourglassOutline,
NotificationsOutline,
LocationOutline,
SwapVertical,
EllipsisHorizontalCircleOutline,
AlbumsOutline,
LogoChrome,
} from '@vicons/ionicons5';
import { renderIcon } from '@/utils';
import FormBasic from './form/basic.vue';
import FormUseForm from './form/useForm.vue';
import Modal from './modal/index.vue';
import Drag from './drag/index.vue';
import Directive from './directive/index.vue';
import ResultSuccess from './result/success.vue';
import ResultFail from './result/fail.vue';
import ResultInfo from './result/info.vue';
import Exception403 from '../../../exception/403.vue';
import Exception404 from '../../../exception/404.vue';
import Exception500 from '../../../exception/500.vue';
import VisPPchart from './vis/ppchart.vue';
import VisEcharts from './vis/echarts.vue';
import VisMadeAPie from './vis/madeapie.vue';
import TextPinyin from './text/pinyin/index.vue';
import TextMint from './text/mint/index.vue';
import TextGradient from './text/gradient/index.vue';
import TextHigh from './text/high/index.vue';
import IconsAntd from './icons/antd.vue';
import IconsIonicons5 from './icons/ionicons5.vue';
import IconsSelector from './icons/selector.vue';
import Watermark from './watermark/index.vue';
import Des from './des/index.vue';
import Calendar from './calendar/index.vue';
import Timeline from './timeline/index.vue';
import Notice from './notice/index.vue';
import MapGaode from './map/gaode.vue';
import MapBaidu from './map/baidu.vue';
import Print from './print/index.vue';
import TagsView from './tagsView/index.vue';
import MoreComponents from './moreComponents/index.vue';
import Waterfall from './waterfall/index.vue';
import ImportExcel from './import/excel.vue';
import FingerPrintJs from './fingerprintjs/index.vue';
const components = {
formBasic: FormBasic,
formUseForm: FormUseForm,
modal: Modal,
drag: Drag,
directive: Directive,
resultSuccess: ResultSuccess,
resultFail: ResultFail,
resultInfo: ResultInfo,
exception403: Exception403,
exception404: Exception404,
exception500: Exception500,
visPPchart: VisPPchart,
visEcharts: VisEcharts,
visMadeAPie: VisMadeAPie,
textPinyin: TextPinyin,
textMint: TextMint,
textGradient: TextGradient,
textHigh: TextHigh,
iconsAntd: IconsAntd,
iconsIonicons5: IconsIonicons5,
iconsSelector: IconsSelector,
watermark: Watermark,
des: Des,
calendar: Calendar,
timeline: Timeline,
notice: Notice,
mapGaode: MapGaode,
mapBaidu: MapBaidu,
print: Print,
tagsView: TagsView,
moreComponents: MoreComponents,
waterfall: Waterfall,
importExcel: ImportExcel,
fingerPrintJs: FingerPrintJs,
};
const activeKey = ref<string>('formBasic');
const collapsed = ref(false);
const scrollbarStyle = computed(() => {
const height = '82vh';
const width = collapsed.value ? '74px' : '188px';
return {
height: height,
'max-height': height,
'min-width': width,
width: width,
};
});
const currentComponent = computed(() => {
return components[activeKey.value] || null;
});
const menuOptions: MenuOption[] = [
{
label: '表单',
key: 'form',
icon: renderIcon(ProfileOutlined),
children: [
{
label: '基础使用',
key: 'formBasic',
},
{
label: 'useForm',
key: 'formUseForm',
},
],
},
{
label: '图标',
key: 'icons',
icon: renderIcon(StarOutlined),
children: [
{
label: 'Antd',
key: 'iconsAntd',
},
{
label: 'IonIcons5',
key: 'iconsIonicons5',
},
{
label: '选择器',
key: 'iconsSelector',
},
],
},
{
label: '文字处理',
key: 'text',
icon: renderIcon(ReadOutlined),
children: [
{
label: '汉字拼音',
key: 'textPinyin',
},
{
label: '敏感词汇',
key: 'textMint',
},
{
label: '渐变文字',
key: 'textGradient',
},
{
label: '文字高亮',
key: 'textHigh',
},
],
},
{
label: '可视化',
key: 'vis',
icon: renderIcon(FundProjectionScreenOutlined),
children: [
{
label: 'PPChart',
key: 'visPPchart',
},
{
label: 'Echarts',
key: 'visEcharts',
},
{
label: 'MadeAPie',
key: 'visMadeAPie',
},
],
},
{
label: '异常页面',
key: 'exception',
icon: renderIcon(ExclamationCircleOutlined),
children: [
{
label: '403',
key: 'exception403',
},
{
label: '404',
key: 'exception404',
},
{
label: '500',
key: 'exception500',
},
],
},
{
label: '结果页面',
key: 'result',
icon: renderIcon(CheckCircleOutlined),
children: [
{
label: '成功页',
key: 'resultSuccess',
},
{
label: '失败页',
key: 'resultFail',
},
{
label: '信息页',
key: 'resultInfo',
},
],
},
{
label: '地图',
key: 'map',
icon: renderIcon(LocationOutline),
children: [
{
label: '高德地图',
key: 'mapGaode',
},
{
label: '百度地图',
key: 'mapBaidu',
},
],
},
{
label: '弹窗扩展',
key: 'modal',
icon: renderIcon(BorderOutlined),
},
{
label: '消息提示',
key: 'notice',
icon: renderIcon(NotificationsOutline),
},
{
label: '瀑布流',
key: 'waterfall',
icon: renderIcon(AlbumsOutline),
},
{
label: '拖拽',
key: 'drag',
icon: renderIcon(NumberOutlined),
},
{
label: '水印',
key: 'watermark',
icon: renderIcon(PushpinOutlined),
},
{
label: '日历',
key: 'calendar',
icon: renderIcon(ReadOutlined),
},
{
label: '打印',
key: 'print',
icon: renderIcon(PrinterOutlined),
},
{
label: '时间线',
key: 'timeline',
icon: renderIcon(HourglassOutline),
},
{
label: 'tagsView',
key: 'tagsView',
icon: renderIcon(SwapVertical),
},
{
label: '导入Excel',
key: 'importExcel',
icon: renderIcon(DownloadOutlined),
},
{
label: '卡片描述',
key: 'des',
icon: renderIcon(LayoutOutlined),
},
{
label: '指令示例',
key: 'directive',
icon: renderIcon(BorderOuterOutlined),
},
{
label: '浏览器指纹',
key: 'fingerPrintJs',
icon: renderIcon(LogoChrome),
},
{
label: '更多组件',
key: 'moreComponents',
icon: renderIcon(EllipsisHorizontalCircleOutline),
},
];
</script>
<style lang="less" scoped>
.title {
font-size: 18px;
transition: color 0.3s var(--n-bezier);
flex: 1;
min-width: 0;
color: var(--n-title-text-color);
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<div class="container" ref="container"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import useCreateScript from '@/hooks/useCreateScript';
const SCRIPT_URL =
'http://api.map.baidu.com/getscript?v=3.0&ak=WxbQmaOc3bvSGSaKWcyeFSf8fnYCWpKd&services=&t=' +
new Date().getTime();
const container = ref<HTMLDivElement | null>(null);
const { createScriptPromise } = useCreateScript(SCRIPT_URL);
const initMap = () => {
createScriptPromise.then(() => {
const bMap = (window as any).BMap;
const map: any = new bMap.Map(container.value);
const point = new bMap.Point(116.404, 39.915);
map.centerAndZoom(point, 7);
map.enableScrollWheelZoom();
map.setMapStyleV2({ styleId: 'ea4652613f3629247d47666706ce7e89' });
});
};
onMounted(initMap);
</script>
<style lang="less">
.container {
width: 100%;
height: 720px;
margin-top: -32px;
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<div class="container" ref="container"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import useCreateScript from '@/hooks/useCreateScript';
const SCRIPT_URL = 'https://webapi.amap.com/maps?v=1.4.15&key=9f2d3fcc4b12a7c915fded4b55902e21';
const container = ref<HTMLDivElement | null>(null);
const height = ref(0);
const { createScriptPromise } = useCreateScript(SCRIPT_URL);
const initMap = () => {
height.value = container.value?.parentElement?.getBoundingClientRect().height || 0;
createScriptPromise.then(() => {
const aMap: any = (window as any).AMap;
new aMap.Map(container.value, {
zoom: 22,
center: [116.397428, 39.90923],
viewMode: '3D',
pitch: 75,
});
});
};
onMounted(initMap);
</script>
<style lang="less">
.container {
width: 100%;
height: 720px;
margin-top: -32px;
}
</style>

View File

@@ -0,0 +1,307 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="模态框">
模态框用于向用户收集或展示信息Modal 采用 Dialog 预设扩展拖拽效果
<br />
以下是 useModal
方式ref方式也支持使用方式和其他组件一致modalRef.value.closeModal()
</n-card>
</div>
<n-card :bordered="false" class="mt-4 proCard">
<n-alert title="Modal嵌套Form" type="info">
使用 useModal 进行弹窗展示和操作并演示了在Modal内和Form组件组合使用方法
</n-alert>
<n-divider />
<n-space>
<n-button type="primary" @click="showModal">打开Modal嵌套Form例子</n-button>
</n-space>
<n-divider />
<n-alert title="个性化轻量级" type="info">
使用 useModal 进行弹窗展示和操作自定义配置实现轻量级效果更多配置请参考文档
</n-alert>
<n-divider />
<n-space>
<n-button type="primary" @click="showLightModal">轻量级确认</n-button>
</n-space>
<n-divider />
<n-alert title="提示" type="info">
组件暴露了setProps 方法用于修改组件内部
Props比如标题具体参考UI框架文档DialogReactive Properties
</n-alert>
</n-card>
<basicModal @register="modalRegister" ref="modalRef" class="basicModal" @on-ok="okModal">
<template #default>
<BasicForm @register="register" @reset="handleReset" class="basicForm">
<template #statusSlot="{ model, field }">
<n-input v-model:value="model[field]" />
</template>
</BasicForm>
</template>
</basicModal>
<basicModal
@register="lightModalRegister"
class="basicModalLight"
ref="modalRef"
@on-ok="lightOkModal"
>
<template #default>
<p class="text-gray-500" style="padding-left: 35px">一些对话框内容</p>
</template>
</basicModal>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from 'vue';
import { useMessage } from 'naive-ui';
import { basicModal, useModal } from '@/components/Modal';
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
const schemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '姓名',
labelMessage: '这是一个提示',
giProps: {
span: 1,
},
componentProps: {
placeholder: '请输入姓名',
onInput: (e: any) => {
console.log(e);
},
},
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
},
{
field: 'mobile',
component: 'NInputNumber',
label: '手机',
componentProps: {
placeholder: '请输入手机号码',
showButton: false,
onInput: (e: any) => {
console.log(e);
},
},
},
{
field: 'type',
component: 'NSelect',
label: '类型',
giProps: {
//span: 24,
},
componentProps: {
placeholder: '请选择类型',
options: [
{
label: '舒适性',
value: 1,
},
{
label: '经济性',
value: 2,
},
],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeDate',
component: 'NDatePicker',
label: '预约时间',
giProps: {
//span: 24,
},
defaultValue: 1183135260000,
componentProps: {
type: 'date',
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeTime',
component: 'NTimePicker',
label: '停留时间',
giProps: {
//span: 24,
},
componentProps: {
clearable: true,
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeProject',
component: 'NCheckbox',
label: '预约项目',
giProps: {
//span: 24,
},
componentProps: {
placeholder: '请选择预约项目',
options: [
{
label: '种牙',
value: 1,
},
{
label: '补牙',
value: 2,
},
{
label: '根管',
value: 3,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'makeSource',
component: 'NRadioGroup',
label: '来源',
giProps: {
//span: 24,
},
componentProps: {
options: [
{
label: '网上',
value: 1,
},
{
label: '门店',
value: 2,
},
],
onUpdateChecked: (e: any) => {
console.log(e);
},
},
},
{
field: 'status',
label: '状态',
giProps: {
//span: 24,
},
//插槽
slot: 'statusSlot',
},
];
export default defineComponent({
components: { basicModal, BasicForm },
setup() {
const modalRef: any = ref(null);
const message = useMessage();
const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
title: '新增预约',
});
const [
lightModalRegister,
{
openModal: lightOpenModal,
closeModal: lightCloseModal,
setSubLoading: lightSetSubLoading,
},
] = useModal({
title: '确认对话框',
showIcon: true,
type: 'warning',
closable: false,
maskClosable: true,
});
const [register, { submit }] = useForm({
gridProps: { cols: 1 },
collapsedRows: 3,
labelWidth: 120,
layout: 'horizontal',
submitButtonText: '提交预约',
showActionButtonGroup: false,
schemas,
});
const state = reactive({
formValue: {
name: '小马哥',
},
});
async function okModal() {
const formRes = await submit();
if (formRes) {
closeModal();
console.log('formRes', formRes);
message.success('提交成功');
} else {
message.error('验证失败,请填写完整信息');
setSubLoading(false);
}
}
function lightOkModal() {
lightCloseModal();
lightSetSubLoading(false);
}
function showLightModal() {
lightOpenModal();
}
function showModal() {
openModal();
}
function handleReset(values: Recordable) {
console.log(values);
}
return {
...toRefs(state),
modalRef,
register,
modalRegister,
lightModalRegister,
handleReset,
showModal,
okModal,
lightOkModal,
showLightModal,
};
},
});
</script>
<style lang="less">
.basicForm {
padding-top: 20px;
}
.n-dialog.basicModal {
width: 640px;
}
.n-dialog.basicModalLight {
width: 416px;
padding-top: 26px;
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<Iframe ref="iframeRef" style="zoom: 0.8" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Iframe from '../../../../iframe/index.vue';
const iframeRef = ref();
onMounted(() => {
iframeRef.value?.open('https://www.naiveui.com/zh-CN/os-theme/components/button');
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,322 @@
<template>
<div>
<n-grid :cols="2" :x-gap="10" :y-gap="10">
<n-gi>
<n-card
title="警告信息alert"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space vertical :size="12">
<n-alert title="Info 类型" type="info"> 万事开头难只能一直努力 </n-alert>
<n-alert title="Success 类型" type="success">
成功就是眼前过了黑夜就是白天加油~~
</n-alert>
<n-alert title="Warning 类型" type="warning">
想想昨天看看今天望望明天一切都会好起来
</n-alert>
<n-alert title="Error 类型" type="error"> 有人欺负人请不犹豫请还回去 </n-alert>
<n-alert title="可以关掉" type="info" closable> Gee it's good to be back home </n-alert>
<n-alert title="没有边框" type="info" :bordered="false">
Gee it's good to be back home
</n-alert>
<n-alert title="自定义图标">
<template #icon>
<n-icon>
<BalloonOutline />
</n-icon>
</template>
Well the Ukraine girls really knock me out<br />
They leave the West behind<br />
And Moscow girls make me sing and shout<br />
That Georgia's always on my mind<br />
Aw come on!
</n-alert>
<n-alert :show-icon="false" title="没有图标">
Yeah I'm back in the U.S.S.R.<br />
You don't know how lucky you are boys
</n-alert>
</n-space>
</n-card>
</n-gi>
<n-gi>
<n-card
title="对话框Dialog"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<div class="padding">
<n-space>
<n-button size="small" type="primary" @click="openConfirm('warning')">警告</n-button>
<n-button size="small" type="warning" @click="openConfirm('success')">成功</n-button>
<n-button size="small" type="info" @click="openConfirm('error')">错误</n-button>
</n-space>
</div>
</n-card>
<n-card
class="mt-2"
title="信息Message"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space>
<n-button type="info" @click="openMessage('info')"> 信息 </n-button>
<n-button type="error" @click="openMessage('error')"> 错误 </n-button>
<n-button type="warning" @click="openMessage('warning')"> 警告 </n-button>
<n-button type="success" @click="openMessage('success')"> 成功 </n-button>
<n-button type="primary" @click="openMessage('loading')"> 加载中 </n-button>
</n-space>
</n-card>
<n-card
class="mt-2"
title="通知Notification"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space>
<n-button type="info" @click="openNotification('info')"> 信息 </n-button>
<n-button type="error" @click="openNotification('error')"> 错误 </n-button>
<n-button type="warning" @click="openNotification('warning')"> 警告 </n-button>
<n-button type="success" @click="openNotification('success')"> 成功 </n-button>
</n-space>
</n-card>
<n-card
class="mt-2"
title="弹出确认Popconfirm"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space>
<n-popconfirm
@positive-click="handlePositiveClick"
@negative-click="handleNegativeClick"
>
<template #trigger>
<n-button>引用</n-button>
</template>
一切都将一去杳然,任何人都无法将其捕获。
</n-popconfirm>
<n-popconfirm :negative-text="null" @positive-click="handlePositiveClick">
<template #trigger>
<n-button>只有确定</n-button>
</template>
一切都将一去杳然,任何人都无法将其捕获。
</n-popconfirm>
<n-popconfirm :positive-text="null" @negative-click="handleNegativeClick">
<template #trigger>
<n-button>只有取消</n-button>
</template>
一切都将一去杳然,任何人都无法将其捕获。
</n-popconfirm>
<n-popconfirm :positive-text="null" :negative-text="null">
<template #trigger>
<n-button>什么也没有</n-button>
</template>
一切都将一去杳然,任何人都无法将其捕获。
</n-popconfirm>
<n-popconfirm>
<template #trigger>
<n-button>自定义 action</n-button>
</template>
<template #action> 自定义 action </template>
一切都将一去杳然,任何人都无法将其捕获。
</n-popconfirm>
</n-space>
</n-card>
<n-card
class="mt-2"
title="弹出信息Popover"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space>
<n-popover trigger="hover">
<template #trigger>
<n-button>悬浮</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="hover" :keep-alive-on-hover="false">
<template #trigger>
<n-button>悬浮(忽略主体)</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="click">
<template #trigger>
<n-button>点击</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="focus">
<template #trigger>
<n-button>聚焦</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="manual" :show="showPopover">
<template #trigger>
<n-button @click="showPopover = !showPopover"> 手动 </n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="hover" :delay="500" :duration="500">
<template #trigger>
<n-button>延迟 500ms, 持续 500ms</n-button>
</template>
<span>
Lately did you ever feel the pain In the morning rain as it soaks it to the bone
</span>
</n-popover>
</n-space>
</n-card>
<n-card
class="mt-2"
title="弹出提示Tooltip"
:content-style="{ padding: '10px' }"
:header-style="{ padding: '10px' }"
>
<n-space>
<n-tooltip trigger="hover">
<template #trigger>
<n-button> 鸭子 </n-button>
</template>
如果它长得像鸭子,走起来像鸭子,叫起来也像鸭子,那它一定是个鸭子。
</n-tooltip>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<n-button> 悬浮 </n-button>
</template>
<span> I wish they all could be California girls </span>
</n-tooltip>
<n-tooltip placement="bottom" trigger="click">
<template #trigger>
<n-button> 点击 </n-button>
</template>
<span> I wish they all could be California girls </span>
</n-tooltip>
<n-tooltip :show="showPopover" placement="bottom">
<template #trigger>
<n-button @click="showPopover = !showPopover"> 手动 </n-button>
</template>
<span> I wish they all could be California girls </span>
</n-tooltip>
<n-tooltip :show-arrow="false" trigger="hover">
<template #trigger>
<n-button>默认有箭头</n-button>
</template>
和 Popover 一样
</n-tooltip>
</n-space>
</n-card>
</n-gi>
</n-grid>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useDialog, useMessage, useNotification } from 'naive-ui';
import { BalloonOutline } from '@vicons/ionicons5';
const dialog = useDialog();
const message = useMessage();
const notification = useNotification();
const showPopover = ref(false);
function openConfirm(type: string) {
switch (type) {
case 'warning':
dialog.warning({
title: '警告',
content: '你确定要离开我',
positiveText: '确定',
negativeText: '不确定',
onPositiveClick: () => {
message.success('确定');
},
onNegativeClick: () => {
message.error('不确定');
},
});
break;
case 'success':
dialog.success({
title: '成功',
content: '小哥哥你太棒了~~',
positiveText: '哇哦~',
});
break;
case 'error':
dialog.error({
title: '错误',
content: '知道错了吗下次改了',
positiveText: '快哭了',
onPositiveClick: () => {
message.success('我知道了');
},
});
break;
}
}
function openMessage(type: string) {
switch (type) {
case 'info':
message.info('在心碎中认清遗憾 生命漫长也短暂跳动心脏长出藤蔓');
break;
case 'error':
message.error('愿为险而战 跌入灰暗坠入深渊');
break;
case 'warning':
message.warning('沾满泥土的脸 没有神的光环');
break;
case 'success':
message.success('握紧手中的平凡 此心自称无憾生命的火已点燃');
break;
case 'loading':
message.loading('有一天也许会走远 也许还能再相见');
break;
}
}
function openNotification(type: string) {
switch (type) {
case 'info':
notification.info({
content: '在心碎中认清遗憾 生命漫长也短暂跳动心脏长出藤蔓',
meta: '只要平凡',
});
break;
case 'error':
notification.error({
content: '愿为险而战 跌入灰暗坠入深渊',
meta: '只要平凡',
});
break;
case 'warning':
notification.warning({
content: '沾满泥土的脸 没有神的光环',
meta: '只要平凡',
});
break;
case 'success':
notification.success({
content: '握紧手中的平凡 此心自称无憾生命的火已点燃',
meta: '只要平凡',
});
break;
}
}
function handlePositiveClick() {
message.info('是的');
}
function handleNegativeClick() {
message.info('并不');
}
</script>

View File

@@ -0,0 +1,109 @@
<template>
<div class="main-container">
<n-card title="打印图片" :header-style="{ padding: '5px' }" :content-style="{ padding: '0px' }">
<template #header-extra>
<n-button type="primary" size="small" @click="printImage">打印</n-button>
</template>
<div class="image-wrapper">
<img :src="imagePath" />
</div>
</n-card>
<n-card
title="打印HTML"
:header-style="{ padding: '5px' }"
:content-style="{ padding: '0px' }"
class="mt-4"
>
<template #header-extra>
<n-button type="primary" size="small" @click="printHtml">打印</n-button>
</template>
<div id="htmlWrapper" class="flex justify-center html-wrapper align-center flex-direction">
<n-table :data="dataList">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>职业</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) of dataList" :key="index">
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>{{ item.gender }}</td>
<td>{{ item.career }}</td>
</tr>
</tbody>
</n-table>
</div>
</n-card>
</div>
</template>
<script lang="ts">
import printJS from 'print-js';
import imagePath from '@/assets/images/logo.png';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Print',
setup() {
function printImage() {
printJS({
printable: imagePath,
type: 'image',
showModal: false,
});
}
function printHtml() {
printJS({
printable: 'htmlWrapper',
type: 'html',
targetStyles: ['*'],
});
}
return {
printImage,
printHtml,
imagePath,
dataList: [
{
name: '张三',
age: 30,
gender: '男',
career: '工程师',
},
{
name: '李四',
age: 20,
gender: '男',
career: '服务员',
},
{
name: '王五',
age: 40,
gender: '女',
career: '售货员',
},
],
};
},
});
</script>
<style lang="less" scoped>
.image-wrapper {
width: 30%;
margin: 0 auto;
& > img {
width: 100%;
}
}
.html-wrapper {
width: 80%;
margin: 0 auto;
& > h1 {
color: red;
}
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<n-card :bordered="false" class="proCard">
<div class="result-box">
<n-result status="error" title="操作失败" description="请核对并修改以下信息后,再重新提交。">
<div class="result-box-extra">
<p>您提交的内容有如下错误</p>
<p class="mt-3">
<n-space align="center">
<n-icon size="20" color="#f0a020">
<InfoCircleOutlined />
</n-icon>
<span>认证照片不够清晰</span>
<n-button type="info" text>立即修改</n-button>
</n-space>
</p>
<p class="mt-3">
<n-space>
<n-icon size="20" color="#f0a020">
<InfoCircleOutlined />
</n-icon>
<span>备注包含敏感字符并且不能包含政治相关</span>
<n-button type="info" text>立即修改</n-button>
</n-space>
</p>
</div>
<template #footer>
<div class="flex justify-center mb-4">
<n-space align="center">
<n-button type="info" @click="goHome">回到首页</n-button>
<n-button>查看详情</n-button>
<n-button>打印</n-button>
</n-space>
</div>
</template>
</n-result>
</div>
</n-card>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useThemeVars } from 'naive-ui';
import { useRouter } from 'vue-router';
import { InfoCircleOutlined } from '@vicons/antd';
const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() {
router.push('/');
}
</script>
<style lang="less" scoped>
.result-box {
width: 72%;
margin: 0 auto;
text-align: center;
padding-top: 5px;
&-extra {
padding: 24px 40px;
text-align: left;
background: v-bind(getTableHeaderColor);
border-radius: 4px;
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<n-card :bordered="false" class="proCard">
<div class="result-box">
<n-result
status="info"
title="提示"
description="本次提交将在24小时候内自动转入对方账户如操作失误请及时撤回"
>
<div class="result-box-extra">
<p>您提交的内容如下</p>
<p class="mt-3">
<n-space align="center">
<n-icon size="20" color="#18a058">
<CheckCircleOutlined />
</n-icon>
<span>转入支付宝账户189****54261980</span>
<n-button type="info" text>立即撤回</n-button>
</n-space>
</p>
<p class="mt-3">
<n-space>
<n-icon size="20" color="#18a058">
<CheckCircleOutlined />
</n-icon>
<span>转入支付宝账户187****54262980</span>
<n-button type="info" text>立即撤回</n-button>
</n-space>
</p>
</div>
<template #footer>
<div class="flex justify-center mb-4">
<n-space align="center">
<n-button type="info" @click="goHome">回到首页</n-button>
<n-button>查看详情</n-button>
<n-button>全部撤回</n-button>
</n-space>
</div>
</template>
</n-result>
</div>
</n-card>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useThemeVars } from 'naive-ui';
import { useRouter } from 'vue-router';
import { CheckCircleOutlined } from '@vicons/antd';
const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() {
router.push('/');
}
</script>
<style lang="less" scoped>
.result-box {
width: 72%;
margin: 0 auto;
text-align: center;
padding-top: 5px;
&-extra {
padding: 24px 40px;
text-align: left;
background: v-bind(getTableHeaderColor);
border-radius: 4px;
}
}
</style>

View File

@@ -0,0 +1,55 @@
<template>
<n-card :bordered="false" class="proCard">
<div class="result-box">
<n-result
status="success"
title="操作成功"
description="提交结果页用于反馈一系列操作任务的处理结果,如果仅是简单操作,灰色区域可以显示一些补充的信息。"
>
<div class="result-box-extra">
<p>已提交申请等待财务部门审核</p>
</div>
<template #footer>
<div class="flex justify-center mb-4">
<n-space align="center">
<n-button type="info" @click="goHome">回到首页</n-button>
<n-button>查看详情</n-button>
<n-button>打印</n-button>
</n-space>
</div>
</template>
</n-result>
</div>
</n-card>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { useThemeVars } from 'naive-ui';
const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() {
router.push('/');
}
</script>
<style lang="less" scoped>
.result-box {
width: 72%;
margin: 0 auto;
text-align: center;
padding-top: 5px;
&-extra {
padding: 24px 40px;
text-align: left;
background: v-bind(getTableHeaderColor);
border-radius: 4px;
}
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="tagsView"> 标签页操作项 </n-card>
</div>
<n-card>
<n-space>
<n-button type="info" @click="handleSignal('1')"> 刷新页面路由 </n-button>
<n-button type="error" @click="handleSignal('2')"> 关闭当前页面 </n-button>
<n-button type="warning" @click="handleSignal('3')"> 关闭其他页面 </n-button>
<n-button type="success" @click="handleSignal('4')"> 关闭全部页面 </n-button>
</n-space>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { useTabsViewStore } from '@/store/modules/tabsView';
const tabsViewStore = useTabsViewStore();
function handleSignal(signal: string) {
tabsViewStore.closeSignal(signal);
}
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,70 @@
<template>
<div>
<n-space vertical>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 渐变文字 </template>
<template #header-extra>
<n-gradient-text type="success"> 渐变文字成了 </n-gradient-text>
</template>
<n-space vertical>
<n-gradient-text style="white-space: normal" type="success">
<p
>苏轼1037年1月8日-1101年8月24日字子瞻和仲号铁冠道人东坡居士世称苏东坡苏仙汉族眉州眉山四川省眉山市祖籍河北栾城北宋著名文学家书法家画家历史治水名人苏轼是北宋中期文坛领袖在诗散文画等方面取得很高成就文纵横恣肆诗题材广阔清新豪健善用夸张比喻独具风格与黄庭坚并称苏黄词开豪放一派与辛弃疾同是豪放派代表并称苏辛散文著述宏富豪放自如与欧阳修并称欧苏唐宋八大家之一苏轼善书宋四家之一擅长文人画尤擅墨竹怪石枯木等与韩愈柳宗元和欧阳修合称千古文章四大家作品有东坡七集东坡易传东坡乐府潇湘竹石图卷古木怪石图卷</p
>
</n-gradient-text>
</n-space>
</n-card>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 渐变文字 </template>
<template #header-extra>
<n-gradient-text type="warning"> 渐变文字成了 </n-gradient-text>
</template>
<n-space vertical>
<n-gradient-text style="white-space: normal" type="warning">
<p
>白居易772846字乐天号香山居士又号醉吟先生祖籍太原到其曾祖父时迁居下邽生于河南新郑是唐代伟大的现实主义诗人唐代三大诗人之一白居易与元稹共同倡导新乐府运动世称元白与刘禹锡并称刘白白居易的诗歌题材广泛形式多样语言平易通俗诗魔诗王之称官至翰林学士左赞善大夫公元846年白居易在洛阳逝世葬于香山白氏长庆集传世代表诗作有长恨歌卖炭翁琵琶行</p
>
</n-gradient-text>
</n-space>
</n-card>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 自定义颜色 </template>
<template #header-extra>
<n-gradient-text gradient="linear-gradient(90deg, red 0%, green 50%, blue 100%)">
渐变文字成了
</n-gradient-text>
</template>
<n-space vertical>
<n-gradient-text
gradient="linear-gradient(90deg, red 0%, green 50%, blue 100%)"
style="white-space: normal"
>
<p
>杜甫712770字子美自号少陵野老世称杜工部杜少陵汉族河南府巩县今河南省巩义市唐代伟大的现实主义诗人杜甫被世人尊为诗圣其诗被称为诗史杜甫与李白合称李杜为了跟另外两位诗人李商隐与杜牧即小李杜区别开来杜甫与李白又合称大李杜他忧国忧民人格高尚他的约1400余首诗被保留了下来诗艺精湛在中国古典诗歌中备受推崇影响深远759-766年间曾居成都后世有杜甫草堂纪念</p
>
</n-gradient-text>
</n-space>
</n-card>
</n-space>
</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped>
p {
font-size: 14px;
line-height: 2;
text-indent: 28px;
}
</style>

View File

@@ -0,0 +1,94 @@
<template>
<div>
<n-card title="高亮" style="margin-bottom: 10px">
<n-text>
<p style="line-height: 2" v-html="compData1.text"></p>
</n-text>
</n-card>
<n-card title="自定义颜色" style="margin-bottom: 10px">
<n-text>
<p style="line-height: 2" v-html="compData2.text"></p>
</n-text>
</n-card>
<n-card title="自定义样式">
<n-text>
<p style="line-height: 2" v-html="compData3.text"></p>
</n-text>
</n-card>
</div>
</template>
<script lang="ts" setup>
import highHtml from '@/utils/highHtml';
import { reactive, onMounted } from 'vue';
const text =
'先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。\n' +
'\n' +
'  宫中府中,俱为一体;陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。\n' +
'\n' +
'  侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。\n' +
'\n' +
'  将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。\n' +
'\n' +
'  亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。\n' +
'\n' +
'  臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。\n' +
'\n' +
'  先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。\n' +
'\n' +
'  愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。今当远离,临表涕零,不知所言';
const compData1 = reactive({
text: '',
keys: ['先帝', '先汉', '苟'],
});
const compData2 = reactive({
text: '',
keys: [
{
keyword: '先帝',
bgColor: 'red',
color: '#fff',
renderHighlightKeyword: (str) => {
return `<span class="high" style="padding:2px 5px;margin:0 5px;border-radius:3px;background-color: #3a5ccc;color: #ffffff">${str}</span>`;
},
},
{
keyword: '中',
bgColor: '#b4d5ff',
color: '#fff',
renderHighlightKeyword: (str) => {
return `<span class="high" style="padding:2px 5px;margin:0 5px;border-radius:3px;background-color: #e96656;color: #ffffff">${str}</span>`;
},
},
{ keyword: '苟', bgColor: '#b4d5ff', color: '#fff' },
],
});
const compData3 = reactive({
text: '',
keys: [
{
keyword: '之',
bgColor: 'red',
color: '#fff',
style: { padding: '2px 5px', margin: '0 5px', 'border-radius': '3px' },
},
{ keyword: '先帝', bgColor: '#b4d5ff', color: '#fff' },
{ keyword: '苟', bgColor: '#b4d5ff', color: '#fff' },
],
});
onMounted(() => {
compData1.text = highHtml(text, compData1.keys).highText;
compData2.text = highHtml(text, compData2.keys).highText;
compData3.text = highHtml(text, compData3.keys).highText;
});
</script>
<style lang="less">
.high {
&:hover {
background-color: seagreen !important;
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 敏感词汇验证 </template>
<n-space vertical>
<n-space>
<n-tag
:type="idx % 2 ? 'success' : 'warning'"
:key="idx"
v-for="(item, idx) in compData.keys"
>{{ item }}</n-tag
>
</n-space>
<n-input
placeholder="输入需要验证的词汇文本"
type="textarea"
size="small"
v-model:value="compData.text"
:autosize="{ minRows: 3, maxRows: 5 }"
@update:value="compData.handleUpdateText"
/>
<n-space>
<n-tag
:type="idx % 2 ? 'error' : 'info'"
:key="idx"
v-for="(item, idx) in compData.words"
>{{ item }}</n-tag
>
</n-space>
</n-space>
</n-card>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import Mint from 'mint-filter';
const compData = reactive({
keys: ['敏感词', '胡萝卜', '香蕉', '苹果'],
text: '',
words: [],
});
const mint = new Mint(compData.keys);
compData.handleUpdateText = (value: string | [string, string]) => {
const test = mint.filter(value);
compData.words = test.words;
};
</script>

View File

@@ -0,0 +1,102 @@
<template>
<div>
<n-space vertical>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 字符拼音 </template>
<template #header-extra> 陋室铭唐代刘禹锡 </template>
<div class="pinyin">
<template v-for="item in compData.items1">
<div class="pinyin-item" :key="idx" v-for="(todo, idx) in item">
<span class="py">{{ todo.py }}</span>
<span class="hz">{{ todo.hz }}</span> </div
><br />
</template>
</div>
</n-card>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 字符拼音 </template>
<template #header-extra> 李贺小传李商隐唐代 </template>
<div class="pinyin">
<template v-for="item in compData.items2">
<div class="pinyin-item" :key="idx" v-for="(todo, idx) in item">
<span class="py">{{ todo.py }}</span>
<span class="hz">{{ todo.hz }}</span> </div
><br />
</template>
</div>
</n-card>
</n-space>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { pinyin } from 'pinyin-pro';
interface WordInfo {
hz: string;
py: string;
}
const text1 = [
' 山不在高,有仙则名。水不在深,有龙则灵。斯是陋室,惟吾德馨。苔痕上阶绿,草色入帘青。谈笑有鸿儒,往来无白丁。可以调素琴,阅金经。无丝竹之乱耳,无案牍之劳形。南阳诸葛庐,西蜀子云亭。孔子云:何陋之有?',
];
const text2 = [
' 京兆杜牧为李长吉集序,状长吉之奇甚尽,世传之。长吉姊嫁王氏者,语长吉之事尤备。',
' 长吉细瘦,通眉,长指爪,能苦吟疾书。最先为昌黎韩愈所知。所与游者,王参元、杨敬之、权璩、崔植辈为密,每旦日出与诸公游,未尝得题然后为诗,如他人思量牵合,以及程限为意。恒从小奚奴,骑距驉,背一古破锦囊,遇有所得,即书投囊中。及暮归.太夫人使婢受囊出之,见所书多.辄曰:“是儿要当呕出心乃已尔。”上灯,与食。长吉从婢取书,研墨叠纸足成之,投他囊中。非大醉及吊丧日率如此,过亦不复省。王、杨辈时复来探取写去。长吉往往独骑往还京、洛,所至或时有著,随弃之,故沈子明家所余四卷而已。',
' 长吉将死时,忽昼见一绯衣人,驾赤虬,持一板,书若太古篆或霹雳石文者,云当召长吉。长吉了不能读,欻下榻叩头,言:“阿㜷老且病,贺不愿去。”绯衣人笑曰:“帝成白玉楼,立召君为记。天上差乐,不苦也。”长吉独泣,边人尽见之。少之,长吉气绝。常所居窗中,勃勃有烟气,闻行车嘒管之声。太夫人急止人哭,待之如炊五斗黍许时,长吉竟死。王氏姊非能造作谓长吉者,实所见如此。',
' 呜呼,天苍苍而高也,上果有帝耶?帝果有苑囿、宫室、观阁之玩耶?苟信然,则天之高邈,帝之尊严,亦宜有人物文采愈此世者,何独眷眷于长吉而使其不寿耶?噫,又岂世所谓才而奇者,不独地上少,即天上亦不多耶?长吉生二十七年,位不过奉礼太常,时人亦多排摈毁斥之,又岂才而奇者,帝独重之,而人反不重耶?又岂人见会胜帝耶?',
];
const createHzPy = (text: string[]): WordInfo[][] => {
const items: WordInfo[][] = [];
text.forEach((item) => {
const todo: WordInfo[] = [];
for (let i = 0; i < item.length; i++) {
const tg = item.charAt(i);
todo.push({ hz: tg, py: pinyin(tg)[0] });
}
items.push(todo);
});
return items;
};
const compData = reactive({
items1: createHzPy(text1),
items2: createHzPy(text2),
});
</script>
<style lang="less" scoped>
.pinyin {
&-item {
line-height: 100%;
width: 42px;
text-align: center;
display: inline-block;
.py {
clear: both;
font-size: 12px;
font-weight: normal;
float: left;
width: 42px;
}
.hz {
clear: both;
margin-bottom: 10px;
text-align: center;
float: left;
font-size: 16px;
height: 36px;
width: 42px;
line-height: 36px;
}
}
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 时间线 </template>
<n-space>
<n-card title="竖向">
<n-timeline>
<n-timeline-item content="啊" />
<n-timeline-item type="success" title="成功" content="哪里成功" time="2018-04-03 20:46" />
<n-timeline-item type="error" content="哪里错误" time="2018-04-03 20:46" />
<n-timeline-item type="warning" title="警告" content="哪里警告" time="2018-04-03 20:46" />
<n-timeline-item
type="info"
title="信息"
content="是的"
time="2018-04-03 20:46"
line-type="dashed"
/>
<n-timeline-item content="啊" />
</n-timeline>
</n-card>
<n-card title="横向">
<div style="overflow: auto">
<n-timeline horizontal>
<n-timeline-item content="啊" />
<n-timeline-item
type="success"
title="成功"
content="哪里成功"
time="2018-04-03 20:46"
/>
<n-timeline-item type="error" content="哪里失败" time="2018-04-03 20:46" />
<n-timeline-item
type="warning"
title="警告"
content="哪里警告"
time="2018-04-03 20:46"
/>
<n-timeline-item type="info" title="信息" content="是的" time="2018-04-03 20:46" />
</n-timeline>
</div>
</n-card>
<n-card title="自定义">
<n-timeline :icon-size="20">
<n-timeline-item color="grey" content="啊">
<template #icon>
<n-icon>
<cash-icon />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item type="success" title="成功" content="哪里成功" time="2018-04-03 20:46">
<template #icon>
<n-icon>
<cash-icon />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item type="error" content="哪里错误" time="2018-04-03 20:46">
<template #icon>
<n-icon>
<cash-icon />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item type="warning" title="警告" content="哪里警告" time="2018-04-03 20:46">
<template #icon>
<n-icon>
<cash-icon />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item type="info" title="信息" content="是的" time="2018-04-03 20:46">
<template #icon>
<n-icon>
<cash-icon />
</n-icon>
</template>
</n-timeline-item>
</n-timeline>
</n-card>
</n-space>
</n-card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { CashOutline as CashIcon } from '@vicons/ionicons5';
export default defineComponent({
components: {
CashIcon,
},
});
</script>

View File

@@ -0,0 +1,15 @@
<template>
<Iframe ref="iframeRef" style="zoom: 0.8" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Iframe from '../../../../iframe/index.vue';
const iframeRef = ref();
onMounted(() => {
iframeRef.value?.open('https://www.makeapie.cn/echarts');
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,15 @@
<template>
<Iframe ref="iframeRef" style="zoom: 0.9" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Iframe from '../../../../iframe/index.vue';
const iframeRef = ref();
onMounted(() => {
iframeRef.value?.open('https://madeapie.com/');
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,15 @@
<template>
<Iframe ref="iframeRef" style="zoom: 0.9" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import Iframe from '../../../../iframe/index.vue';
const iframeRef = ref();
onMounted(() => {
iframeRef.value?.open('https://ppchart.com/#/');
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,106 @@
<template>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 瀑布流组件 </template>
<template #header-extra>
<n-tag type="success" @click="handleClick">vue-waterfall-plugin-next</n-tag>
</template>
<Waterfall
:row-key="options.rowKey"
:gutter="options.gutter"
:has-around-gutter="options.hasAroundGutter"
:width="options.width"
:breakpoints="options.breakpoints"
:img-selector="options.imgSelector"
:background-color="options.backgroundColor"
:animation-effect="options.animationEffect"
:animation-duration="options.animationDuration"
:animation-delay="options.animationDelay"
:lazyload="options.lazyload"
:list="compData.items"
>
<template #item="{ item }">
<div class="waterfall-item" :style="item.style">
<span>君问归期未有期巴山夜雨涨秋池何当共剪西窗烛却话巴山夜雨时</span>
</div>
</template>
</Waterfall>
</n-card>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { Waterfall } from 'vue-waterfall-plugin-next';
import 'vue-waterfall-plugin-next/dist/style.css';
import { rdmLightRgbColor } from '@/utils/hotgo';
const options = reactive({
// 唯一key值
rowKey: 'id',
// 卡片之间的间隙
gutter: 20,
// 是否有周围的gutter
hasAroundGutter: true,
// 卡片在PC上的宽度
width: 250,
// 自定义行显示个数,主要用于对移动端的适配
breakpoints: {
1200: {
// 当屏幕宽度小于等于1200
rowPerView: 5,
},
800: {
// 当屏幕宽度小于等于800
rowPerView: 3,
},
500: {
// 当屏幕宽度小于等于500
rowPerView: 1,
},
},
// 动画效果
animationEffect: 'animate__fadeInUp',
// 动画时间
animationDuration: 1000,
// 动画延迟
animationDelay: 300,
// 背景色
backgroundColor: '',
// imgSelector
imgSelector: 'src.original',
// 是否懒加载
lazyload: true,
});
const items: any = [];
const genBetweenRight = (m, n) => Math.floor(Math.random() * (n - m) + 1) + m;
for (let i = 0; i < 90; i++) {
items.push({
style: {
height: genBetweenRight(100, 300) + 'px',
'background-color': rdmLightRgbColor(),
},
});
}
const compData = reactive({
items,
});
const handleClick = () => {
window.open('https://vue-waterfall.netlify.app/');
};
</script>
<style lang="less" scoped>
.waterfall-item {
border: 2px solid rgb(244, 244, 248);
height: 100px;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border-radius: 4px;
}
</style>

View File

@@ -0,0 +1,171 @@
<template>
<n-space vertical>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 文字水印 </template>
<template #header-extra> 核心机密 </template>
<n-watermark
content="核心机密"
cross
selectable
:font-size="16"
:line-height="16"
:width="192"
:height="128"
:x-offset="12"
:y-offset="28"
:rotate="-15"
>
<n-table :single-line="false">
<thead>
<tr>
<th>复盘</th>
<th>赋能</th>
<th>协同</th>
<th>...</th>
<th>串联</th>
</tr>
</thead>
<tbody>
<tr>
<td>拉通</td>
<td>打通</td>
<td>树立</td>
<td>...</td>
<td>履约</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</n-table>
</n-watermark>
</n-card>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 图片水印 </template>
<template #header-extra>
<n-image height="30" width="30" :src="compData.img" />
</template>
<n-watermark
:image="compData.img"
cross
:rotate="-15"
:font-size="16"
:line-height="16"
:width="192"
:height="128"
:x-offset="12"
:y-offset="30"
:image-width="34"
:image-opacity="0.24"
>
<n-table :single-line="false">
<thead>
<tr>
<th>复盘</th>
<th>赋能</th>
<th>协同</th>
<th>...</th>
<th>串联</th>
</tr>
</thead>
<tbody>
<tr>
<td>拉通</td>
<td>打通</td>
<td>树立</td>
<td>...</td>
<td>履约</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</n-table>
</n-watermark>
</n-card>
<n-card
:segmented="{ content: true, footer: true }"
header-style="padding:10px"
footer-style="padding:10px"
>
<template #header> 自定义水印 </template>
<template #header-extra>
<n-input-group>
<n-input v-model:value="compData.text" placeholder="输入自定义水印" />
<n-button type="primary" ghost>添加</n-button>
</n-input-group>
</template>
<n-watermark
v-model:content="compData.text"
cross
selectable
:font-size="16"
:line-height="16"
:width="192"
:height="128"
:x-offset="12"
:y-offset="28"
:rotate="-15"
>
<n-table :single-line="false">
<thead>
<tr>
<th>复盘</th>
<th>赋能</th>
<th>协同</th>
<th>...</th>
<th>串联</th>
</tr>
</thead>
<tbody>
<tr>
<td>拉通</td>
<td>打通</td>
<td>树立</td>
<td>...</td>
<td>履约</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</n-table>
</n-watermark>
</n-card>
</n-space>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import logo from '@/assets/images/logo.png';
export default defineComponent({
setup() {
const compData = reactive({
img: logo,
text: '自定义水印',
});
return {
compData,
};
},
});
</script>

View File

@@ -7,7 +7,7 @@
</n-input-group>
</n-form-item>
<n-form-item label="二维码" path="index">
<n-form-item label="二维码" path="index" v-if="showQrcode">
<div class="text-center">
<qrcode-vue :value="url" :size="220" class="canvas" style="margin: 0 auto" />
</div>
@@ -24,10 +24,12 @@
interface Props {
path: string;
showQrcode?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
path: '',
showQrcode: true,
});
const copy = () => {

View File

@@ -15,7 +15,8 @@
<Form path="/addons/hgexample/default" />
</n-tab-pane>
<n-tab-pane name="websocket" tab="Websocket">
<Form path="/socket/hgexample/index/test?name=HotGo" />
<Form path="/socket/hgexample/index/test?name=HotGo" :showQrcode="false" />
<WebsocketTest />
</n-tab-pane>
</n-tabs>
</n-card>
@@ -24,6 +25,7 @@
<script lang="ts" setup>
import Form from './form.vue';
import WebsocketTest from './websocketTest.vue';
</script>
<style scoped>

View File

@@ -0,0 +1,134 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="测试websocket">
尝试在下方输入框中输入任意文字消息内容发送后websocket服务器收到会原样返回
</n-card>
</div>
<n-card :bordered="false" class="proCard">
<n-space vertical>
<n-input-group style="width: 520px">
<n-input
@keyup.enter="sendMessage"
:style="{ width: '78%' }"
placeholder="请输入消息内容"
:on-focus="onFocus"
:on-blur="onBlur"
v-model:value="inputMessage"
/>
<n-button type="primary" @click="sendMessage"> 发送消息</n-button>
</n-input-group>
<div class="mt-5"></div>
<n-timeline :icon-size="20">
<n-timeline-item color="grey" content="输入中.." v-if="isInput">
<template #icon>
<n-icon>
<MessageOutlined />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item
v-for="item in messages"
:key="item"
:type="item.type == Enum.SendType ? 'success' : 'info'"
:title="item.type == Enum.SendType ? '发送消息' : '收到消息'"
:content="item.content"
:time="item.time"
>
<template #icon>
<n-icon>
<SendOutlined v-if="item.type == Enum.SendType" />
<SoundOutlined v-if="item.type == Enum.ReceiveType" />
</n-icon>
</template>
</n-timeline-item>
</n-timeline>
</n-space>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { MessageOutlined, SendOutlined, SoundOutlined } from '@vicons/antd';
import { format } from 'date-fns';
import { addOnMessage, removeOnMessage, sendMsg, WebSocketMessage } from '@/utils/websocket';
import { useMessage } from 'naive-ui';
interface Message {
type: Enum;
content: string;
time: string;
}
const message = useMessage();
const messages = ref<Message[]>([]);
const inputMessage = ref('你好HotGo');
const isInput = ref(false);
const testMessageEvent = 'admin/addons/hgexample/testMessage';
enum Enum {
SendType = 1, // 发送类型
ReceiveType = 2, // 接受类型
}
function onFocus() {
isInput.value = true;
}
function onBlur() {
isInput.value = false;
}
function sendMessage() {
if (inputMessage.value == '') {
message.error('消息内容不能为空');
return;
}
// 发送消息
sendMsg(testMessageEvent, {
message: inputMessage.value,
});
const msg: Message = {
type: Enum.SendType,
content: inputMessage.value,
time: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
};
insertMessage(msg);
inputMessage.value = '';
}
// 存入本地记录
function insertMessage(msg: Message): void {
messages.value.unshift(msg); // 在头部插入消息
if (messages.value.length > 10) {
messages.value = messages.value.slice(0, 10); // 如果超过10个则只保留最前面10个
}
}
const onMessage = (res: WebSocketMessage) => {
const msg: Message = {
type: Enum.ReceiveType,
content: res.data.message,
time: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
};
insertMessage(msg);
};
onMounted(() => {
// 在当前页面注册消息监听
addOnMessage(testMessageEvent, onMessage);
});
onBeforeUnmount(() => {
// 移除消息监听
removeOnMessage(testMessageEvent);
});
</script>
<style scoped></style>

View File

@@ -21,11 +21,11 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-20000"
>
<template #tableTitle>
<n-button type="primary" @click="handleUpload">
<n-button type="primary" @click="handleUpload" class="ml-2">
<template #icon>
<n-icon>
<UploadOutlined />
@@ -33,26 +33,31 @@
</template>
上传文件
</n-button>
&nbsp;
<n-button type="primary" @click="handleUploadImage">
<n-button type="success" @click="handleMultipartUpload" class="ml-2">
<template #icon>
<n-icon>
<UploadOutlined />
<FileAddOutlined />
</n-icon>
</template>
上传大文件
</n-button>
<n-button type="primary" @click="handleUploadImage" class="ml-2">
<template #icon>
<n-icon>
<FileImageOutlined />
</n-icon>
</template>
上传图片
</n-button>
&nbsp;
<n-button type="primary" @click="handleUploadDoc">
<n-button type="primary" @click="handleUploadDoc" class="ml-2">
<template #icon>
<n-icon>
<UploadOutlined />
<FileWordOutlined />
</n-icon>
</template>
上传文档
</n-button>
&nbsp;
<n-button type="error" @click="batchDelete" :disabled="batchDeleteDisabled">
<n-button type="error" @click="batchDelete" :disabled="batchDeleteDisabled" class="ml-2">
<template #icon>
<n-icon>
<DeleteOutlined />
@@ -67,6 +72,7 @@
<FileUpload ref="fileUploadRef" :finish-call="handleFinishCall" />
<FileUpload ref="imageUploadRef" :finish-call="handleFinishCall" upload-type="image" />
<FileUpload ref="docUploadRef" :finish-call="handleFinishCall" upload-type="doc" />
<MultipartUpload ref="multipartUploadRef" @onFinish="handleFinishCall" />
</div>
</template>
@@ -77,8 +83,15 @@
import { BasicForm, useForm } from '@/components/Form/index';
import { Delete, List } from '@/api/apply/attachment';
import { columns, schemas } from './columns';
import { DeleteOutlined, UploadOutlined } from '@vicons/antd';
import {
DeleteOutlined,
UploadOutlined,
FileWordOutlined,
FileImageOutlined,
FileAddOutlined,
} from '@vicons/antd';
import FileUpload from '@/components/FileChooser/src/Upload.vue';
import MultipartUpload from '@/components/Upload/multipartUpload.vue';
import { Attachment } from '@/components/FileChooser/src/model';
const message = useMessage();
@@ -90,9 +103,10 @@
const fileUploadRef = ref();
const imageUploadRef = ref();
const docUploadRef = ref();
const multipartUploadRef = ref();
const actionColumn = reactive({
width: 150,
width: 120,
title: '操作',
key: 'action',
fixed: 'right',
@@ -132,6 +146,10 @@
docUploadRef.value.openModal();
}
function handleMultipartUpload() {
multipartUploadRef.value.openModal();
}
const loadDataTable = async (res) => {
return await List({ ...res, ...searchFormRef.value?.formModel });
};

View File

@@ -26,7 +26,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-20000"
>
<template #tableTitle>

View File

@@ -19,7 +19,7 @@
:row-key="(row) => row.id"
ref="actionRef"
:actionColumn="actionColumn"
:scroll-x="1800"
:scroll-x="1280"
>
<template #tableTitle>
<n-button type="primary" @click="addTable" class="min-left-space">

View File

@@ -19,7 +19,7 @@
:request="loadDataTable"
:row-key="(row) => row.id"
ref="actionRef"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-10000"
size="small"
>

View File

@@ -6,6 +6,7 @@ import { Option } from '@/api/creditsLog';
import { isNullObject } from '@/utils/is';
import { defRangeShortcuts } from '@/utils/dateUtil';
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
import {Dicts} from "@/api/dict/dict";
export interface State {
id: number;
@@ -240,7 +241,9 @@ export const columns = [
];
async function loadOptions() {
options.value = await Option();
options.value = await Dicts({
types: ['creditType', 'creditGroup'],
});
for (const item of schemas.value) {
switch (item.field) {
case 'creditType':

View File

@@ -14,7 +14,7 @@
<n-tab-pane
:name="item.key.toString()"
:tab="item.label"
v-for="item in options.status"
v-for="item in options.orderStatus"
:key="item.key"
>
<List :type="defaultTab" />

View File

@@ -21,7 +21,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-10000"
>
<template #tableTitle>

View File

@@ -6,6 +6,7 @@ import { Option } from '@/api/order';
import { isNullObject } from '@/utils/is';
import { defRangeShortcuts } from '@/utils/dateUtil';
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
import { Dicts } from '@/api/dict/dict';
export interface State {
id: number;
@@ -49,7 +50,7 @@ export function newState(state: State | null): State {
}
export const options = ref<Options>({
status: [],
orderStatus: [],
acceptRefundStatus: [],
payType: [],
});
@@ -170,12 +171,12 @@ export const columns = [
style: {
marginRight: '6px',
},
type: getOptionTag(options.value.status, row.status),
type: getOptionTag(options.value.orderStatus, row.status),
bordered: false,
},
{
default: () =>
getOptionLabel(options.value.status, row.status) +
getOptionLabel(options.value.orderStatus, row.status) +
(row.status === 9 ? '' + row.rejectRefundReason : ''),
}
);
@@ -190,11 +191,13 @@ export const columns = [
];
async function loadOptions() {
options.value = await Option();
options.value = await Dicts({
types: ['payType', 'orderStatus', 'acceptRefundStatus'],
});
for (const item of schemas.value) {
switch (item.field) {
case 'status':
item.componentProps.options = options.value.status;
item.componentProps.options = options.value.orderStatus;
break;
case 'acceptRefundStatus':
item.componentProps.options = options.value.acceptRefundStatus;

View File

@@ -23,7 +23,7 @@ export class State {
public image = ''; // 单图
public attachfile = ''; // 附件
public cityId = 0; // 所在城市
public switch = 1; // 显示开关
public switch = 2; // 显示开关
public sort = 0; // 排序
public status = 1; // 状态
public createdBy = 0; // 创建者
@@ -31,7 +31,12 @@ export class State {
public createdAt = ''; // 创建时间
public updatedAt = ''; // 修改时间
public deletedAt = ''; // 删除时间
}
constructor(state?: Partial<State>) {
if (state) {
Object.assign(this, state);
}
}}
export function newState(state: State | Record<string, any> | null): State {
if (state !== null) {

View File

@@ -1,23 +0,0 @@
import { cloneDeep } from 'lodash-es';
export const genInfoObj = {
label: '',
name: '',
group: 1,
version: 'v1.0.0',
brief: '',
description: '',
author: '',
};
export const selectListObj = {
groupType: [],
status: [],
};
export function newState(state) {
if (state !== null) {
return cloneDeep(state);
}
return cloneDeep(genInfoObj);
}

View File

@@ -60,7 +60,7 @@
>
<n-form-item label="插件标签" path="label">
<n-input placeholder="请输入" v-model:value="formParams.label" />
<template #feedback>显示在插件列表中的标识. 不要超过20个字符</template>
<template #feedback>显示在插件列表中的模块名称. 不要超过20个字符</template>
</n-form-item>
<n-form-item label="插件包名" path="name">
@@ -73,12 +73,25 @@
<n-form-item label="功能分组" path="group">
<n-select
placeholder="请选择"
:options="selectList.groupType"
:options="options.addonsGroupOptions"
v-model:value="formParams.group"
:on-update:value="onUpdateValueGroup"
/>
</n-form-item>
<n-form-item label="扩展功能" path="extend">
<n-checkbox-group v-model:value="formParams.extend">
<n-space item-style="display: flex;">
<n-checkbox
:value="option.value"
:label="option.label"
v-for="option in options.addonsExtend"
:key="option"
/>
</n-space>
</n-checkbox-group>
</n-form-item>
<n-form-item label="版本" path="version">
<n-input placeholder="请输入" v-model:value="formParams.version" />
<template #feedback>此版本号用于插件的版本更新</template>
@@ -114,24 +127,14 @@
</template>
<script lang="ts" setup>
import { h, onBeforeMount, reactive, ref } from 'vue';
import {
NIcon,
NTag,
NIconWrapper,
useMessage,
NImage,
useDialog,
useNotification,
} from 'naive-ui';
import { h, reactive, ref } from 'vue';
import { NIcon, useMessage, useDialog, useNotification } from 'naive-ui';
import { BasicTable, TableAction } from '@/components/Table';
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
import { List, Selects, Build, UnInstall, Install, Upgrade } from '@/api/develop/addons';
import { PlusOutlined } from '@vicons/antd';
import { newState } from '@/views/develop/addons/components/model';
import { errorImg } from '@/utils/hotgo';
import { isUrl } from '@/utils/is';
import { getIconComponent } from '@/utils/icons';
import { BasicForm, useForm } from '@/components/Form/index';
import { List, Build, UnInstall, Install, Upgrade } from '@/api/develop/addons';
import { PlusOutlined, QuestionCircleOutlined } from '@vicons/antd';
import { newState, schemas, columns, options } from './model';
import { adaModalWidth } from '@/utils/hotgo';
const dialog = useDialog();
const message = useMessage();
@@ -145,158 +148,6 @@
const checkedIds = ref([]);
const searchFormRef = ref<any>();
const selectList = ref({
groupType: [],
status: [],
});
const columns = [
{
title: '图标',
key: 'logo',
width: 80,
render(row) {
if (isUrl(row.logo)) {
return h(NImage, {
width: 48,
height: 48,
src: row.logo,
fallbackSrc: errorImg,
style: {
width: '48px',
height: '48px',
'max-width': '100%',
'max-height': '100%',
},
});
} else {
return h(
NIconWrapper,
{
size: 48,
borderRadius: 8,
},
{
default: () =>
h(
NIcon,
{
size: 36,
style: {
marginTop: '-8px',
},
},
{
default: () => h(getIconComponent(row.logo)),
}
),
}
);
}
},
},
{
title: '模块名称',
key: 'name',
width: 120,
render(row) {
return h('div', {
innerHTML:
'<div >' + row.label + '<br><span style="opacity: 0.8;">' + row.name + '</span></div>',
});
},
},
{
title: '作者',
key: 'author',
width: 100,
},
{
title: '分组',
key: 'groupName',
render(row) {
return h(
NTag,
{
style: {
marginRight: '6px',
},
type: 'info',
bordered: false,
},
{
default: () => row.groupName,
}
);
},
width: 120,
},
{
title: '简介',
key: 'brief',
render(row) {
return row.brief;
},
width: 180,
},
{
title: '详细描述',
key: 'description',
width: 300,
render(row) {
return h('p', { id: 'app' }, [
h('div', {
innerHTML: '<div style="white-space: pre-wrap">' + row.description + '</div>',
}),
]);
},
},
{
title: '版本',
key: 'version',
width: 100,
},
];
const schemas = ref<FormSchema[]>([
{
field: 'name',
component: 'NInput',
label: '模块名称',
componentProps: {
placeholder: '请输入模块名称或标签',
onInput: (e: any) => {
console.log(e);
},
},
rules: [{ trigger: ['blur'] }],
},
{
field: 'group',
component: 'NSelect',
label: '分组',
componentProps: {
placeholder: '请选择分组',
options: [],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'status',
component: 'NSelect',
label: '安装状态',
componentProps: {
placeholder: '请选择状态',
options: [],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
]);
const actionColumn = reactive({
width: 220,
title: '操作',
@@ -345,7 +196,7 @@
}
const loadDataTable = async (res) => {
mapWidth();
adaModalWidth(dialogWidth);
return await List({ ...res, ...searchFormRef.value?.formModel });
};
@@ -365,9 +216,6 @@
reloadTable();
});
},
onNegativeClick: () => {
// message.error('取消');
},
});
}
@@ -383,9 +231,6 @@
reloadTable();
});
},
onNegativeClick: () => {
// message.error('取消');
},
});
}
@@ -401,9 +246,6 @@
reloadTable();
});
},
onNegativeClick: () => {
// message.error('取消');
},
});
}
@@ -437,9 +279,6 @@
buildSuccessNotify();
});
},
onNegativeClick: () => {
// message.error('取消');
},
});
} else {
message.error('请填写完整信息');
@@ -448,32 +287,6 @@
});
}
function mapWidth() {
let val = document.body.clientWidth;
const def = 840; // 默认宽度
if (val < def) {
dialogWidth.value = '100%';
} else {
dialogWidth.value = def + 'px';
}
return dialogWidth.value;
}
const loadSelect = async () => {
selectList.value = await Selects({});
for (const item of schemas.value) {
switch (item.field) {
case 'status':
item.componentProps.options = selectList.value.status;
break;
case 'group':
item.componentProps.options = selectList.value.groupType;
break;
}
}
};
function onUpdateValueGroup(value) {
formParams.value.group = value;
}
@@ -500,10 +313,6 @@
},
});
}
onBeforeMount(async () => {
await loadSelect();
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,203 @@
import { cloneDeep } from 'lodash-es';
import { h, ref } from 'vue';
import { Dicts } from '@/api/dict/dict';
import { errorImg, Option, Options } from '@/utils/hotgo';
import { isUrl } from '@/utils/is';
import { NIcon, NIconWrapper, NImage, NTag } from 'naive-ui';
import { getIconComponent } from '@/utils/icons';
import { FormSchema } from '@/components/Form';
export const genInfoObj = {
label: '',
name: '',
group: 1,
extend: ['resourcePublic', 'resourceTemplate'],
version: 'v1.0.0',
brief: '',
description: '',
author: '',
};
export function newState(state) {
if (state !== null) {
return cloneDeep(state);
}
return cloneDeep(genInfoObj);
}
export const columns = [
{
title: '图标',
key: 'logo',
width: 80,
render(row) {
if (isUrl(row.logo)) {
return h(NImage, {
width: 48,
height: 48,
src: row.logo,
fallbackSrc: errorImg,
style: {
width: '48px',
height: '48px',
'max-width': '100%',
'max-height': '100%',
},
});
} else {
return h(
NIconWrapper,
{
size: 48,
borderRadius: 8,
},
{
default: () =>
h(
NIcon,
{
size: 36,
style: {
marginTop: '-8px',
},
},
{
default: () => h(getIconComponent(row.logo)),
}
),
}
);
}
},
},
{
title: '模块名称',
key: 'name',
width: 120,
render(row) {
return h('div', {
innerHTML:
'<div >' + row.label + '<br><span style="opacity: 0.8;">' + row.name + '</span></div>',
});
},
},
{
title: '作者',
key: 'author',
width: 100,
},
{
title: '分组',
key: 'groupName',
render(row) {
return h(
NTag,
{
style: {
marginRight: '6px',
},
type: 'info',
bordered: false,
},
{
default: () => row.groupName,
}
);
},
width: 120,
},
{
title: '简介',
key: 'brief',
render(row) {
return row.brief;
},
width: 180,
},
{
title: '详细描述',
key: 'description',
width: 300,
render(row) {
return h('p', { id: 'app' }, [
h('div', {
innerHTML: '<div style="white-space: pre-wrap">' + row.description + '</div>',
}),
]);
},
},
{
title: '版本',
key: 'version',
width: 100,
},
];
export const schemas = ref<FormSchema[]>([
{
field: 'name',
component: 'NInput',
label: '模块名称',
componentProps: {
placeholder: '请输入模块名称或标签',
onInput: (e: any) => {
console.log(e);
},
},
rules: [{ trigger: ['blur'] }],
},
{
field: 'group',
component: 'NSelect',
label: '分组',
componentProps: {
placeholder: '请选择分组',
options: [],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
{
field: 'status',
component: 'NSelect',
label: '安装状态',
componentProps: {
placeholder: '请选择状态',
options: [],
onUpdateValue: (e: any) => {
console.log(e);
},
},
},
]);
export interface IOptions extends Options {
addonsGroupOptions: Option[];
addonsInstallStatus: Option[];
addonsExtend: Option[];
}
export const options = ref<IOptions>({
addonsGroupOptions: [],
addonsInstallStatus: [],
addonsExtend: [],
});
async function loadOptions() {
options.value = await Dicts({
types: ['addonsGroupOptions', 'addonsInstallStatus', 'addonsExtend'],
});
for (const item of schemas.value) {
switch (item.field) {
case 'status':
item.componentProps.options = options.value.addonsInstallStatus;
break;
case 'group':
item.componentProps.options = options.value.addonsGroupOptions;
break;
}
}
}
await loadOptions();

View File

@@ -42,7 +42,7 @@
import { BasicTable } from '@/components/Table';
import { genInfoObj, selectListObj } from '@/views/develop/code/components/model';
import { ColumnList } from '@/api/develop/code';
import { NButton, NCheckbox, NInput, NSelect, NTooltip, NTreeSelect } from 'naive-ui';
import { NButton, NCheckbox, NInput, NSelect, NTooltip, NTreeSelect,NCascader } from 'naive-ui';
import { HelpCircleOutline, Reload } from '@vicons/ionicons5';
import { renderIcon } from '@/utils';
import { cloneDeep } from 'lodash-es';
@@ -278,10 +278,15 @@
key: 'dictType',
width: 300,
render(row) {
return h(NTreeSelect, {
value: row.dictType,
disabled: row.name === 'id',
if (row.dictType == 0){
row.dictType = null;
}
return h(NCascader, {
placeholder: '请选择字典类型',
filterable: true,
clearable: true,
disabled: row.name === 'id',
value: row.dictType,
options: props.selectList?.dictMode ?? [],
onUpdateValue: function (e) {
row.dictType = e;

View File

@@ -210,7 +210,7 @@
width: 140,
},
{
title: '菜单名称',
title: '生成名称',
key: 'tableComment',
width: 140,
},

View File

@@ -43,6 +43,14 @@
loading.value = true;
init();
});
function open(src: string) {
frameSrc.value = src;
}
defineExpose({
open,
});
</script>
<style lang="less" scoped>

View File

@@ -20,7 +20,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-20000"
>
<template #tableTitle>

View File

@@ -24,7 +24,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-20000"
>
<template #tableTitle>
@@ -82,7 +82,7 @@
const checkedIds = ref([]);
const actionColumn = reactive({
width: 150,
width: 180,
title: '操作',
key: 'action',
fixed: 'right',

View File

@@ -25,7 +25,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
>
<template #tableTitle>
<n-button type="error" @click="batchDelete" :disabled="batchDeleteDisabled">

View File

@@ -19,7 +19,7 @@
:row-key="(row) => row.id"
ref="actionRef"
:actionColumn="actionColumn"
:scroll-x="1800"
:scroll-x="1280"
>
<template #tableTitle>
<n-button type="info" @click="openGroupModal">

View File

@@ -16,7 +16,7 @@
:row-key="(row) => row.id"
ref="actionRef"
:actionColumn="actionColumn"
:scroll-x="1800"
:scroll-x="1280"
/>
</n-card>
</div>

View File

@@ -27,7 +27,7 @@
:actionColumn="actionColumn"
:checked-row-keys="checkedIds"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
:resizeHeightOffset="-10000"
size="small"
>
@@ -123,7 +123,7 @@
const showModal = ref(false);
const actionColumn = reactive({
width: 180,
width: 150,
title: '操作',
key: 'action',
fixed: 'right',

View File

@@ -162,11 +162,11 @@ export const columns = [
key: 'content',
width: 320,
},
{
title: '调用行',
key: 'line',
width: 150,
},
// {
// title: '调用行',
// key: 'line',
// width: 150,
// },
{
title: '触发时间',
key: 'triggerNs',

View File

@@ -93,9 +93,9 @@
import DataItem from './components/DataItem.vue';
import LoadChart from './components/chart/LoadChart.vue';
import FullYearSalesChart from './components/chart/FullYearSalesChart.vue';
import { defineComponent, inject, onMounted, ref, onUnmounted } from 'vue';
import { defineComponent, onMounted, ref, onUnmounted } from 'vue';
import { SocketEnum } from '@/enums/socketEnum';
import { addOnMessage, sendMsg } from '@/utils/websocket';
import { addOnMessage, removeOnMessage, sendMsg, WebSocketMessage } from '@/utils/websocket';
import { formatBefore } from '@/utils/dateUtil';
import { useDialog, useMessage } from 'naive-ui';
@@ -179,35 +179,32 @@
const loading = ref(true);
const loadChartRef = ref<InstanceType<typeof LoadChart>>();
const fullYearSalesChartRef = ref<InstanceType<typeof FullYearSalesChart>>();
const onMessageList = inject('onMessageList');
const onAdminMonitor = (res: { data: string }) => {
const data = JSON.parse(res.data);
if (data.event === SocketEnum.EventAdminMonitorRunInfo) {
loading.value = false;
if (data.code == SocketEnum.CodeErr) {
message.error('查询出错:' + data.event);
return;
}
dataRunInfo.value = data.data;
// 运行信息
const onMessageAdminMonitorRunInfo = (res: WebSocketMessage) => {
loading.value = false;
if (res.code == SocketEnum.CodeErr) {
message.error('查询出错:' + res.event);
return;
}
if (data.event === SocketEnum.EventAdminMonitorTrends) {
loading.value = false;
if (data.code == SocketEnum.CodeErr) {
message.error('查询出错:' + data.event);
return;
}
dataSource.value = data.data;
return;
}
dataRunInfo.value = res.data;
};
addOnMessage(onMessageList, onAdminMonitor);
// 服务器信息
const onMessageAdminMonitorTrends = (res: WebSocketMessage) => {
loading.value = false;
if (res.code == SocketEnum.CodeErr) {
message.error('查询出错:' + res.event);
return;
}
dataSource.value = res.data;
};
onMounted(() => {
addOnMessage(SocketEnum.EventAdminMonitorRunInfo, onMessageAdminMonitorRunInfo);
addOnMessage(SocketEnum.EventAdminMonitorTrends, onMessageAdminMonitorTrends);
loading.value = true;
sendMsg(SocketEnum.EventAdminMonitorTrends);
sendMsg(SocketEnum.EventAdminMonitorRunInfo);
@@ -231,6 +228,8 @@
onUnmounted(() => {
window.clearInterval(timer.value);
removeOnMessage(SocketEnum.EventAdminMonitorTrends);
removeOnMessage(SocketEnum.EventAdminMonitorRunInfo);
});
return {

View File

@@ -20,7 +20,7 @@
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
:scroll-x="1800"
:scroll-x="1280"
>
<template #tableTitle>
<n-button
@@ -288,7 +288,7 @@
});
const actionColumn = reactive({
width: 220,
width: 240,
title: '操作',
key: 'action',
fixed: 'right',

View File

@@ -6,6 +6,7 @@ import { getPostOption } from '@/api/org/post';
import { FormSchema, useForm } from '@/components/Form';
import { statusOptions } from '@/enums/optionsiEnum';
import { defRangeShortcuts } from '@/utils/dateUtil';
import {Dicts} from "@/api/dict/dict";
// 增加余额/积分.
@@ -213,14 +214,10 @@ export async function loadOptions() {
treeDataToCompressed(role.list);
}
const post = await getPostOption();
if (post.list && post.list.length > 0) {
for (let i = 0; i < post.list.length; i++) {
post.list[i].label = post.list[i].name;
post.list[i].value = post.list[i].id;
}
options.value.post = post.list;
}
const tmpOptions = await Dicts({
types: ['adminPostOption'],
});
options.value.post =tmpOptions?.adminPostOption;
}
function treeDataToCompressed(source) {

View File

@@ -26,9 +26,6 @@
<template #suffix> MB</template>
</n-input-number>
</n-form-item>
<n-form-item label="图片类型限制" path="uploadImageType">
<n-input v-model:value="formValue.uploadImageType" placeholder="" />
</n-form-item>
<n-form-item label="文件大小限制" path="uploadFileSize">
<n-input-number
@@ -39,6 +36,11 @@
<template #suffix> MB</template>
</n-input-number>
</n-form-item>
<n-form-item label="图片类型限制" path="uploadImageType">
<n-input v-model:value="formValue.uploadImageType" placeholder="" />
</n-form-item>
<n-form-item label="文件类型限制" path="uploadFileType">
<n-input v-model:value="formValue.uploadFileType" placeholder="" />
</n-form-item>