feat: print and perf style

This commit is contained in:
xiaoxian521 2022-03-02 13:56:07 +08:00
parent e3fda52801
commit 727c0fe3c0
31 changed files with 4475 additions and 1787 deletions

View File

@ -14,20 +14,16 @@
<body> <body>
<div id="app"> <div id="app">
<style> <style>
* {
margin: 0;
padding: 0;
}
html, html,
body { body,
#app {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
position: relative;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
overflow: hidden; overflow: hidden;
font-family: "Reggae One", cursive;
} }
.loader, .loader,
@ -53,6 +49,8 @@
transform: translateZ(0); transform: translateZ(0);
-webkit-animation-delay: -0.16s; -webkit-animation-delay: -0.16s;
animation-delay: -0.16s; animation-delay: -0.16s;
top: 0;
transform: translate(-50%, 0);
} }
.loader:before, .loader:before,
@ -96,7 +94,7 @@
} }
} }
</style> </style>
<div class="loader">Loading...</div> <div class="loader"></div>
</div> </div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>

View File

@ -76,7 +76,7 @@ const frameRouter = {
}, },
children: [ children: [
{ {
path: "/frame/pure", path: "/iframe/pure",
name: "reFramePure", name: "reFramePure",
meta: { meta: {
i18n: true, i18n: true,

View File

@ -116,7 +116,7 @@
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"typescript": "^4.5.5", "typescript": "^4.5.5",
"unplugin-element-plus": "^0.2.0", "unplugin-element-plus": "^0.2.0",
"vite": "^2.8.5", "vite": "^2.8.6",
"vite-plugin-live-reload": "^2.1.0", "vite-plugin-live-reload": "^2.1.0",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-remove-console": "^0.0.6", "vite-plugin-remove-console": "^0.0.6",

5031
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -126,7 +126,7 @@ onMounted(() => {
} }
.iconfont { .iconfont {
font-size: 25px; font-size: 18px;
} }
.control-container p { .control-container p {

View File

@ -24,6 +24,8 @@ import Location from "@iconify-icons/ep/location";
import Tickets from "@iconify-icons/ep/tickets"; import Tickets from "@iconify-icons/ep/tickets";
import OfficeBuilding from "@iconify-icons/ep/office-building"; import OfficeBuilding from "@iconify-icons/ep/office-building";
import Notebook from "@iconify-icons/ep/notebook"; import Notebook from "@iconify-icons/ep/notebook";
import Rank from "@iconify-icons/ep/rank";
import videoPlay from "@iconify-icons/ep/video-play";
import Monitor from "@iconify-icons/ep/monitor"; import Monitor from "@iconify-icons/ep/monitor";
addIcon("check", Check); addIcon("check", Check);
addIcon("menu", Menu); addIcon("menu", Menu);
@ -47,6 +49,8 @@ addIcon("location", Location);
addIcon("tickets", Tickets); addIcon("tickets", Tickets);
addIcon("office-building", OfficeBuilding); addIcon("office-building", OfficeBuilding);
addIcon("notebook", Notebook); addIcon("notebook", Notebook);
addIcon("video-play", videoPlay);
addIcon("rank", Rank);
addIcon("monitor", Monitor); addIcon("monitor", Monitor);
// remixicon // remixicon

View File

@ -55,4 +55,8 @@ onMounted(() => {
box-sizing: border-box; box-sizing: border-box;
} }
} }
.main-content {
margin: 0;
}
</style> </style>

View File

@ -8,7 +8,6 @@ import { useAppStoreHook } from "/@/store/modules/app";
import { remainingPaths } from "/@/router/modules/index"; import { remainingPaths } from "/@/router/modules/index";
import { Title } from "../../../public/serverConfig.json"; import { Title } from "../../../public/serverConfig.json";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme"; import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import { remainingPaths } from "/@/router/modules/index";
export function useNav() { export function useNav() {
const pureApp = useAppStoreHook(); const pureApp = useAppStoreHook();

View File

@ -44,6 +44,12 @@ import {
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem,
ElTreeV2, ElTreeV2,
ElTable,
ElTableColumn,
ElLink,
ElColorPicker,
ElSelect,
ElOption,
// 指令 // 指令
ElLoading, ElLoading,
ElInfiniteScroll ElInfiniteScroll
@ -96,7 +102,13 @@ const components = [
ElEmpty, ElEmpty,
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem,
ElTreeV2 ElTreeV2,
ElTable,
ElTableColumn,
ElLink,
ElColorPicker,
ElSelect,
ElOption
]; ];
export function useElementPlus(app: App) { export function useElementPlus(app: App) {

View File

@ -37,6 +37,7 @@ export default {
hsAble: "Able", hsAble: "Able",
hsMenuTree: "Menu Tree", hsMenuTree: "Menu Tree",
hsWatermark: "Water Mark", hsWatermark: "Water Mark",
hsPrint: "Print",
hsExternalPage: "External Page", hsExternalPage: "External Page",
hsPureDocument: "Pure Document", hsPureDocument: "Pure Document",
hsEpDocument: "Element Plus Document" hsEpDocument: "Element Plus Document"

View File

@ -37,6 +37,7 @@ export default {
hsAble: "功能", hsAble: "功能",
hsMenuTree: "菜单树结构", hsMenuTree: "菜单树结构",
hsWatermark: "水印", hsWatermark: "水印",
hsPrint: "打印",
hsExternalPage: "外部页面", hsExternalPage: "外部页面",
hsPureDocument: "平台文档", hsPureDocument: "平台文档",
hsEpDocument: "Element Plus文档" hsEpDocument: "Element Plus文档"

View File

@ -30,6 +30,15 @@ const ableRouter = {
title: $t("menus.hsWatermark"), title: $t("menus.hsWatermark"),
i18n: true i18n: true
} }
},
{
path: "/able/print",
name: "rePrint",
component: () => import("/@/views/able/print.vue"),
meta: {
title: $t("menus.hsPrint"),
i18n: true
}
} }
] ]
}; };

View File

@ -12,8 +12,8 @@ body {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility; text-rendering: optimizelegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
Microsoft YaHei, Arial, sans-serif; "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
} }
html { html {

226
src/utils/print.ts Normal file
View File

@ -0,0 +1,226 @@
interface PrintFunction {
extendOptions: Function;
getStyle: Function;
setDomHeight: Function;
toPrint: Function;
}
const Print = function (dom, options?: object): PrintFunction {
options = options || {};
// @ts-expect-error
if (!(this instanceof Print)) return new Print(dom, options);
this.conf = {
styleStr: "",
// Elements that need to dynamically get and set the height
setDomHeightArr: [],
// Echart dom List
echartDomArr: [],
// Callback before printing
printBeforeFn: null,
// Callback after printing
printDoneCallBack: null
};
for (const key in this.conf) {
// eslint-disable-next-line no-prototype-builtins
if (key && options.hasOwnProperty(key)) {
this.conf[key] = options[key];
}
}
if (typeof dom === "string") {
this.dom = document.querySelector(dom);
} else {
this.dom = this.isDOM(dom) ? dom : dom.$el;
}
if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) {
this.setDomHeight(this.conf.setDomHeightArr);
}
this.init();
};
Print.prototype = {
/**
* init
*/
init: function (): void {
const content = this.getStyle() + this.getHtml();
this.writeIframe(content);
},
/**
* Configuration property extension
* @param {Object} obj
* @param {Object} obj2
*/
extendOptions: function <T>(obj, obj2: T): T {
for (const k in obj2) {
obj[k] = obj2[k];
}
return obj;
},
/**
Copy all styles of the original page
*/
getStyle: function (): string {
let str = "";
const styles: NodeListOf<Element> = document.querySelectorAll("style,link");
for (let i = 0; i < styles.length; i++) {
str += styles[i].outerHTML;
}
str += `<style>.no-print{display:none;}${this.conf.styleStr}</style>`;
return str;
},
// form assignment
getHtml: function (): Element {
const inputs = document.querySelectorAll("input");
const selects = document.querySelectorAll("select");
const textareas = document.querySelectorAll("textarea");
for (let k = 0; k < inputs.length; k++) {
if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
if (inputs[k].checked == true) {
inputs[k].setAttribute("checked", "checked");
} else {
inputs[k].removeAttribute("checked");
}
} else if (inputs[k].type == "text") {
inputs[k].setAttribute("value", inputs[k].value);
} else {
inputs[k].setAttribute("value", inputs[k].value);
}
}
for (let k2 = 0; k2 < textareas.length; k2++) {
if (textareas[k2].type == "textarea") {
textareas[k2].innerHTML = textareas[k2].value;
}
}
for (let k3 = 0; k3 < selects.length; k3++) {
if (selects[k3].type == "select-one") {
const child = selects[k3].children;
for (const i in child) {
if (child[i].tagName == "OPTION") {
// @ts-ignore
if (child[i].selected == true) {
child[i].setAttribute("selected", "selected");
} else {
child[i].removeAttribute("selected");
}
}
}
}
}
return this.dom.outerHTML;
},
/**
create iframe
*/
writeIframe: function (content) {
let w: Document | Window;
let doc: Document;
const iframe: HTMLIFrameElement = document.createElement("iframe");
const f: HTMLIFrameElement = document.body.appendChild(iframe);
iframe.id = "myIframe";
iframe.setAttribute(
"style",
"position:absolute;width:0;height:0;top:-10px;left:-10px;"
);
// eslint-disable-next-line prefer-const
w = f.contentWindow || f.contentDocument;
// eslint-disable-next-line prefer-const
doc = f.contentDocument || f.contentWindow.document;
doc.open();
doc.write(content);
doc.close();
// eslint-disable-next-line @typescript-eslint/no-this-alias
const _this = this;
iframe.onload = function (): void {
// Before popping, callback
if (_this.conf.printBeforeFn) {
_this.conf.printBeforeFn({ doc });
}
_this.drawEchartImg(doc).then(() => {
_this.toPrint(w);
setTimeout(function () {
document.body.removeChild(iframe);
// After popup, callback
if (_this.conf.printDoneCallBack) {
_this.conf.printDoneCallBack();
}
}, 100);
});
};
},
/**
* echarts printing
* @param {Object} doc iframe window
*/
drawEchartImg(doc): Promise<void> {
return new Promise<void>(resolve => {
if (this.conf.echartDomArr && this.conf.echartDomArr.length > 0) {
this.conf.echartDomArr.forEach(e => {
const dom = doc.querySelector("#" + e.$el.id);
const img = new Image();
const w = dom.offsetWidth + "px";
const H = dom.offsetHeight + "px";
img.style.width = w;
img.style.height = H;
img.src = e.imgSrc;
dom.innerHTML = "";
dom.appendChild(img);
});
}
resolve();
});
},
/**
Print
*/
toPrint: function (frameWindow): void {
try {
setTimeout(function () {
frameWindow.focus();
try {
if (!frameWindow.document.execCommand("print", false, null)) {
frameWindow.print();
}
} catch (e) {
frameWindow.print();
}
frameWindow.close();
}, 10);
} catch (err) {
console.error(err);
}
},
isDOM:
typeof HTMLElement === "object"
? function (obj) {
return obj instanceof HTMLElement;
}
: function (obj) {
return (
obj &&
typeof obj === "object" &&
obj.nodeType === 1 &&
typeof obj.nodeName === "string"
);
},
/**
* Set the height of the specified dom element by getting the existing height of the dom element and setting
* @param {Array} arr
*/
setDomHeight(arr) {
if (arr && arr.length) {
arr.forEach(name => {
const domArr = document.querySelectorAll(name);
domArr.forEach(dom => {
dom.style.height = dom.offsetHeight + "px";
});
});
}
}
};
export default Print;

View File

@ -6,31 +6,78 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from "vue"; import { ref, computed } from "vue";
import type { ElTreeV2 } from "element-plus";
import { transformI18n } from "/@/plugins/i18n";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
import { extractPathList, deleteChildren } from "/@/utils/tree"; import { extractPathList, deleteChildren } from "/@/utils/tree";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import type { TreeNode } from "element-plus/es/components/tree-v2/src/types";
interface treeNode extends TreeNode {
meta: {
title: string;
i18n: boolean;
};
}
const query = ref("");
let dataProps = ref({ let dataProps = ref({
value: "uniqueId", value: "uniqueId",
children: "children" children: "children"
}); });
const treeRef = ref<InstanceType<typeof ElTreeV2>>();
let menusData = computed(() => { let menusData = computed(() => {
return deleteChildren(usePermissionStoreHook().menusTree); return deleteChildren(usePermissionStoreHook().menusTree);
}); });
let expandedKeys = extractPathList(menusData.value); let expandedKeys = extractPathList(menusData.value);
const onQueryChanged = (query: string) => {
// @ts-expect-error
treeRef.value!.filter(query);
};
const filterMethod = (query: string, node: treeNode) => {
return transformI18n(node.meta.title, node.meta.i18n)!.indexOf(query) !== -1;
};
</script> </script>
<template> <template>
<el-tree-v2 <el-card>
:data="menusData" <template #header>
:props="dataProps" <div class="card-header">
show-checkbox <span class="font-medium"
:height="500" >菜单树结构采用element-plus的
:default-expanded-keys="expandedKeys" <el-link
> href="https://element-plus.gitee.io/zh-CN/component/tree-v2.html"
<template #default="{ data }"> target="_blank"
<span>{{ $t(data.meta.title) }}</span> :icon="useRenderIcon('node-tree')"
style="font-size: 16px; margin: 0 5px 4px 0"
>Tree V2</el-link
>组件并支持国际化</span
>
</div>
</template> </template>
</el-tree-v2> <el-input
class="mb-4"
v-model="query"
placeholder="请输入关键字查找"
clearable
@input="onQueryChanged"
></el-input>
<el-tree-v2
ref="treeRef"
:data="menusData"
:props="dataProps"
show-checkbox
:height="500"
:filter-method="filterMethod"
:default-expanded-keys="expandedKeys"
>
<template #default="{ data }">
<span>{{ $t(data.meta.title) }}</span>
</template>
</el-tree-v2>
</el-card>
</template> </template>

329
src/views/able/print.vue Normal file
View File

@ -0,0 +1,329 @@
<script setup lang="ts">
import Print from "/@/utils/print";
import { reactive, ref } from "vue";
import { VxeTablePropTypes } from "vxe-table";
import { ReLine } from "/@/components/ReCharts/index";
interface User {
date: string;
name: string;
address: string;
}
const demo1 = reactive({
tableData: [
{
id: 10001,
name: "Test1",
role: "Develop",
sex: "Man",
age: 28,
address: "test abc"
},
{
id: 10002,
name: "Test2",
role: "Test",
sex: "Women",
age: 22,
address: "Guangzhou"
},
{
id: 10003,
name: "Test3",
role: "PM",
sex: "Man",
age: 32,
address: "Shanghai"
},
{
id: 10004,
name: "Test4",
role: "Designer",
sex: "Women",
age: 24,
address: "Shanghai"
}
]
});
const value = ref("1");
const options = [
{
value: "1",
el: ".el-table",
label: "Element-Plus Table"
},
{
value: "2",
el: ".vxe-table",
label: "Vxe Table"
},
{
value: "3",
el: ".echart",
label: "Echart"
},
{
value: "4",
el: ".img",
label: "Image"
}
];
function onPrint() {
let el = options.filter(v => v.value === value.value)[0]?.el;
Print(el).toPrint;
}
const headerCellStyle: VxeTablePropTypes.HeaderCellStyle = ({ column }) => {
if (column.property === "name") {
return {
backgroundColor: "#f60",
color: "#ffffff"
};
}
};
const rowStyle: VxeTablePropTypes.RowStyle = ({ rowIndex }) => {
if ([2, 3, 5].includes(rowIndex)) {
return {
backgroundColor: "red",
color: "#ffffff"
};
}
};
const cellStyle: VxeTablePropTypes.CellStyle = ({ row, column }) => {
if (column.property === "sex") {
if (row.sex >= "1") {
return {
backgroundColor: "#187"
};
} else if (row.age === 26) {
return {
backgroundColor: "#2db7f5"
};
}
}
};
const tableRowClassName = ({ rowIndex }: { row: User; rowIndex: number }) => {
if (rowIndex === 1) {
return "warning-row";
} else if (rowIndex === 3) {
return "success-row";
}
return "";
};
const tableData: User[] = [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
}
];
</script>
<template>
<el-card>
<template #header>
<div class="card-header">
<span class="font-medium">打印功能报表图表图片</span>
<div>
<el-select
v-model="value"
class="m-2"
placeholder="Select"
size="small"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<el-button size="small" type="primary" @click="onPrint"
>打印</el-button
>
</div>
</div>
</template>
<el-row :gutter="24">
<el-col
:xs="22"
:sm="22"
:md="11"
:lg="11"
:xl="11"
style="margin: 10px; border: 0.01rem solid var(--el-color-primary)"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 200
}
}"
>
<p class="font-medium pt-1">Element-Plus Table</p>
<el-table
class="el-table"
:data="tableData"
border
style="margin: 40px auto; width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</el-col>
<el-col
:xs="22"
:sm="22"
:md="11"
:lg="11"
:xl="11"
style="margin: 10px; border: 0.01rem solid var(--el-color-primary)"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 200
}
}"
>
<p class="font-medium pt-1">Vxe Table</p>
<vxe-table
class="vxe-table"
border
style="margin: 40px auto"
:header-cell-style="headerCellStyle"
:row-style="rowStyle"
:cell-style="cellStyle"
:data="demo1.tableData"
>
<vxe-column type="seq" width="60"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
<vxe-column field="attr1" title="Attr1"></vxe-column>
<vxe-column
field="address"
title="Address"
show-overflow
></vxe-column>
</vxe-table>
</el-col>
<el-col
:xs="22"
:sm="22"
:md="11"
:lg="11"
:xl="11"
style="
width: 200px;
height: 300px;
margin: 10px;
border: 0.01rem solid var(--el-color-primary);
"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 200
}
}"
>
<p class="font-medium pt-1">Echart</p>
<ReLine class="echart" style="margin: 0 auto" />
</el-col>
<el-col
:xs="22"
:sm="22"
:md="11"
:lg="11"
:xl="11"
style="
width: 200px;
height: 300px;
margin: 10px;
border: 0.01rem solid var(--el-color-primary);
"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 200
}
}"
>
<p class="font-medium pt-1">Image</p>
<img
src="../../assets/avatars.jpg"
alt="avatars"
class="img"
style="width: 200px; height: 200px; margin: 50px auto"
/>
</el-col>
</el-row>
</el-card>
</template>
<style lang="scss" scoped>
:deep(.el-table__row.warning-row) {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
:deep(.el-table__row.success-row) {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -1,13 +1,34 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useWatermark } from "/@/utils/watermark"; import { useWatermark } from "/@/utils/watermark";
let color = ref("#409EFF");
let value = ref("vue-pure-admin");
const { setWatermark, clear } = useWatermark(); const { setWatermark, clear } = useWatermark();
</script> </script>
<template> <template>
<div> <el-card>
<el-button @click="setWatermark('vue-pure-admin')">创建</el-button> <template #header>
<div class="card-header">
<span class="font-medium">页面水印功能</span>
</div>
</template>
<span>请输入要创建水印的值</span
><el-input
class="mb-4 mr-4"
style="width: 200px"
v-model="value"
clearable
></el-input>
<span>请选择要创建水印的颜色</span
><el-color-picker v-model="color" show-alpha />
<br />
<el-button @click="setWatermark(value, { fillStyle: color })"
>创建</el-button
>
<el-button @click="clear">清除</el-button> <el-button @click="clear">清除</el-button>
</div> </el-card>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@ -7,13 +7,19 @@ const url = ref(`${VITE_PUBLIC_PATH}html/button.html`);
</script> </script>
<template> <template>
<iframe :src="url" frameborder="0" class="iframe"></iframe> <el-card>
<template #header>
<div class="card-header">
<span class="font-medium">通过iframe引入按钮页面</span>
</div>
</template>
<iframe :src="url" frameborder="0" class="iframe"></iframe>
</el-card>
</template> </template>
<style scoped> <style scoped>
.iframe { .iframe {
width: 98%; width: 100%;
height: 90vh; height: 60vh;
border-radius: 6px;
} }
</style> </style>

View File

@ -5,7 +5,12 @@ import menuDynamic from "./menuDynamic.vue";
</script> </script>
<template> <template>
<div> <el-card>
<template #header>
<div class="card-header">
<span class="font-medium">右键菜单组件</span>
</div>
</template>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :xs="24" :sm="10" :md="10" :lg="8" :xl="10"> <el-col :xs="24" :sm="10" :md="10" :lg="8" :xl="10">
<!-- 基本使用 --> <!-- 基本使用 -->
@ -20,5 +25,5 @@ import menuDynamic from "./menuDynamic.vue";
<menuDynamic /> <menuDynamic />
</el-col> </el-col>
</el-row> </el-row>
</div> </el-card>
</template> </template>

View File

@ -3,35 +3,32 @@ import { ReNormalCountTo, ReboundCountTo } from "/@/components/ReCountTo";
</script> </script>
<template> <template>
<div> <el-card>
<el-row :gutter="24"> <template #header>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12"> <div class="card-header">
<el-card shadow="always"> <span class="font-medium">数字动画组件</span>
<ReNormalCountTo </div>
prefix="$" </template>
:duration="1000" <div class="flex items-center">
:color="'#409EFF'" <ReNormalCountTo
:fontSize="'2.3em'" prefix="$"
:startVal="1" :duration="1000"
:endVal="1000" :color="'#409EFF'"
/> :fontSize="'2em'"
</el-card> :startVal="1"
</el-col> :endVal="1000"
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12"> />
<el-card shadow="always"> <ul class="flex ml-8">
<ul class="flex"> <ReboundCountTo
<ReboundCountTo v-for="(num, inx) of [1, 6, 6, 6]"
v-for="(num, inx) of [1, 6, 6, 6]" :key="inx"
:key="inx" :i="num"
:i="num" :blur="inx"
:blur="inx" :delay="inx + 1"
:delay="inx + 1" />
/> </ul>
</ul> </div>
</el-card> </el-card>
</el-col>
</el-row>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -3,9 +3,9 @@ import { ref, nextTick, getCurrentInstance } from "vue";
import Cropper from "/@/components/ReCropper"; import Cropper from "/@/components/ReCropper";
import img from "./picture.jpeg"; import img from "./picture.jpeg";
const instance = getCurrentInstance();
let info = ref<object>(null); let info = ref<object>(null);
let cropperImg = ref<string>(""); let cropperImg = ref<string>("");
const instance = getCurrentInstance();
const onCropper = (): void => { const onCropper = (): void => {
nextTick(() => { nextTick(() => {
@ -25,14 +25,19 @@ const onCropper = (): void => {
</script> </script>
<template> <template>
<div> <el-card>
<template #header>
<div class="card-header">
<span class="font-medium">图片裁剪组件</span>
</div>
</template>
<div class="cropper-container"> <div class="cropper-container">
<Cropper ref="refCropper" :width="'40vw'" :src="img" /> <Cropper ref="refCropper" :width="'40vw'" :src="img" />
<img :src="cropperImg" class="croppered" v-if="cropperImg" /> <img :src="cropperImg" class="croppered" v-if="cropperImg" />
</div> </div>
<el-button type="primary" @click="onCropper">裁剪</el-button> <el-button type="primary" @click="onCropper">裁剪</el-button>
<p v-if="cropperImg">裁剪后图片信息{{ info }}</p> <p v-if="cropperImg">裁剪后图片信息{{ info }}</p>
</div> </el-card>
</template> </template>
<style scoped> <style scoped>

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import draggable from "vuedraggable/src/vuedraggable"; import draggable from "vuedraggable/src/vuedraggable";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
let gridLists = ref<Array<Object>>([ let gridLists = ref<Array<Object>>([
{ grid: "cn", num: 1 }, { grid: "cn", num: 1 },
@ -47,77 +48,92 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="drag-container"> <el-card>
<!-- grid列表拖拽 --> <template #header>
<el-row :gutter="25"> <div class="card-header">
<el-col :xs="25" :sm="8" :md="8" :lg="8"> <span
<el-card> >拖拽组件采用开源的<el-link
<template #header> href="https://sortablejs.github.io/vue.draggable.next/#/simple"
<div class="card-header"> target="_blank"
<span>grid列表拖拽</span> :icon="useRenderIcon('rank')"
</div> style="font-size: 16px; margin: 0 4px 5px"
</template> >vuedraggable</el-link
<draggable ></span
v-model="gridLists" >
class="grid-container" </div>
item-key="grid" </template>
animation="300" <div class="drag-container">
chosenClass="chosen" <!-- grid列表拖拽 -->
forceFallback="true" <el-row :gutter="25">
> <el-col :xs="25" :sm="8" :md="8" :lg="8">
<template #item="{ element }"> <el-card>
<div :class="'item' + ' ' + 'item-' + element.num"> <template #header>
{{ element.num }} <div class="card-header">
<span>grid列表拖拽</span>
</div> </div>
</template> </template>
</draggable> <draggable
</el-card> v-model="gridLists"
</el-col> class="grid-container"
item-key="grid"
<el-col :xs="25" :sm="8" :md="8" :lg="8"> animation="300"
<el-card> chosenClass="chosen"
<template #header> forceFallback="true"
<div class="card-header">
<span>单列拖拽</span>
</div>
</template>
<!-- 单列拖拽 -->
<draggable
v-model="lists"
item-key="name"
@change="change"
chosen-class="chosen"
force-fallback="true"
animation="300"
>
<template #item="{ element, index }">
<div class="item-single">{{ element.name }} {{ index }}</div>
</template>
</draggable>
</el-card>
</el-col>
<el-col :xs="25" :sm="8" :md="8" :lg="8">
<el-card>
<template #header>
<div class="card-header">
<span>拖拽实现元素位置切换</span>
</div>
</template>
<!-- 拖拽实现元素位置切换 -->
<div class="cut-container">
<div
class="item-cut"
v-for="(item, index) in cutLists"
:key="index"
> >
<p>{{ item.name }}</p> <template #item="{ element }">
<div :class="'item' + ' ' + 'item-' + element.num">
{{ element.num }}
</div>
</template>
</draggable>
</el-card>
</el-col>
<el-col :xs="25" :sm="8" :md="8" :lg="8">
<el-card>
<template #header>
<div class="card-header">
<span>单列拖拽</span>
</div>
</template>
<!-- 单列拖拽 -->
<draggable
v-model="lists"
item-key="name"
@change="change"
chosen-class="chosen"
force-fallback="true"
animation="300"
>
<template #item="{ element, index }">
<div class="item-single">{{ element.name }} {{ index }}</div>
</template>
</draggable>
</el-card>
</el-col>
<el-col :xs="25" :sm="8" :md="8" :lg="8">
<el-card>
<template #header>
<div class="card-header">
<span>拖拽实现元素位置切换</span>
</div>
</template>
<!-- 拖拽实现元素位置切换 -->
<div class="cut-container">
<div
class="item-cut"
v-for="(item, index) in cutLists"
:key="index"
>
<p>{{ item.name }}</p>
</div>
</div> </div>
</div> </el-card>
</el-card> </el-col>
</el-col> </el-row>
</el-row> </div>
</div> </el-card>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -16,27 +16,34 @@ const settingTB: ContextProps = reactive({
</script> </script>
<template> <template>
<div class="split-pane"> <el-card>
<splitpane :splitSet="settingLR"> <template #header>
<!-- #paneL 表示指定该组件为左侧面板 --> <div class="card-header">
<template #paneL> <span class="font-medium">切割面板组件</span>
<!-- 自定义左侧面板的内容 --> </div>
<div class="dv-a">A</div> </template>
</template> <div class="split-pane">
<!-- #paneR 表示指定该组件为右侧面板 --> <splitpane :splitSet="settingLR">
<template #paneR> <!-- #paneL 表示指定该组件为左侧面板 -->
<!-- 再次将右侧面板进行拆分 --> <template #paneL>
<splitpane :splitSet="settingTB"> <!-- 自定义左侧面板的内容 -->
<template #paneL> <div class="dv-a">A</div>
<div class="dv-b">B</div> </template>
</template> <!-- #paneR 表示指定该组件为右侧面板 -->
<template #paneR> <template #paneR>
<div class="dv-c">C</div> <!-- 再次将右侧面板进行拆分 -->
</template> <splitpane :splitSet="settingTB">
</splitpane> <template #paneL>
</template> <div class="dv-b">B</div>
</splitpane> </template>
</div> <template #paneR>
<div class="dv-c">C</div>
</template>
</splitpane>
</template>
</splitpane>
</div>
</el-card>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -2,13 +2,16 @@
import { onMounted } from "vue"; import { onMounted } from "vue";
import Player from "xgplayer/dist/simple_player"; import Player from "xgplayer/dist/simple_player";
import volume from "xgplayer/es/controls/volume"; import volume from "xgplayer/es/controls/volume";
import playbackRate from "xgplayer/es/controls/playbackRate";
import screenShot from "xgplayer/es/controls/screenShot"; import screenShot from "xgplayer/es/controls/screenShot";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import playbackRate from "xgplayer/es/controls/playbackRate";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
onMounted(() => { onMounted(() => {
new Player({ new Player({
id: "mse", id: "mse",
//
volume: 0,
autoplay: false, autoplay: false,
screenShot: true, screenShot: true,
url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4", url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4",
@ -23,7 +26,22 @@ onMounted(() => {
</script> </script>
<template> <template>
<div id="mse"></div> <el-card>
<template #header>
<div class="card-header">
<span class="font-medium"
>视频组件采用开源的<el-link
href="https://v2.h5player.bytedance.com"
target="_blank"
:icon="useRenderIcon('video-play')"
style="font-size: 16px; margin: 0 4px 5px"
>西瓜播放器</el-link
></span
>
</div>
</template>
<div id="mse"></div>
</el-card>
</template> </template>
<style scoped> <style scoped>

View File

@ -5,12 +5,12 @@ export default {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, unref } from "vue";
import WangEditor from "wangeditor"; import WangEditor from "wangeditor";
import { onMounted, onBeforeUnmount, ref, unref } from "vue";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
// eslint-disable-next-line no-undef
const editor = ref(null);
const html = ref(null); const html = ref(null);
const editor = ref(null);
let instance: WangEditor; let instance: WangEditor;
onMounted(() => { onMounted(() => {
@ -29,17 +29,26 @@ onBeforeUnmount(() => {
</script> </script>
<template> <template>
<div> <el-card>
<template #header>
<div class="card-header">
<span class="font-medium"
>编辑器组件采用开源的<el-link
href="https://www.wangeditor.com"
target="_blank"
:icon="useRenderIcon('edit')"
style="font-size: 16px; margin: 0 4px 5px"
>wangeditor</el-link
></span
>
</div>
</template>
<div ref="editor"></div> <div ref="editor"></div>
<div :innerHTML="html"></div> <div :innerHTML="html"></div>
</div> </el-card>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.main-content {
margin: 40px;
}
:deep(.w-e-text-container) { :deep(.w-e-text-container) {
z-index: 99 !important; z-index: 99 !important;
} }

View File

@ -1,13 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, unref, onMounted } from "vue"; import demoData from "./dataTurbo.json";
import LogicFlow from "@logicflow/core";
import { Snapshot, BpmnElement, Menu } from "@logicflow/extension";
import "@logicflow/core/dist/style/index.css"; import "@logicflow/core/dist/style/index.css";
import "@logicflow/extension/lib/style/index.css"; import "@logicflow/extension/lib/style/index.css";
import LogicFlow from "@logicflow/core";
import { ref, unref, onMounted } from "vue";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
import { BpmnNode } from "/@/components/ReFlowChart/src/config";
import { Snapshot, BpmnElement, Menu } from "@logicflow/extension";
import { Control, NodePanel, DataDialog } from "/@/components/ReFlowChart"; import { Control, NodePanel, DataDialog } from "/@/components/ReFlowChart";
import { toLogicflowData } from "/@/components/ReFlowChart/src/adpterForTurbo"; import { toLogicflowData } from "/@/components/ReFlowChart/src/adpterForTurbo";
import { BpmnNode } from "/@/components/ReFlowChart/src/config";
import demoData from "./dataTurbo.json";
let lf = ref(null); let lf = ref(null);
let graphData = ref(null); let graphData = ref(null);
@ -57,37 +59,52 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="logic-flow-view"> <el-card>
<!-- 辅助工具栏 --> <template #header>
<Control <div class="card-header">
class="demo-control" <span class="font-medium"
v-if="lf" >流程图组件采用开源的<el-link
:lf="lf" href="http://logic-flow.org/"
:catTurboData="false" target="_blank"
@catData="catData" :icon="useRenderIcon('set-up')"
></Control> style="font-size: 16px; margin: 0 4px 5px"
<!-- 节点面板 --> >LogicFlow</el-link
<NodePanel :lf="lf" :nodeList="nodeList"></NodePanel> ></span
<!-- 画布 --> >
<div id="LF-Turbo"></div> </div>
<!-- 数据查看面板 --> </template>
<el-dialog <div class="logic-flow-view">
customClass="flow-dialog" <!-- 辅助工具栏 -->
title="数据" <Control
v-model="dataVisible" class="demo-control"
width="50%" v-if="lf"
> :lf="lf"
<el-scrollbar> :catTurboData="false"
<DataDialog :graphData="graphData"></DataDialog> @catData="catData"
</el-scrollbar> ></Control>
</el-dialog> <!-- 节点面板 -->
</div> <NodePanel :lf="lf" :nodeList="nodeList"></NodePanel>
<!-- 画布 -->
<div id="LF-Turbo"></div>
<!-- 数据查看面板 -->
<el-dialog
customClass="flow-dialog"
title="数据"
v-model="dataVisible"
width="50%"
>
<el-scrollbar>
<DataDialog :graphData="graphData"></DataDialog>
</el-scrollbar>
</el-dialog>
</div>
</el-card>
</template> </template>
<style scoped> <style scoped>
#LF-Turbo { #LF-Turbo {
width: 100%; width: 100%;
height: calc(100vh - 90px); height: 70vh;
} }
.logic-flow-view { .logic-flow-view {
@ -137,8 +154,4 @@ onMounted(() => {
height: 85vh; height: 85vh;
overflow: auto; overflow: auto;
} }
.main-content {
margin: 0;
}
</style> </style>

View File

@ -1,14 +1,7 @@
<script lang="ts">
export default {
name: "reGuide"
};
</script>
<script setup lang="ts"> <script setup lang="ts">
import Driver from "driver.js"; import Driver from "driver.js";
import "driver.js/dist/driver.min.css"; import "driver.js/dist/driver.min.css";
//
const steps = [ const steps = [
{ {
element: "#header-notice", element: "#header-notice",
@ -72,8 +65,14 @@ const guide = () => {
</script> </script>
<template> <template>
<div> <el-card>
<p>引导页对于一些第一次进入项目的人很有用你可以简单介绍下项目的功能</p> <template #header>
<div class="card-header">
<span class="font-medium"
>引导页对于一些第一次进入项目的人很有用你可以简单介绍下项目的功能</span
>
</div>
</template>
<el-button <el-button
type="primary" type="primary"
style="margin-top: 10px" style="margin-top: 10px"
@ -81,7 +80,7 @@ const guide = () => {
> >
打开引导页 打开引导页
</el-button> </el-button>
</div> </el-card>
</template> </template>
<style> <style>

View File

@ -20,12 +20,16 @@ function changRole(value) {
</script> </script>
<template> <template>
<div> <el-card>
<el-radio-group v-model="auth" @change="changRole"> <template #header>
<el-radio-button label="admin"></el-radio-button> <div class="card-header">
<el-radio-button label="test"></el-radio-button> <el-radio-group v-model="auth" @change="changRole">
</el-radio-group> <el-radio-button label="admin"></el-radio-button>
<el-radio-button label="test"></el-radio-button>
</el-radio-group>
</div>
</template>
<p v-auth="'v-admin'">只有admin可看</p> <p v-auth="'v-admin'">只有admin可看</p>
<p v-auth="'v-test'">只有test可看</p> <p v-auth="'v-test'">只有test可看</p>
</div> </el-card>
</template> </template>

View File

@ -29,19 +29,23 @@ function changRole() {
</script> </script>
<template> <template>
<div> <el-card>
<h4> <template #header>
当前角色 <div class="card-header">
<span style="font-size: 26px">{{ purview }}</span> <span>
<p style="color: #ffa500"> 当前角色
查看左侧菜单变化(系统管理)模拟后台根据不同角色返回对应路由 <span style="font-size: 26px">{{ purview }}</span>
</p> <p style="color: #ffa500">
</h4> 查看左侧菜单变化(系统管理)模拟后台根据不同角色返回对应路由
</p></span
>
</div>
</template>
<el-button <el-button
type="primary" type="primary"
@click="changRole" @click="changRole"
:icon="useRenderIcon('user', { color: '#fff' })" :icon="useRenderIcon('user', { color: '#fff' })"
>切换角色</el-button >切换角色</el-button
> >
</div> </el-card>
</template> </template>

View File

@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const activeName = ref("tag");
function toDetail(index: number) { function toDetail(index: number) {
useMultiTagsStoreHook().handleTags("push", { useMultiTagsStoreHook().handleTags("push", {
@ -25,33 +23,16 @@ function toDetail(index: number) {
</script> </script>
<template> <template>
<el-collapse v-model="activeName" class="tabs-container"> <el-card>
<el-collapse-item <template #header>
title="标签页复用超出限制自动关闭(使用场景: 动态路由)" <div class="card-header">
name="tag" <span class="font-medium"
> >标签页复用超出限制自动关闭使用场景: 动态路由</span
<el-button v-for="index in 6" :key="index" @click="toDetail(index)"> >
打开{{ index }}详情页 </div>
</el-button> </template>
</el-collapse-item> <el-button v-for="index in 6" :key="index" @click="toDetail(index)">
</el-collapse> 打开{{ index }}详情页
</el-button>
</el-card>
</template> </template>
<style lang="scss" scoped>
.tabs-container {
padding: 10px;
background: #fff;
::v-deep(.el-collapse-item__header) {
line-height: 20px;
}
::v-deep(.el-collapse-item__wrap) {
border-bottom: none;
}
button {
margin: 10px;
}
}
</style>

View File

@ -61,7 +61,7 @@ const openDepot = (): void => {
} }
}" }"
> >
<el-card> <el-card style="height: 360px">
<template #header> <template #header>
<span style="font-size: 16px; font-weight: 500">GitHub信息</span> <span style="font-size: 16px; font-weight: 500">GitHub信息</span>
</template> </template>
@ -93,7 +93,7 @@ const openDepot = (): void => {
} }
}" }"
> >
<el-card> <el-card style="height: 360px">
<template #header> <template #header>
<span style="font-size: 16px; font-weight: 500" <span style="font-size: 16px; font-weight: 500"
>GitHub滚动信息</span >GitHub滚动信息</span