Conflicts:
	src/layout/TagsView/components/MoreButton.vue
	src/views/home/index.vue
This commit is contained in:
zouzhibing 2022-11-03 23:11:44 +08:00
commit cd3688d055
54 changed files with 2228 additions and 1427 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,269 @@
<template>
<div ref="chartsRef" class="echarts"/>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts'
import { EChartsType } from 'echarts/core'
import { onMounted, ref } from 'vue'
const chartsRef = ref<HTMLElement | null>()
let colorList = ['#46ea91', '#2ba0ff', '#ed593b', '#7357ff', '#f2d750'];
const options = {
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 5,
textStyle: {
color: '#fff',
padding: [3, 0, 0, 0],
},
},
tooltip: {
trigger: 'axis',
},
grid: {
top: '14%',
left: '3%',
right: '4%',
bottom: '10%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: ['1', '2', '3', '4', '5', '6', '7', '8'],
axisLine: {
lineStyle: {
color: '#33BBFF',
},
},
axisTick: {
show: false,
},
axisLabel: {
interval: 0,
textStyle: {
color: '#5FBBEB',
},
// x
fontSize: 12,
// margin:x
margin: 10,
},
axisPointer: {
label: {
// padding: [11, 5, 7],
padding: [0, 0, 0, 0],
// marginaxisLabelmargin!
margin: 10,
//
fontSize: 12,
backgroundColor: 'rgba(0,0,0,0)',
},
},
boundaryGap: false,
},
],
yAxis: [
{
name: '单位/件',
axisTick: {
show: false,
},
axisLine: {
show: true,
lineStyle: {
color: '#05D5FF',
},
},
axisLabel: {
textStyle: {
color: '#5FBBEB',
},
},
splitLine: {
show: false,
},
},
],
series: [
{
name: '咨询',
type: 'line',
data: [100, 20, 30, 102, 15, 30, 20, 18],
symbolSize: 1,
symbol: 'circle',
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
{
offset: 0,
color: '#90ffc6',
},
{
offset: 1,
color: '#46ea91',
},
]),
shadowColor: 'rgba(144, 255, 198, .3)',
shadowBlur: 5,
shadowOffsetY: 5,
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0],
},
},
},
{
name: '求助',
type: 'line',
data: [20, 12, 11, 14, 25, 16, 10, 20],
symbolSize: 1,
symbol: 'circle',
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
{
offset: 0,
color: '#67bcfc',
},
{
offset: 1,
color: '#2ba0ff',
},
]),
shadowColor: 'rgba(105, 188, 252,.3)',
shadowBlur: 5,
shadowOffsetY: 5,
},
itemStyle: {
normal: {
color: colorList[1],
borderColor: colorList[1],
},
},
},
{
name: '无效',
type: 'line',
data: [150, 120, 170, 140, 100, 160, 110, 110],
symbolSize: 1,
symbol: 'circle',
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
{
offset: 0,
color: '#fc937e ',
},
{
offset: 1,
color: '#ed593b',
},
]),
shadowColor: 'rgb(252, 147, 126,.3)',
shadowBlur: 2,
shadowOffsetY: 2,
},
itemStyle: {
normal: {
color: colorList[2],
borderColor: colorList[2],
},
},
},
{
name: '投诉举报',
type: 'line',
data: [200, 80, 100, 30, 60, 50, 110, 20],
symbolSize: 1,
symbol: 'circle',
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
{
offset: 0,
color: '#a390ff',
},
{
offset: 1,
color: '#7357ff',
},
]),
shadowColor: 'rgba(115, 87, 255, .1)',
shadowBlur: 5,
shadowOffsetY: 5,
},
itemStyle: {
normal: {
color: colorList[3],
borderColor: colorList[3],
},
},
},
{
name: '建议',
type: 'line',
data: [20, 80, 150, 30, 60, 50, 50, 20],
symbolSize: 1,
symbol: 'circle',
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
{
offset: 0,
color: '#ffeb86',
},
{
offset: 1,
color: '#f2d750',
},
]),
shadowColor: 'rgba(255, 235, 134, .5)',
shadowBlur: 5,
shadowOffsetY: 5,
},
itemStyle: {
normal: {
color: colorList[4],
borderColor: colorList[4],
},
},
},
],
};
let chart: EChartsType
const initChart = () => {
const chart = echarts.init(chartsRef.value)
chart.setOption(options)
return chart
}
onMounted(() => {
chart = initChart()
window.addEventListener('resize', function () {
chart && chart.resize()
})
})
</script>
<style lang="scss" scoped>
.echarts{
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,179 @@
<template>
<div ref="chartsRef" class="echarts"/>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts'
import { EChartsType } from 'echarts/core'
import { onMounted, ref } from 'vue'
const chartsRef = ref<HTMLElement | null>()
var trafficWay = [
{
name: 'Ⅰ类',
value: 20,
},
{
name: 'Ⅱ类',
value: 20,
},
{
name: 'Ⅲ类',
value: 20,
},
{
name: 'Ⅳ类',
value: 20,
},
{ name: 'Ⅴ类', value: 20 },
{ name: '劣Ⅴ类', value: 20 },
];
var data = [];
var color = ['#fd566a', '#9787ff', '#fdb36a', '#fdd56a', '#6da7ff', '#63e1f2', '#ff3000'];
for (var i = 0; i < trafficWay.length; i++) {
data.push(
{
value: trafficWay[i].value,
name: trafficWay[i].name,
itemStyle: {
normal: {
borderWidth: 5,
shadowBlur: 20,
borderColor: color[i],
shadowColor: color[i],
},
},
},
{
value: 2,
name: '',
itemStyle: {
normal: {
label: {
show: false,
},
labelLine: {
show: false,
},
color: 'rgba(0, 0, 0, 0)',
borderColor: 'rgba(0, 0, 0, 0)',
borderWidth: 0,
},
},
}
);
}
var seriesOption = [
{
name: '',
type: 'pie',
clockWise: false,
radius: [105, 109],
hoverAnimation: false,
itemStyle: {
normal: {
label: {
show: true,
position: 'outside',
formatter: function (params) {
var percent = 0;
var total = 0;
for (var i = 0; i < trafficWay.length; i++) {
total += trafficWay[i].value;
}
percent = ((params.value / total) * 100).toFixed(0);
if (params.name !== '') {
return params.name + '\t' + percent + '%';
} else {
return '';
}
},
},
labelLine: {
length: 10,
length2: 20,
show: true,
color: '#00ffff',
},
},
},
data: data,
},
];
let options = {
color: color,
title: [{
text: '水质监测',
top: '44%',
textAlign: 'center',
left: '49.50%',
backgroundColor: '#163253',
borderRadius: 100,
textStyle: {
color: '#fff',
fontSize: 20,
fontWeight: '400',
},
}, {
text: '水环境监测站',
top: '49%',
textAlign: 'center',
left: '49.50%',
textStyle: {
color: '#fff',
fontSize: 20,
fontWeight: '400',
},
}, {
text: '9',
top: '53%',
textAlign: 'center',
left: '48%',
textStyle: {
color: '#f6ea2f',
fontSize: 25,
fontWeight: '800',
fontStyle: 'italic'
},
}, {
text: '个',
top: '53.5%',
textAlign: 'center',
left: '50.5%',
textStyle: {
color: '#fff',
fontSize: 16,
fontWeight: '400',
},
}],
tooltip: {
show: false,
},
toolbox: {
show: false,
},
series: seriesOption,
};
let chart: EChartsType
const initChart = () => {
const chart = echarts.init(chartsRef.value)
chart.setOption(options)
return chart
}
onMounted(() => {
chart = initChart()
window.addEventListener('resize', function () {
chart && chart.resize()
})
})
</script>
<style lang="scss" scoped>
.echarts{
width: 100%;
height: 100%;
}
</style>

View File

@ -30,7 +30,6 @@
/>
</el-form-item>
</template>
</el-col>
</el-row>
</el-form>
@ -117,4 +116,4 @@ const resetForm = (formEl: FormInstance | undefined) => {
overflow: hidden;
}
}
</style>
</style>

View File

@ -74,7 +74,7 @@
<template #default="scope">
<el-button
v-if="scope.row.edit"
type="success"
type="primary"
size="small"
icon="CircleCheckFilled"
@click="confirmEdit(scope.row)"

View File

