Merge branch 'master' of github.com:youlaitech/vue3-element-admin

Former-commit-id: 915c61985daddc357b73c13d44e922246b8c1971
This commit is contained in:
hxr
2023-08-07 23:45:50 +08:00
73 changed files with 1427 additions and 492 deletions

View File

@@ -5,15 +5,12 @@
<div class="title">
业绩柱状图
<el-tooltip effect="dark" content="点击试试下载" placement="bottom">
<i-ep-download
class="download"
@click="downloadEchart"
></i-ep-download>
<i-ep-download class="download" @click="downloadEchart" />
</el-tooltip>
</div>
</template>
<div :id="id" :class="className" :style="{ height, width }" />
<div :id="id" :class="className" :style="{ height, width }"></div>
</el-card>
</template>

View File

@@ -1,96 +1,96 @@
<!-- 漏斗图 -->
<template>
<div :id="id" :class="className" :style="{ height, width }" />
<div :id="id" :class="className" :style="{ height, width }"></div>
</template>
<script setup lang="ts">
import * as echarts from 'echarts';
import * as echarts from "echarts";
const props = defineProps({
id: {
type: String,
default: 'funnelChart'
default: "funnelChart",
},
className: {
type: String,
default: ''
default: "",
},
width: {
type: String,
default: '200px',
required: true
default: "200px",
required: true,
},
height: {
type: String,
default: '200px',
required: true
}
default: "200px",
required: true,
},
});
const options = {
title: {
show: true,
text: '订单线索转化漏斗图',
x: 'center',
text: "订单线索转化漏斗图",
x: "center",
padding: 15,
textStyle: {
fontSize: 18,
fontStyle: 'normal',
fontWeight: 'bold',
color: '#337ecc'
}
fontStyle: "normal",
fontWeight: "bold",
color: "#337ecc",
},
},
grid: {
left: '2%',
right: '2%',
bottom: '10%',
containLabel: true
left: "2%",
right: "2%",
bottom: "10%",
containLabel: true,
},
legend: {
x: 'center',
y: 'bottom',
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
x: "center",
y: "bottom",
data: ["Show", "Click", "Visit", "Inquiry", "Order"],
},
series: [
{
name: 'Funnel',
type: 'funnel',
left: '20%',
name: "Funnel",
type: "funnel",
left: "20%",
top: 60,
bottom: 60,
width: '60%',
sort: 'descending',
width: "60%",
sort: "descending",
gap: 2,
label: {
show: true,
position: 'inside'
position: "inside",
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
type: "solid",
},
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1
borderColor: "#fff",
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
fontSize: 20,
},
},
data: [
{ value: 60, name: 'Visit' },
{ value: 40, name: 'Inquiry' },
{ value: 20, name: 'Order' },
{ value: 80, name: 'Click' },
{ value: 100, name: 'Show' }
]
}
]
{ value: 60, name: "Visit" },
{ value: 40, name: "Inquiry" },
{ value: 20, name: "Order" },
{ value: 80, name: "Click" },
{ value: 100, name: "Show" },
],
},
],
};
onMounted(() => {
@@ -99,7 +99,7 @@ onMounted(() => {
);
chart.setOption(options);
window.addEventListener('resize', () => {
window.addEventListener("resize", () => {
chart.resize();
});
});

View File

@@ -2,69 +2,69 @@
<template>
<el-card>
<template #header> 产品分类饼图 </template>
<div :id="id" :class="className" :style="{ height, width }" />
<div :id="id" :class="className" :style="{ height, width }"></div>
</el-card>
</template>
<script setup lang="ts">
import * as echarts from 'echarts';
import * as echarts from "echarts";
const props = defineProps({
id: {
type: String,
default: 'pieChart'
default: "pieChart",
},
className: {
type: String,
default: ''
default: "",
},
width: {
type: String,
default: '200px',
required: true
default: "200px",
required: true,
},
height: {
type: String,
default: '200px',
required: true
}
default: "200px",
required: true,
},
});
const options = {
grid: {
left: '2%',
right: '2%',
bottom: '10%',
containLabel: true
left: "2%",
right: "2%",
bottom: "10%",
containLabel: true,
},
legend: {
top: 'bottom',
top: "bottom",
textStyle: {
color: '#999'
}
color: "#999",
},
},
series: [
{
name: 'Nightingale Chart',
type: 'pie',
name: "Nightingale Chart",
type: "pie",
radius: [50, 130],
center: ['50%', '50%'],
roseType: 'area',
center: ["50%", "50%"],
roseType: "area",
itemStyle: {
borderRadius: 1,
color: function (params: any) {
//自定义颜色
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
const colorList = ["#409EFF", "#67C23A", "#E6A23C", "#F56C6C"];
return colorList[params.dataIndex];
}
},
},
data: [
{ value: 26, name: '家用电器' },
{ value: 27, name: '户外运动' },
{ value: 24, name: '汽车用品' },
{ value: 23, name: '手机数码' }
]
}
]
{ value: 26, name: "家用电器" },
{ value: 27, name: "户外运动" },
{ value: 24, name: "汽车用品" },
{ value: 23, name: "手机数码" },
],
},
],
};
onMounted(() => {
@@ -72,7 +72,7 @@ onMounted(() => {
document.getElementById(props.id) as HTMLDivElement
);
chart.setOption(options);
window.addEventListener('resize', () => {
window.addEventListener("resize", () => {
chart.resize();
});
});

View File

@@ -2,89 +2,89 @@
<template>
<el-card>
<template #header> 订单状态雷达图 </template>
<div :id="id" :class="className" :style="{ height, width }" />
<div :id="id" :class="className" :style="{ height, width }"></div>
</el-card>
</template>
<script setup lang="ts">
import * as echarts from 'echarts';
import * as echarts from "echarts";
const props = defineProps({
id: {
type: String,
default: 'radarChart'
default: "radarChart",
},
className: {
type: String,
default: ''
default: "",
},
width: {
type: String,
default: '200px',
required: true
default: "200px",
required: true,
},
height: {
type: String,
default: '200px',
required: true
}
default: "200px",
required: true,
},
});
const options = {
grid: {
left: '2%',
right: '2%',
bottom: '10%',
containLabel: true
left: "2%",
right: "2%",
bottom: "10%",
containLabel: true,
},
legend: {
x: 'center',
y: 'bottom',
data: ['预定数量', '下单数量', '发货数量'],
x: "center",
y: "bottom",
data: ["预定数量", "下单数量", "发货数量"],
textStyle: {
color: '#999'
}
color: "#999",
},
},
radar: {
// shape: 'circle',
radius: '60%',
radius: "60%",
indicator: [
{ name: '家用电器' },
{ name: '服装箱包' },
{ name: '运动户外' },
{ name: '手机数码' },
{ name: '汽车用品' },
{ name: '家具厨具' }
]
{ name: "家用电器" },
{ name: "服装箱包" },
{ name: "运动户外" },
{ name: "手机数码" },
{ name: "汽车用品" },
{ name: "家具厨具" },
],
},
series: [
{
name: 'Budget vs spending',
type: 'radar',
name: "Budget vs spending",
type: "radar",
itemStyle: {
borderRadius: 6,
color: function (params: any) {
//自定义颜色
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
const colorList = ["#409EFF", "#67C23A", "#E6A23C", "#F56C6C"];
return colorList[params.dataIndex];
}
},
},
data: [
{
value: [400, 400, 400, 400, 400, 400],
name: '预定数量'
name: "预定数量",
},
{
value: [300, 300, 300, 300, 300, 300],
name: '下单数量'
name: "下单数量",
},
{
value: [200, 200, 200, 200, 200, 200],
name: '发货数量'
}
]
}
]
name: "发货数量",
},
],
},
],
};
onMounted(() => {
@@ -93,7 +93,7 @@ onMounted(() => {
);
chart.setOption(options);
window.addEventListener('resize', () => {
window.addEventListener("resize", () => {
chart.resize();
});
});

View File

@@ -1,27 +1,28 @@
<script setup lang="ts">
import { useUserStore } from "@/store/modules/user";
import { useTransition, TransitionPresets } from "@vueuse/core";
defineOptions({
// eslint-disable-next-line vue/no-reserved-component-names
// eslint-disable-next-line
name: "Dashboard",
inheritAttrs: false,
});
import { useUserStore } from "@/store/modules/user";
import { useTransition, TransitionPresets } from "@vueuse/core";
const userStore = useUserStore();
const date: Date = new Date();
const greetings = computed(() => {
if (date.getHours() >= 6 && date.getHours() < 8) {
const hours = date.getHours();
if (hours >= 6 && hours < 8) {
return "晨起披衣出草堂,轩窗已自喜微凉🌅!";
} else if (date.getHours() >= 8 && date.getHours() < 12) {
} else if (hours >= 8 && hours < 12) {
return "上午好🌞!";
} else if (date.getHours() >= 12 && date.getHours() < 18) {
} else if (hours >= 12 && hours < 18) {
return "下午好☕!";
} else if (date.getHours() >= 18 && date.getHours() < 24) {
} else if (hours >= 18 && hours < 24) {
return "晚上好🌃!";
} else if (date.getHours() >= 0 && date.getHours() < 6) {
} else if (hours >= 0 && hours < 6) {
return "偷偷向银河要了一把碎星,只等你闭上眼睛撒入你的梦中,晚安🌛!";
}
});

177
src/views/demo/icons.vue Normal file
View File

@@ -0,0 +1,177 @@
<template>
<div class="icons-container">
<el-tabs type="border-card">
<el-tab-pane label="Icons">
<div class="grid">
<div
v-for="item of svg_icons"
:key="item"
@click="handleClipboard(generateIconCode(item), $event)"
>
<el-tooltip
effect="dark"
:content="generateIconCode(item)"
placement="top"
>
<div class="icon-item">
<svg-icon :icon-class="item" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Element-UI Icons">
<div class="grid">
<div
v-for="(icon, name) of icons"
:key="name"
@click="handleClipboard(generateElementIconCode(name), $event)"
>
<el-tooltip
effect="dark"
:content="generateElementIconCode(name)"
placement="top"
>
<div class="icon-item">
<el-icon :size="20">
<component :is="icon" />
</el-icon>
<span>{{ name }}</span>
</div>
</el-tooltip>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import SvgIcon from "@/components/SvgIcon/index.vue";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
defineOptions({
// eslint-disable-next-line
name: "Icons",
inheritAttrs: false,
});
const svg_icons: string[] = [
"advert",
"api",
"brand",
"bug",
"cascader",
"chart",
"client",
"close",
"close_all",
"close_left",
"close_other",
"close_right",
"coupon",
"dashboard",
"dict",
"dict_item",
"document",
"download",
"drag",
"edit",
"exit-fullscreen",
"eye-open",
"eye",
"fullscreen",
"github",
"goods-list",
"goods",
"guide",
"homepage",
"lab",
"language",
"link",
"menu",
"message",
"money",
"monitor",
"multi_level",
"nested",
"number",
"order",
"password",
"peoples",
"perm",
"publish",
"rabbitmq",
"rate",
"redis",
"refresh",
"role",
"security",
"shopping",
"size",
"skill",
"system",
"theme",
"tree",
"user",
"uv",
"valid_code",
"verify_code",
];
const icons = ref(ElementPlusIconsVue);
const { text, isSupported, copy } = useClipboard();
function generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`;
}
function generateElementIconCode(symbol) {
return `<el-icon><${symbol} /></el-icon>`;
}
function handleClipboard(text, event) {
// clipboard(text, event);
copy(text)
.then(() => {
ElMessage.success("Copy successfully");
})
.catch(() => {
ElMessage.warning("Copy failed");
});
}
</script>
<style lang="scss" scoped>
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
.grid {
position: relative;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.icon-item {
float: left;
width: 100px;
height: 85px;
margin: 20px;
font-size: 30px;
color: #24292e;
text-align: center;
cursor: pointer;
}
span {
display: block;
margin-top: 10px;
font-size: 16px;
}
.disabled {
pointer-events: none;
}
}
</style>

View File

@@ -15,8 +15,8 @@
<div class="label-wrap" @click="handleClickHead(oneCol)">
<div>{{ oneCol.label }}</div>
<template v-if="oneCol.order">
<i-ep-sort-up v-if="oneCol.order === 'asc'"></i-ep-sort-up>
<i-ep-sort-down v-else></i-ep-sort-down>
<i-ep-sort-up v-if="oneCol.order === 'asc'" />
<i-ep-sort-down v-else />
</template>
</div>
@@ -24,7 +24,7 @@
trigger="click"
@command="(command: string) => handleCommand(command, oneCol)"
>
<i-ep-arrow-down class="action-more"></i-ep-arrow-down>
<i-ep-arrow-down class="action-more" />
<template #dropdown>
<el-dropdown-menu>
<template v-if="oneCol.prop === 'id'">
@@ -56,7 +56,7 @@
action=""
:http-request="handleUploadFile"
>
<i-ep-upload-filled class="icon"></i-ep-upload-filled>
<i-ep-upload-filled class="icon" />
<div class="el-upload__text">拖拽excel到这里</div>
</el-upload>
<div v-loading="loading" class="excel-table-wrap">

View File

@@ -1,6 +1,7 @@
<!-- wangEditor富文本编辑器示例 -->
<script setup lang="ts">
import Editor from "@/components/WangEditor/index.vue";
const value = ref("初始内容");
</script>

View File

@@ -1,18 +1,18 @@
<!-- websocket 示例 -->
<script setup lang="ts">
import { sendToAll, sendToUser } from "@/api/websocket";
import { sendToAll, sendToUser } from "@/api/websocket"; // 点对点消息列表
import { useUserStore } from "@/store/modules/user";
import { useWebSocket } from "@vueuse/core";
const inputVal = ref("初始内容");
const topicMsgs = ref<string[]>(["接收到一条主题消息"]); // 主题消息列表
const p2pMsgs = ref<string[]>(["接收到一条点对线消息"]); // 点对点消息列表
import { useUserStore } from "@/store/modules/user";
const p2pMsgs = ref<string[]>(["接收到一条点对线消息"]);
const userId = useUserStore().userId;
import { useWebSocket } from "@vueuse/core";
const { data, status, close, send, open } = useWebSocket(
"ws://localhost:8989/ws",
{

View File

@@ -1,14 +1,13 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "Page401",
};
</script>
<script setup lang="ts">
import { reactive, toRefs } from "vue";
import { useRouter } from "vue-router";
import { defineComponent } from "vue";
defineComponent({
name: "Page401",
});
const state = reactive({
errGif: new URL(`../../assets/401_images/401.gif`, import.meta.url).href,

View File

@@ -0,0 +1,27 @@
<template>
<div>
<div style="margin-bottom: 15px">Your roles: {{ roles }}</div>
Switch roles:
<el-radio-group v-model="switchRoles">
<el-radio-button label="EDITOR" />
<el-radio-button label="ADMIN" />
</el-radio-group>
</div>
</template>
<script setup lang="ts">
import { useUserStoreHook } from "@/store/modules/user";
import { storeToRefs } from "pinia";
const emit = defineEmits(["change"]);
const store = storeToRefs(useUserStoreHook());
const { roles } = store;
const switchRoles = computed({
get: () => roles.value[0],
set: (val) => {
roles.value = [val];
emit("change");
},
});
</script>

View File

@@ -0,0 +1,21 @@
<template>
<div class="app-container">
<switch-roles @change="handleRolesChange" />
</div>
</template>
<script setup lang="ts">
import router from "@/router";
import SwitchRoles from "./components/SwitchRoles.vue";
defineOptions({
// eslint-disable-next-line
name: "PagePermission",
inheritAttrs: false,
});
function handleRolesChange() {
console.log("roles changed");
router.push({ path: "/permission/page?" + new Date() });
}
</script>

View File

@@ -1,9 +1,9 @@
<template>
<div />
<div></div>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
@@ -11,5 +11,5 @@ const router = useRouter();
const { params, query } = route;
const { path } = params;
router.replace({ path: '/' + path, query });
router.replace({ path: "/" + path, query });
</script>

View File

@@ -1,9 +1,4 @@
<script setup lang="ts">
defineOptions({
name: "Dept",
inheritAttrs: false,
});
import {
getDeptForm,
deleteDept,
@@ -15,6 +10,11 @@ import {
import { DeptVO, DeptForm, DeptQuery } from "@/api/dept/types";
defineOptions({
name: "Dept",
inheritAttrs: false,
});
const queryFormRef = ref(ElForm);
const deptFormRef = ref(ElForm);

View File

@@ -1,10 +1,5 @@
<!-- 字典数据 -->
<script setup lang="ts">
defineOptions({
name: "DictData",
inheritAttrs: false,
});
import {
getDictPage,
getDictFormData,
@@ -14,6 +9,11 @@ import {
} from "@/api/dict";
import { DictPageVO, DictForm, DictQuery } from "@/api/dict/types";
defineOptions({
name: "DictData",
inheritAttrs: false,
});
const props = defineProps({
typeCode: {
type: String,
@@ -311,7 +311,7 @@ onMounted(() => {
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea"></el-input>
<el-input v-model="formData.remark" type="textarea" />
</el-form-item>
</el-form>
<template #footer>

View File

@@ -1,10 +1,5 @@
<!--字典类型-->
<script setup lang="ts">
defineOptions({
name: "DictType",
inheritAttrs: false,
});
import {
getDictTypePage,
getDictTypeForm,
@@ -17,6 +12,11 @@ import DictData from "@/views/system/dict/DictData.vue";
import { DictTypePageVO, DictTypeQuery, DictTypeForm } from "@/api/dict/types";
defineOptions({
name: "DictType",
inheritAttrs: false,
});
const queryFormRef = ref(ElForm);
const dataFormRef = ref(ElForm);

View File

@@ -1,10 +1,4 @@
<script setup lang="ts">
defineOptions({
// eslint-disable-next-line vue/no-reserved-component-names
name: "Menu",
inheritAttrs: false,
});
import { MenuQuery, MenuForm, MenuVO } from "@/api/menu/types";
import {
listMenus,
@@ -20,6 +14,12 @@ import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
import SvgIcon from "@/components/SvgIcon/index.vue";
import IconSelect from "@/components/IconSelect/index.vue";
defineOptions({
// eslint-disable-next-line vue/no-reserved-component-names
name: "Menu",
inheritAttrs: false,
});
const queryFormRef = ref(ElForm);
const menuFormRef = ref(ElForm);

View File

@@ -1,9 +1,4 @@
<script setup lang="ts">
defineOptions({
name: "Role",
inheritAttrs: false,
});
import {
getRolePage,
updateRole,
@@ -17,6 +12,11 @@ import { listMenuOptions } from "@/api/menu";
import { RolePageVO, RoleForm, RoleQuery } from "@/api/role/types";
defineOptions({
name: "Role",
inheritAttrs: false,
});
const queryFormRef = ref(ElForm);
const roleFormRef = ref(ElForm);
const menuRef = ref(ElTree);

View File

@@ -2,10 +2,6 @@
/**
* @see {@link https://vuejs.org/api/sfc-script-setup.html#defineoptions}
*/
defineOptions({
name: "User",
inheritAttrs: false,
});
import { UploadFile } from "element-plus";
import {
getUserPage,
@@ -24,6 +20,11 @@ import { listRoleOptions } from "@/api/role";
import { UserForm, UserQuery, UserPageVO } from "@/api/user/types";
defineOptions({
name: "User",
inheritAttrs: false,
});
const deptTreeRef = ref(ElTree); // 部门树
const queryFormRef = ref(ElForm); // 查询表单
const userFormRef = ref(ElForm); // 用户表单
@@ -383,7 +384,7 @@ onMounted(() => {
:filter-node-method="handleDeptFilter"
default-expand-all
@node-click="handleDeptNodeClick"
></el-tree>
/>
</el-card>
</el-col>
@@ -524,7 +525,7 @@ onMounted(() => {
align="center"
prop="createTime"
width="180"
></el-table-column>
/>
<el-table-column label="操作" fixed="right" width="220">
<template #default="scope">
<el-button