@ -1,14 +1,18 @@
<template>
<div class="zb-pro-table">
<div class="header">
<el-form :inline="true" :model="formInline" class="demo-form-inline search-form" ref="ruleFormRef" >
<el-form :inline="true"
class="search-form"
:model="formInline" ref="ruleFormRef" >
<template v-for="(item, index) in formSearchData" :key="index">
<el-form-item :label="item.label" v-show="isExpand ? isExpand : index < 2">
<template v-if="item.valueType === 'input'">
<el-input v-model="formInline[item.name]" :placeholder="`请输入${item.label}`" />
</template>
<template v-if="item.valueType === 'select'">
<el-select v-model="formInline[item.name]" :placeholder="`请选择${item.label}`">
<el-select
style="width: 100%"
v-model="formInline[item.name]" :placeholder="`请选择${item.label}`">
<el-option
v-for="ite in item.options"
:key="ite.value"
@ -192,7 +196,18 @@ const deleteAction = (row) => {
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
.search-form{
flex: 1;
::v-deep{
.el-input--default{
width: 200px;
}
}
}
.search{
flex-shrink: 0;
white-space: nowrap;
}
}
.footer{
flex: 1;
@ -203,6 +218,7 @@ const deleteAction = (row) => {
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
min-height: 300px;
.operator{
margin-bottom: 15px
}
@ -215,13 +231,6 @@ const deleteAction = (row) => {
height: 100%;
}
}
.search-form{
flex: 1;
}
.search{
white-space: nowrap;
}
::v-deep{
.el-table__header th{
font-size: 15px;

View File

@ -0,0 +1,19 @@
<template>
<div class="footer ">
<span href="/" target="_blank"> 2022 © VUE-ADMIN-PERFECT By ZB Technology. </span>
</div>
</template>
<style scoped lang="scss">
.footer{
height: 40px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
background: #ffffff;
border-top: 1px solid #e4e7ed;
flex-shrink: 0;
color: rgba(0, 0, 0, 0.45);
}
</style>

View File

@ -1,18 +1,22 @@
<template>
<div class="app-main" v-if="isReload">
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive :include="cacheRoutes">
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
</router-view>
<div class="app-main-inner">
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive :include="cacheRoutes">
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</div>
<Theme />
<Footer/>
</div>
</template>
<script lang="ts" setup>
import Theme from '@/components/Theme/index.vue'
import Footer from '../Footer/index.vue'
import { computed, ref } from 'vue'
import {useSettingStore} from "@/store/modules/setting"
import {usePermissionStore} from "@/store/modules/permission"
@ -32,5 +36,13 @@
flex-direction: column;
width: 100%;
box-sizing: border-box;
.app-main-inner{
flex: 1;
display: flex;
overflow-x: hidden;
flex-direction: column;
width: 100%;
box-sizing: border-box;
}
}
</style>

View File

@ -6,10 +6,10 @@
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="refresh">{{ $t("tagsView.refresh") }}</el-dropdown-item>
<el-dropdown-item @click="closeCurrentTab">{{ $t("tagsView.closeCurrent") }}</el-dropdown-item>
<el-dropdown-item @click="closeOtherTab">{{ $t("tagsView.closeOther") }}</el-dropdown-item>
<el-dropdown-item @click="closeAllTab">{{ $t("tagsView.closeAll") }}</el-dropdown-item>
<el-dropdown-item @click="refresh"><el-icon :size="14"><Refresh /></el-icon> {{ $t("tagsView.refresh") }}</el-dropdown-item>
<el-dropdown-item @click="closeCurrentTab"><el-icon :size="14"><FolderRemove/></el-icon> {{ $t("tagsView.closeCurrent") }}</el-dropdown-item>
<el-dropdown-item @click="closeOtherTab"><el-icon :size="14"><Close /></el-icon>{{ $t("tagsView.closeOther") }}</el-dropdown-item>
<el-dropdown-item @click="closeAllTab"><el-icon :size="14"><FolderDelete /></el-icon>{{ $t("tagsView.closeAll") }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -18,7 +18,9 @@
import {computed} from "vue";
import {useSettingStore} from "@/store/modules/setting"
import {useTagsViewStore} from "@/store/modules/tagsView"
import { useRouter,useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const SettingStore = useSettingStore()
const TagsViewStore = useTagsViewStore()
const visitedViews = computed(() => TagsViewStore.visitedViews)
@ -27,24 +29,19 @@ const refresh = () => {
}
//
const closeCurrentTab = (event)=>{
closeSelectedTag(event,route)
TagsViewStore.toLastView(route.path)
TagsViewStore.delView(route.path)
}
//
const closeOtherTab= async ()=>{
const { name } = route
for(let item of visitedViews.value){
if(item.name!==name){
await closeSelectedTag(null,item)
}
}
TagsViewStore.delOtherViews(route.path)
}
//
const closeAllTab = async ()=>{
let visitedViews = await TagsViewStore.delAllViews()
await TagsViewStore.goHome()
await TagsViewStore.delAllViews()
TagsViewStore.goHome()
}
</script>
<style lang="scss" scoped>

View File

@ -109,7 +109,6 @@ function toLastView(activeTabPath) {
router.push(nextTab.path);
TagsViewStore.addVisitedView(nextTab)
}
// Tab Click
const tabClick = (tabItem: TabsPaneContext) => {
let path = tabItem.props.name as string;
router.push(path);

View File

@ -11,7 +11,7 @@
<div :style="{ height:`${showTag?90:50}px` }" v-if="SettingStore.themeConfig.fixedHeader"></div>
<u-header />
<div class="m-container-content" :class="{ 'app-main-hide-tag': !showTag }">
<u-main />
<Main />
</div>
</div>
</div>
@ -22,7 +22,7 @@
import {useSettingStore} from "@/store/modules/setting"
import Sidebar from './Sidebar/index.vue'
import UHeader from './Header/index.vue'
import UMain from './Main/index.vue'
import Main from './Main/index.vue'
import { useResizeHandler } from './hooks/useResizeHandler'

237
src/mock/system.ts Normal file
View File

@ -0,0 +1,237 @@
export const userData =[
{
username:'zzb',
nickname:'林峰',
sex:'男',
role:'超级管理员',
status:true,
},
{
username:'zhangsan',
nickname:'张三',
sex:'女',
role:'管理员',
status:true,
},
{
username:'lisi',
nickname:'李四',
sex:'男',
role:'管理员',
status:true,
},
{
username:'wangwu',
nickname:'王五',
sex:'男',
role:'超级管理员',
status:false,
},
{
username:'zhaoliu',
nickname:'赵柳',
sex:'男',
role:'普通用户',
status:false,
},
]
export const roleData =[
{
roleName:'超级管理员',
roleId:'admin',
createTime:'2022-09-02',
},
{
roleName:'管理员',
roleId:'role',
createTime:'2022-09-02',
},
{
roleName:'普通用户',
roleId:'other',
createTime:'2022-09-02',
},
]
export const menuData =[
{
menuName:'首页',
menuType:'菜单',
menuRouter:'/home',
identification:'menu:home',
parentId:0,
level:1,
id:0,
createTime:'2022-09-02',
},
{
menuName:'表格',
menuType:'目录',
menuRouter:'/table',
identification:'menu:table',
parentId:0,
level:1,
id:1,
createTime:'2022-09-02',
children:[
{
menuName:'菜单1',
menuType:'菜单',
menuRouter:'/table',
identification:'menu1:view',
parentId:1,
level:2,
id:10,
createTime:'2022-09-02',
children:[
{
menuName:'按钮1',
menuType:'按钮',
menuRouter:'/table',
identification:'menu1:view:btn1',
parentId:10,
id:20,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮2',
menuType:'按钮',
menuRouter:'/table',
identification:'menu1:view:btn2',
parentId:10,
id:21,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮3',
menuType:'按钮',
menuRouter:'/table',
identification:'menu1:view:btn2',
parentId:10,
id:22,
level:3,
createTime:'2022-09-02',
}
]
}
]
},
{
menuName:'可视化图表',
menuType:'目录',
menuRouter:'/charts',
identification:'menu:charts',
parentId:0,
level:1,
id:1,
createTime:'2022-09-02',
children:[
{
menuName:'菜单1',
menuType:'菜单',
menuRouter:'/charts',
identification:'menu1:view',
parentId:1,
level:2,
id:10,
createTime:'2022-09-02',
children:[
{
menuName:'按钮1',
menuType:'按钮',
menuRouter:'/charts',
identification:'menu1:view:btn1',
parentId:10,
id:20,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮2',
menuType:'按钮',
menuRouter:'/charts',
identification:'menu1:view:btn2',
parentId:10,
id:21,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮3',
menuType:'按钮',
menuRouter:'/charts',
identification:'menu1:view:btn2',
parentId:10,
id:22,
level:3,
createTime:'2022-09-02',
}
]
}
]
},
{
menuName:'基础组件',
menuType:'目录',
menuRouter:'/components',
identification:'menu:components',
parentId:0,
level:1,
id:1,
createTime:'2022-09-02',
children:[
{
menuName:'菜单1',
menuType:'菜单',
menuRouter:'/components',
identification:'menu1:view',
parentId:1,
level:2,
id:10,
createTime:'2022-09-02',
children:[
{
menuName:'按钮1',
menuType:'按钮',
menuRouter:'/components',
identification:'menu1:view:btn1',
parentId:10,
id:20,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮2',
menuType:'按钮',
menuRouter:'/components',
identification:'menu1:view:btn2',
parentId:10,
id:21,
level:3,
createTime:'2022-09-02',
},
{
menuName:'按钮3',
menuType:'按钮',
menuRouter:'/components',
identification:'menu1:view:btn2',
parentId:10,
id:22,
level:3,
createTime:'2022-09-02',
}
]
}
]
},
]

132
src/mock/table.ts Normal file
View File

@ -0,0 +1,132 @@
export const tableList = [
{
date: '2016-05-02',
name: '王五',
price: 20,
province: '上海',
admin: 'admin',
sex: 1,
id: 1,
age: 18,
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333,
},
{
date: '2018-06-11',
name: '梦琪',
price: 20,
province: '上海',
admin: 'admin',
sex: 1,
id: 2,
age: 22,
city: '普陀区',
address: '上海市普陀区金沙江路 1519 弄',
zip: 200333,
},
{
date: '2022-05-23',
name: '忆柳',
price: 22,
province: '上海',
admin: 'admin',
sex: 0,
id: 3,
age: 23,
city: '普陀区',
address: '上海市普陀区金沙江路 1520 弄',
zip: 200333,
},
{
date: '2022-01-24',
name: '之桃',
price: 33,
province: '上海',
admin: 'admin',
sex: 1,
id: 4,
age: 24,
city: '普陀区',
address: '上海市普陀区金沙江路 2222 弄',
zip: 200333,
},
{
date: '2022-07-22',
name: '慕青',
price: 45,
province: '上海',
admin: 'admin',
sex: 0,
id: 5,
age: 25,
city: '普陀区',
address: '上海市普陀区金沙江路 2223 弄',
zip: 200333,
},
{
date: '2016-05-02',
name: '问兰',
price: 47,
province: '上海',
admin: 'admin',
sex: 1,
id: 6,
age: 26,
city: '普陀区',
address: '上海市普陀区金沙江路 2224 弄',
zip: 200333,
},
{
date: '2016-08-02',
name: '元香',
price: 45,
province: '上海',
admin: 'admin',
sex: 0,
id: 7,
age: 27,
city: '普陀区',
address: '上海市普陀区金沙江路 2225 弄',
zip: 200333,
},
{
date: '2019-11-11',
name: '初夏',
price: 23,
province: '上海',
admin: 'admin',
sex: 1,
id: 8,
age: 28,
city: '普陀区',
address: '上海市普陀区金沙江路 2226 弄',
zip: 200333,
},
{
date: '2018-08-02',
name: '沛菡',
price: 33,
province: '上海',
admin: 'other',
sex: 0,
id: 9,
age: 29,
city: '普陀区',
address: '上海市普陀区金沙江路 2227 弄',
zip: 200339,
},
{
date: '2022-11-03',
name: '傲珊',
price: 222,
province: '浙江',
admin: 'admin',
sex: 1,
id: 10,
age: 30,
city: '杭州',
address: '杭州市滨江区建业路228号',
zip: 200433,
}
]

View File

@ -16,8 +16,6 @@ import chatRouter from './modules/chat'
import othersRouter from './modules/other'
import externalLink from './modules/externalLink'
import formRouter from './modules/from'
import zipRoutes from './modules/zip'
import clipboardTable from './modules/clipboard'
// 异步组件
export const asyncRoutes = [
@ -29,14 +27,12 @@ export const asyncRoutes = [
...othersRouter,
...nestedRouter,
...excelRouter,
...zipRoutes,
...errorRouter,
...externalLink,
...clipboardTable,
...systemRouter,
{
path: '/:pathMatch(.*)',
redirect: '/error/404'
redirect: '/404'
}
]

View File

@ -25,12 +25,12 @@ const chartsRouter = [{
name: 'charts-simple',
meta: { title: '简单图表', roles:['other'] , icon: 'MenuIcon'}
},
{
path: 'complex',
component: () => import('@/views/charts/complex.vue'),
name: 'charts-complex',
meta: { title: '复杂图表', roles:['other'] , icon: 'MenuIcon'}
},
// {
// path: 'complex',
// component: () => import('@/views/charts/complex.vue'),
// name: 'charts-complex',
// meta: { title: '复杂图表', roles:['other'] , icon: 'MenuIcon'}
// },
// {
// path: 'animation',
// component: () => import('@/views/charts/animation.vue'),

View File

@ -1,23 +0,0 @@
import Layout from '@/layout/index.vue'
const clipboardTable = [{
path: '/clipboard',
component: Layout,
redirect: '/clipboard/index',
name: 'clipboard',
meta: {
title: 'clipboard',
icon: 'document-copy',
roles:['other']
},
children: [
{
path: 'index',
component: () => import('@/views/clipboard/index.vue'),
name: 'map',
meta: { title: '剪贴板', roles:['other'] ,icon: 'document-copy',}
},
]
}]
export default clipboardTable

View File

@ -1,32 +1,33 @@
/** When your routing table is too long, you can split it into small modules**/
import { RouteRecordRaw } from "vue-router";
import Layout from "@/layout/index.vue";
const errorRouter = [{
path: '/error',
component: Layout,
redirect: '/error/404',
name: 'error',
meta: {
title: '错误页面',
icon: 'School'
// 扩展继承属性
interface extendRoute {
hidden?:boolean
}
const errorRouter : Array<RouteRecordRaw&extendRoute> = [
{
path: "/403",
name: "403",
component: () => import("@/views/error/403.vue"),
hidden:true,
meta: {
requiresAuth: true,
title: "403页面",
key: "403"
}
},
children: [
{
path: '404',
component: () => import('@/views/error/404.vue'),
name: '404',
meta: { title: '404', icon: 'MenuIcon' }
},
{
path: '401',
component: () => import('@/views/error/401.vue'),
name: '401',
meta: { title: '401', icon: 'MenuIcon'}
},
]
}]
{
path: "/404",
name: "404",
component: () => import("@/views/error/404.vue"),
hidden:true,
meta: {
requiresAuth: true,
title: "404页面",
key: "404"
}
}
]
export default errorRouter

View File

@ -1,7 +1,5 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from "@/layout/index.vue";
const excelRouter = [{
@ -10,33 +8,39 @@ const excelRouter = [{
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'Excel',
title: 'Excel&Zip',
icon: 'School'
},
children: [
{
path: 'export-excel',
component: () => import('@/views/excel/export-excel.vue'),
component: () => import('@/views/excel/exportExcel/index.vue'),
name: 'export-excel',
meta: { title: '导出 Excel', icon: 'MenuIcon'}
},
{
path: 'export-merge-header',
component: () => import('@/views/excel/export-merge-header.vue'),
component: () => import('@/views/excel/exportMergeHeader/index.vue'),
name: 'export-merge-header',
meta: { title: '导出 多级表头', icon: 'MenuIcon' }
},
{
path: 'upload-style-excel',
component: () => import('@/views/excel/exportStyleExcel/index.vue'),
name: 'upload-style-excel',
meta: { title: '自定义样式导出 Excel', icon: 'MenuIcon' }
},
{
path: 'upload-excel',
component: () => import('@/views/excel/upload-excel.vue'),
component: () => import('@/views/excel/uploadExcel/index.vue'),
name: 'upload-excel',
meta: { title: '上传 Excel', icon: 'MenuIcon' }
},
{
path: 'upload-style-excel',
component: () => import('@/views/excel/export-style-excel.vue'),
name: 'upload-style-excel',
meta: { title: '自定义样式导出 Excel', icon: 'MenuIcon' }
path: 'zip',
component: () => import('@/views/excel/zip/index.vue'),
name: 'Zip',
meta: { title: 'Zip', roles:['other'] ,icon: 'MenuIcon',}
},
]
}]

View File

@ -1,4 +1,4 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from "@/layout/index.vue";

View File

@ -8,10 +8,16 @@ const othersRouter = [{
redirect: '/other/editor',
name: 'other',
meta: {
title: '扩展组件',
title: '常用组件',
icon: 'management'
},
children: [
{
path: 'clipboard',
component: () => import('@/views/other/clipboard/index.vue'),
name: 'clipboard',
meta: { title: '剪贴板', roles:['other'] ,icon: 'MenuIcon',}
},
{
path: 'editor',
component: () => import('@/views/other/editor/index.vue'),

View File

@ -15,19 +15,19 @@ const tableRouter = [{
children: [
{
path: 'comprehensive',
component: () => import('@/views/table/ComprehensiveTable.vue'),
component: () => import('@/views/table/ComprehensiveTable/index.vue'),
name: 'comprehensive',
meta: { title: '综合表格', keepAlive: true , icon: 'MenuIcon'}
},
{
path: 'inline-table',
component: () => import('@/views/table/InlineEditTable.vue'),
component: () => import('@/views/table/InlineEditTable/index.vue'),
name: 'inline-table',
meta: { title: '行内编辑', keepAlive: true , icon: 'MenuIcon'}
},
{
path: 'editableProTable',
component: () => import('@/views/table/EditableProTable.vue'),
component: () => import('@/views/table/EditableProTable/index.vue'),
name: 'edit-table',
meta: { title: '可编辑表格', keepAlive: true , icon: 'MenuIcon'}
},

View File

@ -1,27 +0,0 @@
import Layout from '@/layout/index.vue'
const zipRoutes = [{
path: '/zip',
component: Layout,
isShow:true,
redirect: 'noRedirect',
name: 'zip',
alwaysShow:true,
meta: {
title: 'Zip',
icon: 'document-copy',
roles:['other']
},
children: [
{
path: 'download',
component: () => import('@/views/zip/download.vue'),
name: 'download',
meta: { title: 'Zip', roles:['other'] ,icon: 'document-copy',}
},
]
}]
export default zipRoutes

View File

@ -1,6 +1,5 @@
import {defineStore} from 'pinia'
import { useRouter } from "vue-router";
const router = useRouter()
import router from "@/router/index";
export const useTagsViewStore = defineStore({
// id: 必须的,在所有 Store 中唯一
@ -52,6 +51,13 @@ export const useTagsViewStore = defineStore({
})
},
toLastView(activeTabPath){
let index = this.visitedViews.findIndex(item=>item.path===activeTabPath)
const nextTab = this.visitedViews[index + 1] || this.visitedViews[index - 1];
if (!nextTab) return;
router.push(nextTab.path);
this.addVisitedView(nextTab)
},
delVisitedView(path){
return new Promise(resolve => {
this.visitedViews = this.visitedViews.filter(v=>{
@ -82,9 +88,17 @@ export const useTagsViewStore = defineStore({
resolve([...this.visitedViews])
})
},
async goHome() {
router.push('/home');
delOtherViews(path){
this.visitedViews = this.visitedViews.filter(item => {
return item.path === path || item.meta.affix;
});
this.cachedViews = this.visitedViews.filter(item => {
return item.path === path || item.meta.affix;
});
},
goHome() {
this.activeTabsValue = '/home';
router.push({path: '/home'});
},
updateVisitedView(view){
for (let v of this.visitedViews) {

View File

@ -55,6 +55,8 @@ body{
}
/* nprogress样式 */
#nprogress .bar {
background: $primaryColor !important;
@ -66,3 +68,19 @@ body{
#nprogress .peg {
box-shadow: 0 0 10px $primaryColor, 0 0 5px $primaryColor !important;
}
.app-container {
height: 100%;
width: 100%;
padding: 10px 12px;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.app-container-inner{
height: 100%;
width: 100%;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
background: white;
padding: 20px;
}

View File

@ -3,3 +3,7 @@
color: #252525;
background: #fafafa;
}
.el-table .el-table__header th {
background: var(--el-fill-color-light)!important;
}

View File

@ -0,0 +1,160 @@
.m-data-screen{
width: 1920px;
height: 1080px;
box-sizing: border-box;
overflow:hidden;
//background: url("/static/screen/bg.png") no-repeat center center;
background: #041c4a;
.header{
box-sizing: border-box;
width: 100%;
height: 91px;
background: url("/static/screen/header-bg.png");
background-size:contain ;
background-repeat:no-repeat;
display: flex;
align-items: center;
justify-content: center;
.header-bg-title{
font-size: 42px;
font-weight: normal;
font-stretch: normal;
letter-spacing: 2px;
color: #ffffff;
margin-bottom: 14px;
}
.date{
top: 25px;
line-height: 68px;
font-size: 18px;
position: absolute;
letter-spacing: 0px;
color: #87caff;
right: 20px;
}
}
.circle-bg{
animation:rotate 5s infinite linear;
}
@keyframes rotate{
0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);}
}
.center{
padding-left: 40px;
padding-right: 50px;
padding-bottom: 40px;
padding-top: 50px;
display: flex;
align-items: center;
justify-content: space-between;
.circle-bg {
animation: rotate 5s infinite linear;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.item {
display: flex;
/*align-items: center;*/
.item-icon {
width: 117px;
height: 109px;
}
.item-icon1 {
background: url('@/assets/image/center-inner1.png') no-repeat center 43%;
}
.item-right {
margin-left: 20px;
.item-right-inner {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.text-title {
font-size: 20px;
letter-spacing: 1px;
color: #ffffff;
/*margin-bottom: 6px;*/
/*height: 20px;*/
}
.text-number {
font-size: 44px;
letter-spacing: 2px;
color: #00e4ff;
}
.text-der {
overflow: hidden;
box-sizing: border-box;
display: flex;
font-size: 16px;
color: #ffffff;
letter-spacing: 1px;
.left {
margin-right: 10px;
}
img {
margin-right: 4px;
margin-top: 4px;
width: 11px;
height: 15px;
}
.right {
color: #ff0000;
}
}
}
}
}
.footer{
position: relative;
padding: 0 30px 0 24px;
display: flex;
.left{
box-sizing: border-box;
padding: 32px 0;
width: 568px;
//height: 701px;
background-size: contain;
background-repeat: no-repeat;
}
.middle{
flex: 1;
position: relative;
.migration{
left: -18%;
top:-15%;
width: 900px;
height: 900px;
position: absolute;
}
}
.right{
width: 568px;
align-items: flex-end;
box-sizing: border-box;
padding: 32px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
background-size:contain ;
background-repeat:no-repeat;
}
.item-complex {
width: 558px;
height: 362px;
background-image: url('@/assets/image/charts/1-1-bg.png');
background-color: #042750;
background-size: contain;
background-repeat: no-repeat;
}
}
}

View File

@ -31,7 +31,7 @@
<line-charts width="100%" height="100%" />
</div>
<div class="item-complex">
<bar-charts width="100%" height="100%" />
<MultilineCharts />
</div>
</div>
<div class="middle">
@ -41,10 +41,10 @@
</div>
<div class="right">
<div class="item-complex" style="margin-bottom: 20px">
<line-charts width="100%" height="100%" />
<BarCharts width="100%" height="100%" />
</div>
<div class="item-complex">
<bar-charts width="100%" height="100%" />
<PieCharts />
</div>
</div>
</div>
@ -58,6 +58,8 @@ import CountTo from '@/components/CountTo/index.vue'
import LineCharts from '@/views/charts/components/complex/line/index.vue'
import BarCharts from '@/views/charts/components/complex/bar/index.vue'
import MigrationCharts from '@/views/charts/components/migration/index.vue'
import MultilineCharts from '@/components/DataScreen/Multiline/index'
import PieCharts from '@/components/DataScreen/Pie/index'
const timeDate = ref()
const hourTime = ref()
@ -93,171 +95,5 @@ onBeforeUnmount(()=>{
</script>
<style lang="scss" scoped>
.m-data-screen{
width: 1920px;
height: 1080px;
box-sizing: border-box;
overflow:hidden;
//background: url("/static/screen/bg.png") no-repeat center center;
background: #041c4a;
.header{
box-sizing: border-box;
width: 100%;
height: 91px;
background: url("/static/screen/header-bg.png");
background-size:contain ;
background-repeat:no-repeat;
display: flex;
align-items: center;
justify-content: center;
.header-bg-title{
font-size: 42px;
font-weight: normal;
font-stretch: normal;
letter-spacing: 2px;
color: #ffffff;
margin-bottom: 14px;
}
.date{
top: 25px;
line-height: 68px;
font-size: 18px;
position: absolute;
letter-spacing: 0px;
color: #87caff;
right: 20px;
}
}
//.header{
// width: 100%;
// .header-bg-title{
// width: 100%;
// }
//
//}
.circle-bg{
animation:rotate 5s infinite linear;
}
@keyframes rotate{
0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);}
}
.center{
padding-left: 40px;
padding-right: 50px;
padding-bottom: 40px;
padding-top: 50px;
display: flex;
align-items: center;
justify-content: space-between;
.circle-bg {
animation: rotate 5s infinite linear;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.item {
display: flex;
/*align-items: center;*/
.item-icon {
width: 117px;
height: 109px;
}
.item-icon1 {
background: url('@/assets/image/center-inner1.png') no-repeat center 43%;
}
.item-right {
margin-left: 20px;
.item-right-inner {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.text-title {
font-size: 20px;
letter-spacing: 1px;
color: #ffffff;
/*margin-bottom: 6px;*/
/*height: 20px;*/
}
.text-number {
font-size: 44px;
letter-spacing: 2px;
color: #00e4ff;
}
.text-der {
overflow: hidden;
box-sizing: border-box;
display: flex;
font-size: 16px;
color: #ffffff;
letter-spacing: 1px;
.left {
margin-right: 10px;
}
img {
margin-right: 4px;
margin-top: 4px;
width: 11px;
height: 15px;
}
.right {
color: #ff0000;
}
}
}
}
}
.footer{
position: relative;
padding: 0 30px 0 24px;
display: flex;
.left{
box-sizing: border-box;
padding: 32px 0;
width: 568px;
//height: 701px;
background-size: contain;
background-repeat: no-repeat;
}
.middle{
flex: 1;
position: relative;
.migration{
left: -18%;
top:-15%;
width: 900px;
height: 900px;
position: absolute;
}
}
.right{
width: 568px;
align-items: flex-end;
box-sizing: border-box;
padding: 32px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
background-size:contain ;
background-repeat:no-repeat;
}
.item-complex {
width: 558px;
height: 362px;
background-image: url('@/assets/image/charts/1-1-bg.png');
background-color: #042750;
background-size: contain;
background-repeat: no-repeat;
}
}
}
@import "./index";
</style>

236
src/views/error/403.vue Normal file
View File

@ -0,0 +1,236 @@
<template>
<div class="wscn-http403-container">
<div class="wscn-http403">
<div class="pic-403">
<img
class="pic-403__parent"
src="@/assets/403_images/403.png"
alt="403"
/>
<img
class="pic-403__child left"
src="@/assets/403_images/403_cloud.png"
alt="403"
/>
<img
class="pic-403__child mid"
src="@/assets/403_images/403_cloud.png"
alt="403"
/>
<img
class="pic-403__child right"
src="@/assets/403_images/403_cloud.png"
alt="403"
/>
</div>
<div class="bullshit">
<div class="bullshit__oops">您没有访问权限</div>
<div class="bullshit__info">
请检查URL地址是否正确, 或点击回到首页
</div>
<router-link to="/" class="bullshit__return-home">回到首页</router-link>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
</script>
<style lang="scss" scoped>
.wscn-http403-container {
transform: translate(-50%, -50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http403 {
position: relative;
width: 1200px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 50px;
overflow: hidden;
.pic-403 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@ -1,58 +1,218 @@
<template>
<u-container-layout>
<div class="error-page">
<div class="img">
<img :src="fourPng" style="width: 100%" />
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">页面不存在</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查URL地址是否正确, 或点击回到首页</div>
<router-link to="/" class="bullshit__return-home">回到首页</router-link>
</div>
<div>
<div class="oops">抱歉!</div>
<div class="text">当前页面不存在...</div>
<div style="margin-bottom: 20px; font-size: 13px; color: grey"
>请检查您输入的URL是否正确或单击下面的按钮返回主页</div
>
<el-button type="primary" @click="goBackHome">返回首页</el-button></div
>
</div>
</u-container-layout>
</div>
</template>
<script lang="ts" setup>
import fourPng from '@/assets/image/error/404s.png'
import { useRouter } from 'vue-router'
const router = useRouter()
const goBackHome = () => {
router.push({
path: '/',
})
}
<script lang="ts">
</script>
<style lang="scss" scoped>
.error-page {
height: 100%;
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
display: flex;
align-items: center;
.img {
width: 500px;
justify-content: center;
align-items: center;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
.oops {
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
-webkit-animation-fill-mode: forwards;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
.text {
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
-webkit-animation-delay: 0.1s;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@ -1,123 +0,0 @@
<template>
<u-container-layout>
<div class="header">
<el-input
v-model="input"
placeholder="请输入文件名"
style="width: 200px; margin-right: 10px"
/>
<div>
<label style="margin-right: 10px">导出格式</label>
<el-select
v-model="format"
class="m-2"
placeholder="导出格式"
style="width: 200px; margin-right: 10px"
>
<el-option label="xlsx" value="xlsx" />
<el-option label="csv" value="csv" />
</el-select>
</div>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 10px"><document-remove /></el-icon> Excel
</el-button>
</div>
<div class="footer">
<el-table :data="list" border class="table">
<template v-for="(item, index) in column" :key="index">
<el-table-column :prop="item.name" :label="item.label" :width="item.width" />
</template>
</el-table>
</div>
</u-container-layout>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import * as dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
import { exportExcel } from '@/utils/exprotExcel'
const data = []
for (let i = 0; i < 10; i++) {
data.push({
date: '2016-05-02',
name: '王五' + i,
price: 1 + i,
province: '上海',
admin: 'admin',
sex: i % 2 ? 1 : 0,
checked: true,
id: i + 1,
age: 0,
city: '普陀区',
address: '上海市普上海',
zip: 200333,
})
}
const column = [
{ name: 'id', label: 'id' },
{ name: 'name', label: '姓名', inSearch: true, valueType: 'input' },
{ name: 'age', label: '年龄', align: 'right' },
{
name: 'sex',
label: '性别',
slot: true,
inSearch: true,
options: [
{
value: 1,
label: '男',
},
{
value: 0,
label: '女',
},
],
valueType: 'select',
},
{
name: 'price',
label: '价格',
inSearch: true,
valueType: 'input',
},
{ name: 'admin', label: '账号', inSearch: true, valueType: 'input' },
{ name: 'address', label: '地址', inSearch: true, valueType: 'input' },
{ name: 'date', label: '日期', sorter: true, inSearch: true, valueType: 'input' },
{ name: 'province', label: '省份' },
{ name: 'city', label: '城市' },
{ name: 'zip', label: '邮编' },
]
const list = ref(data)
const input = ref('')
const format = ref('xlsx')
const exportExcelAction = () => {
exportExcel({
column,
data,
filename: input.value || '导出 excel',
format: format.value,
autoWidth: true,
})
}
</script>
<style lang="scss" scoped>
.header{
display: flex;
align-items: center;
margin-bottom: 15px;
flex-shrink: 0;
}
.footer{
flex: 1;
position: relative;
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}
</style>

View File

@ -0,0 +1,32 @@
.header{
display: flex;
padding: 16px 16px 16px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.footer-inner{
position: relative;
width: 100%;
height: 100%;
}
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}

View File

@ -0,0 +1,69 @@
<template>
<div class="app-container">
<div class="header">
<el-input
v-model="input"
placeholder="请输入文件名"
style="width: 200px; margin-right: 10px"
/>
<div>
<label style="margin-right: 10px">导出格式</label>
<el-select
v-model="format"
class="m-2"
placeholder="导出格式"
style="width: 200px; margin-right: 10px"
>
<el-option label="xlsx" value="xlsx" />
<el-option label="csv" value="csv" />
</el-select>
</div>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 6px"><Download /></el-icon> Excel
</el-button>
</div>
<div class="footer">
<div class="footer-inner">
<el-table :data="list" border class="table">
<template v-for="(item, index) in column" :key="index">
<el-table-column :prop="item.name" :label="item.label" :width="item.width" />
</template>
</el-table>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {tableList} from "@/mock/table"
import { ref } from 'vue'
import { exportExcel } from '@/utils/exprotExcel'
const column = [
{ name: 'id', label: 'id' },
{ name: 'name', label: '姓名' },
{ name: 'age', label: '年龄', align: 'right' },
{name: 'price', label: '价格', },
{ name: 'admin', label: '账号' },
{ name: 'address', label: '地址',width:250 },
{ name: 'date', label: '日期' ,width:140},
{ name: 'province', label: '省份' },
{ name: 'city', label: '城市' },
{ name: 'zip', label: '邮编' },
]
const list = ref(tableList)
const input = ref('')
const format = ref('xlsx')
const exportExcelAction = () => {
exportExcel({
column,
data:list.value,
filename: input.value || '导出 excel',
format: format.value,
autoWidth: true,
})
}
</script>
<style lang="scss" scoped>
@import "./index";
</style>

View File

@ -1,34 +1,35 @@
<template>
<u-container-layout>
<div style="margin-bottom: 15px">
<div class="app-container">
<div class="header">
<el-input
v-model="input"
placeholder="请输入文件名"
style="width: 200px; margin-right: 10px"
/>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 10px"><document-remove /></el-icon> Excel
<el-icon style="margin-right: 6px"><Download /></el-icon> Excel
</el-button>
</div>
<el-table :data="list" style="width: 100%" border>
<template v-for="(item, index) in column" :key="index">
<template v-if="item.children">
<el-table-column :prop="item.name" :label="item.label" :width="item.width">
<template v-for="(ite, i) in item.children" :key="i">
<el-table-column :prop="ite.name" :label="ite.label" :width="ite.width" />
</template>
</el-table-column>
<div class="footer">
<el-table :data="list" style="width: 100%" border>
<template v-for="(item, index) in column" :key="index">
<template v-if="item.children">
<el-table-column :prop="item.name" :label="item.label" :width="item.width">
<template v-for="(ite, i) in item.children" :key="i">
<el-table-column :prop="ite.name" :label="ite.label" :width="ite.width" />
</template>
</el-table-column>
</template>
<el-table-column :prop="item.name" :label="item.label" :width="item.width" v-else />
</template>
<el-table-column :prop="item.name" :label="item.label" :width="item.width" v-else />
</template>
</el-table>
</u-container-layout>
</el-table>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import * as dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
import { exportMultiHeaderExcel } from '@/utils/exprotExcel'
const data = []
for (let i = 0; i < 10; i++) {
@ -73,4 +74,23 @@
}
</script>
<style></style>
<style lang="scss" scoped>
.header{
display: flex;
padding: 16px 16px 16px 16px;
margin-bottom: 12px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
</style>

View File

@ -1,23 +1,25 @@
<template>
<u-container-layout>
<div class=".header">
<div class="app-container">
<div class="header">
<el-input
v-model="input"
placeholder="请输入文件名"
placeholder="默认文件名导出 excel"
style="width: 200px; margin-right: 10px"
/>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 10px"><document-remove /></el-icon> Excel
<el-icon style="margin-right: 6px"><Download /></el-icon> Excel
</el-button>
</div>
<div class="footer">
<el-table :data="list" class="table" border>
<template v-for="(item, index) in column" :key="index">
<el-table-column :prop="item.name" :label="item.label" :width="item.width" />
</template>
</el-table>
<div class="footer-inner">
<el-table :data="list" class="table" border>
<template v-for="(item, index) in column" :key="index">
<el-table-column :prop="item.name" :label="item.label" :width="item.width" />
</template>
</el-table>
</div>
</div>
</u-container-layout>
</div>
</template>
<script lang="ts" setup>
@ -44,34 +46,12 @@
}
const column = [
{ name: 'id', label: 'id' },
{ name: 'name', label: '姓名', inSearch: true, valueType: 'input' },
{ name: 'name', label: '姓名' },
{ name: 'age', label: '年龄', align: 'right' },
{
name: 'sex',
label: '性别',
slot: true,
inSearch: true,
options: [
{
value: 1,
label: '男',
},
{
value: 0,
label: '女',
},
],
valueType: 'select',
},
{
name: 'price',
label: '价格',
inSearch: true,
valueType: 'input',
},
{ name: 'admin', label: '账号', inSearch: true, valueType: 'input' },
{ name: 'address', label: '地址', inSearch: true, valueType: 'input' },
{ name: 'date', label: '日期', sorter: true, inSearch: true, valueType: 'input' },
{name: 'price', label: '价格', },
{ name: 'admin', label: '账号' },
{ name: 'address', label: '地址',width:180 },
{ name: 'date', label: '日期' ,width:140},
{ name: 'province', label: '省份' },
{ name: 'city', label: '城市' },
{ name: 'zip', label: '邮编' },
@ -93,13 +73,28 @@
<style lang="scss" scoped>
.header{
display: flex;
align-items: center;
margin-bottom: 15px;
flex-shrink: 0;
padding: 16px 16px 16px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.footer-inner{
position: relative;
width: 100%;
height: 100%;
}
.table{
position: absolute;
left: 0;

View File

@ -0,0 +1,108 @@
<template>
<div class="app-container">
<div class="header">
<el-input
v-model="zipName"
placeholder="请输入文件名"
style="width: 200px; margin-right: 10px"
/>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 6px"><Download/></el-icon> zip
</el-button>
</div>
<div class="footer">
<el-table :data="list" style="width: 100%" border>
<template v-for="(item, index) in column" :key="index">
<el-table-column
:prop="item.name"
:label="item.label"
:width="item.width" />
</template>
</el-table>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import * as dayjs from 'dayjs'
import { downloadFiles2Zip } from '@/utils/downloadzip'
const data = []
for (let i = 0; i < 10; i++) {
data.push({
date: '2016-05-02',
name: '王五' + i,
price: 1 + i,
province: '上海',
admin: 'admin',
checked: true,
id: i + 1,
age: 0,
city: '普陀区',
address: '上海市普上海',
zip: 200333,
})
}
const column = [
{ name: 'id', label: 'id' },
{ name: 'name', label: '姓名' },
{ name: 'age', label: '年龄', align: 'right' },
{name: 'price', label: '价格', },
{ name: 'admin', label: '账号' },
{ name: 'address', label: '地址',width:180 },
{ name: 'date', label: '日期' ,width:140},
{ name: 'province', label: '省份' },
{ name: 'city', label: '城市' },
{ name: 'zip', label: '邮编' },
]
const list = ref(data)
const zipName = ref('下载zip')
const format = ref('xlsx')
const exportExcelAction = () => {
downloadFiles2Zip({
zipName: zipName.value,
files: [
{
filename: 'test',
sheets: [
{
sheetName: 'test',
columns: column,
dataSource: data,
},
],
},
],
})
}
</script>
<style lang="scss" scoped>
.app-container{
height: 100%;
width: 100%;
padding: 10px 12px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.header{
display: flex;
padding: 16px 16px 16px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
}
</style>

View File

@ -48,14 +48,17 @@
><el-icon style="font-size: 24px; color: white"><user /></el-icon
></div>
<div class="right">
<h2 style="color: #2d8cf0"
<div
class="h2"
style="color: #2d8cf0"
>
><count-to
:start-val="0"
:end-val="5268"
:duration="2000"
:autoplay="true"
></count-to
></h2>
></div>
<div>用户访问量 </div>
</div>
</div>
@ -68,14 +71,17 @@
><el-icon style="font-size: 24px; color: white"><user /></el-icon
></div>
<div class="right">
<h2 style="color: #64d572"
<div
class="h2"
style="color: #64d572"
>
><count-to
:start-val="0"
:end-val="9599"
:duration="2000"
:autoplay="true"
></count-to
></h2>
></div>
<div>系统消息 </div>
</div>
</div>
@ -88,14 +94,16 @@
><el-icon style="font-size: 24px; color: white"><user /></el-icon
></div>
<div class="right">
<h2 style="color: #f25e43"
<div
class="h2"
style="color: #f25e43"
><count-to
:start-val="0"
:end-val="595453"
:duration="2000"
:autoplay="true"
></count-to
></h2>
></div>
<div>数量 </div>
</div>
</div>
@ -202,6 +210,10 @@
flex-direction: column;
padding-left: 20px;
justify-content: center;
.h2{
font-size: 24px;
margin-bottom: 10px;
}
}
}
.custom {

View File

@ -2,8 +2,8 @@
<u-container-layout>
<el-card style="margin-bottom: 20px">
<div style="margin-bottom: 10px">输入内容并点击复制按钮</div>
<el-input v-model="inputData" placeholder="请输入" style="width: 400px; max-width: 100%" />
<el-button type="primary" @click="handleCopy(inputData, $event)">
<el-input v-model="copyValue" placeholder="请输入" style="width: 400px; max-width: 100%" />
<el-button type="primary" @click="handleCopy(copyValue, $event)">
<el-icon style="margin-right: 6px"><document-copy /></el-icon>
</el-button>
</el-card>
@ -14,12 +14,12 @@
</u-container-layout>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import clip from '@/utils/clipboard'
const inputData = ref('https://github.com/zouzhibin/vue-admin-perfect')
const testValue = ref('')
import { ref } from 'vue'
import clip from '@/utils/clipboard'
const copyValue = ref('https://github.com/zouzhibin/vue-admin-perfect')
const testValue = ref('')
const handleCopy = (text, event) => {
clip(text, event)
}
const handleCopy = (text, event) => {
clip(text, event)
}
</script>

View File

@ -1,223 +0,0 @@
<template>
<div>
<input ref="input" type="file" name="image" accept="image/*" @change="setImage" />
<div class="content">
<section class="cropper-area">
<div class="img-cropper">
<vue-cropper ref="cropper" :aspect-ratio="16 / 9" :src="imgSrc" preview=".preview" />
</div>
<div class="actions">
<a href="#" role="button" @click.prevent="zoom(0.2)"> Zoom In </a>
<a href="#" role="button" @click.prevent="zoom(-0.2)"> Zoom Out </a>
<a href="#" role="button" @click.prevent="move(-10, 0)"> Move Left </a>
<a href="#" role="button" @click.prevent="move(10, 0)"> Move Right </a>
<a href="#" role="button" @click.prevent="move(0, -10)"> Move Up </a>
<a href="#" role="button" @click.prevent="move(0, 10)"> Move Down </a>
<a href="#" role="button" @click.prevent="rotate(90)"> Rotate +90deg </a>
<a href="#" role="button" @click.prevent="rotate(-90)"> Rotate -90deg </a>
<a ref="flipX" href="#" role="button" @click.prevent="flipX"> Flip X </a>
<a ref="flipY" href="#" role="button" @click.prevent="flipY"> Flip Y </a>
<a href="#" role="button" @click.prevent="cropImage"> Crop </a>
<a href="#" role="button" @click.prevent="reset"> Reset </a>
<a href="#" role="button" @click.prevent="getData"> Get Data </a>
<a href="#" role="button" @click.prevent="setData"> Set Data </a>
<a href="#" role="button" @click.prevent="getCropBoxData"> Get CropBox Data </a>
<a href="#" role="button" @click.prevent="setCropBoxData"> Set CropBox Data </a>
<a href="#" role="button" @click.prevent="showFileChooser"> Upload Image </a>
</div>
<textarea v-model="data" />
</section>
<section class="preview-area">
<p>Preview</p>
<div class="preview" />
<p>Cropped Image</p>
<div class="cropped-image">
<img v-if="cropImg" :src="cropImg" alt="Cropped Image" />
<div v-else class="crop-placeholder" />
</div>
</section>
</div>
</div>
</template>
<script lang="ts">
import VueCropper from 'vue-cropperjs'
import 'cropperjs/dist/cropper.css'
// import imgSrc from "@/assets/image/berserk.jpg"
import imgSrc from '@/assets/image/im1.jpeg'
export default {
components: {
VueCropper,
},
data() {
return {
imgSrc: imgSrc,
cropImg: '',
data: null,
}
},
methods: {
cropImage() {
// get image data for post processing, e.g. upload or setting image src
this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
},
flipX() {
const dom = this.$refs.flipX
let scale = dom.getAttribute('data-scale')
scale = scale ? -scale : -1
this.$refs.cropper.scaleX(scale)
dom.setAttribute('data-scale', scale)
},
flipY() {
const dom = this.$refs.flipY
let scale = dom.getAttribute('data-scale')
scale = scale ? -scale : -1
this.$refs.cropper.scaleY(scale)
dom.setAttribute('data-scale', scale)
},
getCropBoxData() {
this.data = JSON.stringify(this.$refs.cropper.getCropBoxData(), null, 4)
},
getData() {
this.data = JSON.stringify(this.$refs.cropper.getData(), null, 4)
},
move(offsetX, offsetY) {
this.$refs.cropper.move(offsetX, offsetY)
},
reset() {
this.$refs.cropper.reset()
},
rotate(deg) {
this.$refs.cropper.rotate(deg)
},
setCropBoxData() {
if (!this.data) return
this.$refs.cropper.setCropBoxData(JSON.parse(this.data))
},
setData() {
if (!this.data) return
this.$refs.cropper.setData(JSON.parse(this.data))
},
setImage(e) {
const file = e.target.files[0]
if (file.type.indexOf('image/') === -1) {
alert('Please select an image file')
return
}
if (typeof FileReader === 'function') {
const reader = new FileReader()
reader.onload = (event) => {
this.imgSrc = event.target.result
// rebuild cropperjs with the updated source
this.$refs.cropper.replace(event.target.result)
}
reader.readAsDataURL(file)
} else {
alert('Sorry, FileReader API not supported')
}
},
showFileChooser() {
this.$refs.input.click()
},
zoom(percent) {
this.$refs.cropper.relativeZoom(percent)
},
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
body {
font-family: Arial, Helvetica, sans-serif;
width: 1024px;
margin: 0 auto;
}
input[type='file'] {
display: none;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0 5px 0;
}
.header h2 {
margin: 0;
}
.header a {
text-decoration: none;
color: black;
}
.content {
display: flex;
justify-content: space-between;
}
.cropper-area {
width: 614px;
}
.actions {
margin-top: 1rem;
}
.actions a {
display: inline-block;
padding: 5px 15px;
background: #0062cc;
color: white;
text-decoration: none;
border-radius: 3px;
margin-right: 1rem;
margin-bottom: 1rem;
}
textarea {
width: 100%;
height: 100px;
}
.preview-area {
width: 307px;
}
.preview-area p {
font-size: 1.25rem;
margin: 0;
margin-bottom: 1rem;
}
.preview-area p:last-of-type {
margin-top: 1rem;
}
.preview {
width: 100%;
height: calc(372px * (9 / 16));
overflow: hidden;
}
.crop-placeholder {
width: 100%;
height: 200px;
background: #ccc;
}
.cropped-image img {
max-width: 100%;
}
</style>

View File

@ -1,167 +0,0 @@
<template>
<div class="m-cropper">
<div class="left">
<div class="cropper-content">
<vue-cropper
ref="cropper"
:src="options.imgSrc"
@crop="cropmove"
:minCropBoxWidth="options.minCropBoxWidth"
preview=".preview"
/>
</div>
<div>
<el-button type="primary" @click.prevent="zoom(0.2)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-0.2)">缩小</el-button>
<el-button type="primary" @click.prevent="move(-10, 0)">向左移动</el-button>
<el-button type="primary" @click.prevent="move(10, 0)">向右移动</el-button>
<el-button type="primary" @click.prevent="move(0, -10)">向上移动</el-button>
<el-button type="primary" @click.prevent="move(0, 10)">向下移动</el-button>
<el-button type="primary" @click.prevent="rotate(90)">旋转90度</el-button>
<el-button type="primary" @click.prevent="rotate(-90)">旋转-90</el-button>
<el-button type="primary" ref="flipX" @click.prevent="flipXAction">翻转 X</el-button>
<el-button type="primary" ref="flipY" @click.prevent="flipYAction">翻转 Y</el-button>
<el-button type="primary" @click.prevent="reset">重置</el-button>
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
<el-upload class="upload-demo" :show-file-list="false" action :before-upload="beforeUpload">
<el-button type="primary">选择上传图片</el-button>
</el-upload>
</div>
</div>
<div>
<div class="preview"> </div>
<h4 style="margin-top: 10px">得到结果</h4>
<div style="background: #ccc; width: 100px; height: 100px">
<img :src="options.cropImg" style="width: 100%; height: 100%" v-if="options.cropImg" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
// http://github.xyxiao.cn/vue-cropper/example/
// https://codepen.io/xyxiao001/pen/yLooYKg
import VueCropper from 'vue-cropperjs'
import imgSrc from '@/assets/image/cro.jpg'
import 'cropperjs/dist/cropper.css'
import { reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus'
const cropper = ref()
const flipX = ref()
const flipY = ref()
const options = reactive({
cropImg: null,
imgSrc: imgSrc,
minCropBoxWidth: 200,
})
const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
console.log(file, uploadFiles)
}
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
console.log(uploadFile)
}
const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
ElMessage.warning(
`The limit is 3, you selected ${files.length} files this time, add up to ${
files.length + uploadFiles.length
} totally`,
)
}
const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
return ElMessageBox.confirm(`Cancel the transfert of ${uploadFile.name} ?`).then(
() => true,
() => false,
)
}
const cropmove = (e) => {
// console.log('eeeeee',e)
}
const zoom = (percent) => {
cropper.value.relativeZoom(percent)
}
const move = (offsetX, offsetY) => {
cropper.value.move(offsetX, offsetY)
}
const rotate = (deg) => {
cropper.value.rotate(deg)
}
const flipXAction = () => {
const dom = flipX.value
let scale = dom.getAttribute('data-scale')
scale = scale ? -scale : -1
cropper.value.scaleX(scale)
dom.setAttribute('data-scale', scale)
}
const flipYAction = () => {
const dom = flipY.value
let scale = dom.getAttribute('data-scale')
scale = scale ? -scale : -1
cropper.value.scaleY(scale)
dom.setAttribute('data-scale', scale)
}
const cropImage = () => {
options.cropImg = cropper.value.getCroppedCanvas().toDataURL()
}
const reset = () => {
cropper.value.reset()
}
const beforeUpload = (file, fileList) => {
return new Promise((resolve, reject) => {
var reader = new FileReader()
// let res = !/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
reader.readAsDataURL(file)
let name = file.name
if (reg.test(name)) {
reader.onload = (e: FileReader) => {
resolve(e.target.result)
options.imgSrc = e.target.result
cropper.value.replace(e.target.result)
}
} else {
ElMessage.error('请上传图片')
reject()
}
})
}
</script>
<style lang="scss" scoped>
.cropper-content {
display: flex;
width: 400px;
text-align: center;
}
::v-deep(.el-button) {
margin-top: 15px;
}
.m-cropper {
display: flex;
.left {
width: 400px;
margin-right: 30px;
}
.preview {
width: 200px;
height: 200px;
margin-top: 20px;
overflow: hidden;
}
}
</style>

View File

@ -1,220 +0,0 @@
<template>
<div class="m-cropper">
<div class="left">
<div class="cropper-content">
<vue-cropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:fixed="fixed"
:fixed-number="fixedNumber"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixed-box="option.fixedBox"
:auto-crop="option.autoCrop"
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
@real-time="realTime"
/>
</div>
<!-- <div>-->
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
<!-- <el-button type="primary" @click.prevent="move(0, -10)">向上移动</el-button>-->
<!-- <el-button type="primary" @click.prevent="move(0, 10)">向下移动</el-button>-->
<!-- <el-button type="primary" @click.prevent="rotate(90)">旋转90度</el-button>-->
<!-- <el-button type="primary" @click.prevent="rotate(-90)">旋转-90</el-button>-->
<!-- <el-button type="primary"-->
<!-- ref="flipX"-->
<!-- @click.prevent="flipXAction">翻转 X</el-button>-->
<!-- <el-button type="primary"-->
<!-- ref="flipY"-->
<!-- @click.prevent="flipYAction">翻转 Y</el-button>-->
<el-button type="primary" @click.prevent="reset">重置</el-button>
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
<el-upload class="upload-demo" :show-file-list="false" action :before-upload="beforeUpload">
<el-button type="primary">选择上传图片</el-button>
</el-upload>
<!-- </div>-->
</div>
<div>
<h4 style="margin-top: 10px">实时预览</h4>
<div
class="show-preview"
:style="{
width: option.previews.w + 'px',
height: option.previews.h + 'px',
overflow: 'hidden',
margin: '5px',
}"
>
<div :style="option.previews.div">
<img :src="option.previews.url" :style="option.previews.img" />
</div>
</div>
<h4 style="margin-top: 10px">获取结果</h4>
<div style="background: #ccc; width: 100px; height: 100px">
<img :src="option.cropImg" style="width: 100%; height: 100%" v-if="option.cropImg" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
// http://github.xyxiao.cn/vue-cropper/example/
// https://codepen.io/xyxiao001/pen/yLooYKg
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
import imgSrc from '@/assets/image/cro.jpg'
import { reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus'
const cropper = ref()
const flipX = ref()
const flipY = ref()
const option = reactive({
img: imgSrc,
size: 1,
full: false, //
outputType: 'png',
canMove: true,
info: true, //
outputSize: 0.8, //
fixedBox: false, //
original: false, //
canMoveBox: true, //
autoCrop: true, //
autoCropWidth: 300, //
autoCropHeight: 300, //
//
centerBox: false, //
high: true,
max: 99999,
previews: {
url: '',
},
cropImg: '',
})
const realTime = (data) => {
option.previews = data
}
const cropmove = (e) => {
// console.log('eeeeee',e)
}
const zoom = (percent) => {
cropper.value.changeScale(percent)
}
const rotateLeft = () => {
cropper.value.rotateLeft()
}
const rotateRight = () => {
cropper.value.rotateRight()
}
const move = (offsetX, offsetY) => {
cropper.value.move(offsetX, offsetY)
}
const down = (type) => {
// event.preventDefault()
var aLink = document.createElement('a')
aLink.download = 'demo'
//
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
// this.downImg = window.URL.createObjectURL(data);
aLink.href = window.URL.createObjectURL(data)
aLink.click()
})
} else {
cropper.value.getCropData((data) => {
// this.downImg = data;
aLink.href = data
aLink.click()
})
}
}
const rotate = (deg) => {
cropper.value.rotate(deg)
}
const cropImage = () => {
cropper.value.getCropData((data) => {
option.cropImg = data
})
}
const reset = () => {
option.img = ''
option.cropImg = ''
}
const beforeUpload = (file, fileList) => {
return new Promise((resolve, reject) => {
var reader = new FileReader()
// let res = !/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
reader.readAsDataURL(file)
let name = file.name
if (reg.test(name)) {
reader.onload = (e: FileReader) => {
let data
if (typeof e.target.result === 'object') {
// Array Bufferblob base64
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
resolve(e.target.result)
option.imgSrc = data
option.img = data
}
} else {
ElMessage.error('请上传图片')
reject()
}
})
}
</script>
<style lang="scss" scoped>
.cropper-content {
display: flex;
width: 400px;
height: 400px;
text-align: center;
}
::v-deep(.el-button) {
margin-top: 15px;
}
.m-cropper {
width: 100%;
height: 500px;
display: flex;
.left {
width: 400px;
margin-right: 30px;
}
.preview {
width: 200px;
height: 200px;
margin-top: 20px;
overflow: hidden;
}
}
</style>

View File

@ -1,51 +1,53 @@
<template>
<u-container-layout>
<el-form ref="formRef" :model="dynamicValidateForm" label-width="80px" class="demo-dynamic">
<el-form-item
prop="title"
label="标题"
:rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]"
<div class="app-container">
<div class="app-container-inner">
<el-form ref="formRef" :model="dynamicValidateForm" label-width="80px" class="demo-dynamic">
<el-form-item
prop="title"
label="标题"
:rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]"
>
<el-input v-model="dynamicValidateForm.title" />
</el-form-item>
<el-form-item
prop="content"
label="标题"
:rules="[{ required: true, message: '请输入内容', trigger: 'blur' }]"
>
<wang-edior v-model="dynamicValidateForm.content" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(formRef)">保存</el-button>
<el-button type="primary" @click="preview(formRef)">预览</el-button>
<el-button @click="resetForm(formRef)">重置</el-button>
</el-form-item>
</el-form>
<el-descriptions title="配置项 " :column="1" border class="descriptions">
<el-descriptions-item label="value"> 双向绑定的 value 使用示例v-model='content' </el-descriptions-item>
<el-descriptions-item label="参考文档"> <a href="https://www.wangeditor.com/v5/for-frame.html" target="_blank"> https://www.wangeditor.com/v5/for-frame.html </a> </el-descriptions-item>
</el-descriptions>
<el-dialog
v-model="dialogVisible"
title="预览"
width="60%"
>
<el-input v-model="dynamicValidateForm.title" />
</el-form-item>
<el-form-item
prop="content"
label="标题"
:rules="[{ required: true, message: '请输入内容', trigger: 'blur' }]"
>
<wang-edior v-model="dynamicValidateForm.content" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(formRef)">保存</el-button>
<el-button type="primary" @click="preview(formRef)">预览</el-button>
<el-button @click="resetForm(formRef)">重置</el-button>
</el-form-item>
</el-form>
<el-descriptions title="配置项 " :column="1" border class="descriptions">
<el-descriptions-item label="value"> 双向绑定的 value 使用示例v-model='content' </el-descriptions-item>
<el-descriptions-item label="参考文档"> <a href="https://www.wangeditor.com/v5/for-frame.html" target="_blank"> https://www.wangeditor.com/v5/for-frame.html </a> </el-descriptions-item>
</el-descriptions>
<el-dialog
v-model="dialogVisible"
title="预览"
width="60%"
>
<div style="display: flex;align-items: center;margin-bottom: 20px">
<span style="width: 50px;font-weight: bold">标题</span>
<div>{{dynamicValidateForm.title}}</div>
</div>
<div style="display: flex;align-items: center;margin-bottom: 20px">
<span style="width: 50px;font-weight: bold">内容</span>
<div v-html="dynamicValidateForm.content"></div>
</div>
<template #footer>
</template>
</el-dialog>
</u-container-layout>
<div style="display: flex;align-items: center;margin-bottom: 20px">
<span style="width: 50px;font-weight: bold">标题</span>
<div>{{dynamicValidateForm.title}}</div>
</div>
<div style="display: flex;align-items: center;margin-bottom: 20px">
<span style="width: 50px;font-weight: bold">内容</span>
<div v-html="dynamicValidateForm.content"></div>
</div>
<template #footer>
</template>
</el-dialog>
</div>
</div>
</template>
<script lang="ts" setup>
@ -96,3 +98,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
formEl.resetFields()
}
</script>

View File

@ -1,33 +1,39 @@
<template>
<u-container-layout class="components-container">
<el-form :inline="true" :model="formInline" class="demo-form-inline" ref="ruleFormRef1">
<el-form-item label="用户名" prop="username">
<el-input v-model="formInline.username" />
</el-form-item>
<el-form-item>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 15px; display: flex; justify-content: flex-end">
<el-button type="primary" @click="add">新增</el-button>
<div class="app-container">
<div class="header">
<el-form :inline="true" :model="formInline" class="demo-form-inline" ref="ruleFormRef1">
<el-form-item label="用户名" prop="username">
<el-input v-model="formInline.username" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" :icon="Search">查询</el-button>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
<div class="util">
<el-button type="primary" @click="add">新增</el-button>
</div>
<div class="table-wrap">
<el-table :data="tableData" style="width: 100%" border default-expand-all row-key="id" class="table">
<el-table-column prop="menuName" label="权限名称" />
<el-table-column prop="menuType" label="权限类型" />
<el-table-column prop="menuRouter" label="权限路由" />
<el-table-column prop="identification" label="权限标识" />
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-table :data="tableData" style="width: 100%" border default-expand-all row-key="id">
<el-table-column prop="menuName" label="权限名称" />
<el-table-column prop="menuType" label="权限类型" />
<el-table-column prop="menuRouter" label="权限路由" />
<el-table-column prop="identification" label="权限标识" />
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer v-model="dialogVisible" :title="title" width="50%">
<el-form
ref="ruleFormRef"
@ -61,13 +67,14 @@
</span>
</template>
</el-drawer>
</u-container-layout>
</div>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import { reactive, ref } from 'vue'
import { menuData } from './data/user'
import {Search } from '@element-plus/icons-vue'
import { menuData } from '@/mock/system'
import * as dayjs from 'dayjs'
const tableData = ref(menuData)
const dialogVisible = ref(false)
@ -159,4 +166,49 @@
}
</script>
<style scoped></style>
<style scoped lang="scss">
.header{
display: flex;
padding: 16px 16px 0px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.util{
margin-bottom: 15px;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
.table-wrap{
flex: 1;
display: flex;
position: relative;
overflow: hidden;
}
.table-inner{
width: 100%;
height: 100%;
position: relative;
}
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}
</style>

View File

@ -1,31 +1,37 @@
<template>
<u-container-layout class="components-container">
<el-form :inline="true" :model="formInline" class="demo-form-inline" ref="ruleFormRef1">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="formInline.roleName" />
</el-form-item>
<el-form-item>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 15px; display: flex; justify-content: flex-end">
<el-button type="primary" @click="add">新增</el-button>
<div class="app-container">
<div class="header">
<el-form :inline="true" :model="formInline" class="demo-form-inline" ref="ruleFormRef1">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="formInline.roleName" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" :icon="Search">查询</el-button>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
<div class="util">
<el-button type="primary" @click="add">新增</el-button>
</div>
<div class="table-inner">
<el-table :data="tableData" style="width: 100%" border>
<el-table-column prop="roleName" label="角色名称" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-table :data="tableData" style="width: 100%" border>
<el-table-column prop="roleName" label="角色名称" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer v-model="dialogVisible" :title="title" width="50%">
<el-form
ref="ruleFormRef"
@ -58,13 +64,14 @@
</span>
</template>
</el-drawer>
</u-container-layout>
</div>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import {Search } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue'
import { roleData, menuData } from './data/user'
import { roleData } from '@/mock/system'
import * as dayjs from 'dayjs'
const tableData = ref(roleData)
const dialogVisible = ref(false)
@ -155,4 +162,43 @@
}
</script>
<style scoped></style>
<style scoped lang="scss">
.header{
display: flex;
padding: 16px 16px 0px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.util{
margin-bottom: 15px;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
.table-inner{
flex: 1;
position: relative;
}
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}
</style>

View File

@ -1,38 +1,44 @@
<template>
<u-container-layout class="components-container">
<el-form :inline="true" :model="formInline" class="demo-form-inline" ref="ruleFormRef1">
<el-form-item label="用户名" prop="username">
<el-input v-model="formInline.username" />
</el-form-item>
<el-form-item>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 15px; display: flex; justify-content: flex-end">
<el-button type="primary" @click="add">新增</el-button>
<div class="app-container">
<div class="header">
<el-form :inline="true" :model="formInline" ref="ruleFormRef1">
<el-form-item label="用户名" prop="username">
<el-input v-model="formInline.username" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" :icon="Search">查询</el-button>
<el-button @click="reset(ruleFormRef1)">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
<div class="util">
<el-button type="primary" @click="add">新增</el-button>
</div>
<div class="table-inner">
<el-table :data="tableData" style="width: 100%" border>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="nickname" label="昵称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-switch v-model="scope.row.status" @change="changeStatus(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-table :data="tableData" style="width: 100%" border>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="nickname" label="昵称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-switch v-model="scope.row.status" @change="changeStatus(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="status" label="操作">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog @close="close" v-model="dialogVisible" :title="title" width="50%">
<el-form
ref="ruleFormRef"
@ -69,13 +75,14 @@
</span>
</template>
</el-dialog>
</u-container-layout>
</div>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import {Search } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue'
import { userData } from './data/user'
import { userData } from '@/mock/system'
import * as dayjs from 'dayjs'
const tableData = ref(userData)
const dialogVisible = ref(false)
@ -168,4 +175,43 @@
}
</script>
<style scoped></style>
<style scoped lang="scss">
.header{
display: flex;
padding: 16px 16px 0px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.util{
margin-bottom: 15px;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
.table-inner{
flex: 1;
position: relative;
}
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}
</style>

View File

@ -273,7 +273,6 @@ onMounted(() => {
width: 100%;
padding: 16px;
box-sizing: border-box;
}
.cancel-btn {
position: absolute;

View File

@ -1,7 +1,7 @@
<template>
<u-container-layout>
<div class="inline-edit-table">
<el-form :inline="true" :model="formInline1" class="demo-form-inline">
<div class="app-container">
<div class="header">
<el-form :inline="true" :model="formInline1" >
<el-form-item label="姓名">
<el-input v-model="formInline1.username" placeholder="请输入姓名" />
</el-form-item>
@ -9,9 +9,14 @@
<el-button type="primary" @click="onSubmit">搜索</el-button>
</el-form-item>
</el-form>
<el-table :data="list" style="width: 100%" :border="true" v-loading="loading">
<el-table-column prop="id" width="60" label="id" />
<el-table-column prop="name" label="姓名" min-width="200px">
</div>
<div class="footer">
<el-table
:data="list"
style="width: 100%" :border="true" v-loading="loading">
<el-table-column prop="id" width="60" label="id" align="center"/>
<el-table-column prop="name" label="姓名" min-width="200px" align="center">
<template #default="scope">
<template v-if="scope.row.edit">
<div style="display: flex; align-items: center">
@ -29,19 +34,21 @@
<template v-else>{{ scope.row.name }}</template>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" />
<el-table-column prop="sex" label="性别">
<el-table-column prop="age" label="年龄" align="center"/>
<el-table-column prop="sex" label="性别" align="center">
<template #default="scope">
{{ scope.row.sex ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column prop="price" label="价格" />
<el-table-column prop="admin" label="账号" />
<el-table-column prop="address" label="地址" width="180"/>
<el-table-column prop="date" label="日期" width="180"/>
<el-table-column prop="province" label="省份" width="120"/>
<el-table-column prop="city" label="城市" />
<el-table-column prop="operator" label="操作" width="180px" fixed="right">
<el-table-column prop="price" label="价格" align="center"/>
<el-table-column prop="admin" label="账号" align="center"/>
<el-table-column prop="address"
:show-overflow-tooltip="true"
label="地址" width="180" align="center"/>
<el-table-column prop="date" label="日期" width="180" align="center"/>
<el-table-column prop="province" label="省份" width="120" align="center"/>
<el-table-column prop="city" label="城市" align="center"/>
<el-table-column prop="operator" label="操作" width="180px" fixed="right" align="center">
<template #default="scope">
<el-button
v-if="scope.row.edit"
@ -80,7 +87,7 @@
/>
</div>
</div>
</u-container-layout>
</div>
</template>
<script lang="ts" setup name="inline-table">
import { computed, ref } from 'vue'
@ -99,7 +106,7 @@
img: 'https://img1.baidu.com/it/u=300787145,1214060415&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500',
age: 0,
city: '普陀区',
address: '上海市普上海',
address: `上海市普陀区金沙江路 222${i}`,
zip: 200333,
})
}
@ -121,7 +128,6 @@
return arr.splice((currentPage1.value - 1) * 10, 10)
})
const listLoading = ref(false)
const confirmEdit = (row) => {
row.edit = false
@ -160,16 +166,23 @@
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
.inline-edit-table {
width: 100%;
}
<style scoped lang="scss">
.header{
display: flex;
padding: 16px 16px 0px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
</style>

View File

@ -1,99 +0,0 @@
<template>
<u-container-layout>
<div style="margin-bottom: 15px; display: flex; align-items: center">
<el-input
v-model="input"
placeholder="请输入文件名"
style="width: 200px; margin-right: 10px"
/>
<el-button @click="exportExcelAction" type="primary">
<el-icon style="margin-right: 10px"><document-remove /></el-icon> zip
</el-button>
</div>
<el-table :data="list" style="width: 100%" border>
<template v-for="(item, index) in column" :key="index">
<el-table-column :prop="item.name" :label="item.label" :width="item.width" />
</template>
</el-table>
</u-container-layout>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import * as dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
import { downloadFiles2Zip } from '@/utils/downloadzip'
const data = []
for (let i = 0; i < 10; i++) {
data.push({
date: '2016-05-02',
name: '王五' + i,
price: 1 + i,
province: '上海',
admin: 'admin',
sex: i % 2 ? 1 : 0,
checked: true,
id: i + 1,
age: 0,
city: '普陀区',
address: '上海市普上海',
zip: 200333,
})
}
const column = [
{ name: 'id', label: 'id' },
{ name: 'name', label: '姓名', inSearch: true, valueType: 'input' },
{ name: 'age', label: '年龄', align: 'right' },
{
name: 'sex',
label: '性别',
slot: true,
inSearch: true,
options: [
{
value: 1,
label: '男',
},
{
value: 0,
label: '女',
},
],
valueType: 'select',
},
{
name: 'price',
label: '价格',
inSearch: true,
valueType: 'input',
},
{ name: 'admin', label: '账号', inSearch: true, valueType: 'input' },
{ name: 'address', label: '地址', inSearch: true, valueType: 'input' },
{ name: 'date', label: '日期', sorter: true, inSearch: true, valueType: 'input' },
{ name: 'province', label: '省份' },
{ name: 'city', label: '城市' },
{ name: 'zip', label: '邮编' },
]
const list = ref(data)
const input = ref('')
const format = ref('xlsx')
const exportExcelAction = () => {
downloadFiles2Zip({
zipName: '压缩包',
files: [
{
filename: 'test',
sheets: [
{
sheetName: 'test',
columns: column,
dataSource: data,
},
],
},
],
})
}
</script>
<style></style>