<template>
<div class="login">
<el-card class="login_center">
<template #header>
<div class="card_header">
<span>用户登录</span>
</div>
</template>
<el-form :model="loginFormState" :rules="rules" ref="loginFormRef">
<el-form-item prop="name">
<el-input
prefix-icon="el-icon-user-solid"
v-model.trim="loginFormState.name"
maxlength="32"
placeholder="请输入账号"
clearable
></el-input>
</el-form-item>
<el-form-item prop="pwd">
<el-input
prefix-icon="el-icon-lock"
v-model.trim="loginFormState.pwd"
maxlength="16"
show-password
placeholder="请输入密码"
clearable
@keyup.enter.exact="handleLogin"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%" :loading="loginFormState.loading" @click="handleLogin">登 录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
import { getCurrentInstance, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import { encode } from "js-base64";
?
export default {
setup() {
const { proxy } = getCurrentInstance();
const router = useRouter();
const store = useStore();
const loginFormRef = ref();
?
const loginFormState = reactive({
name: "",
pwd: "",
loading: false
});
?
const rules = {
name: [{ required: true, message: "账号不能为空", trigger: "blur" }],
pwd: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{ min: 5, max: 16, message: "密码长度为5-16位", trigger: "blur" }
]
};
?
const handleLogin = () => {
loginFormRef.value.validate(valid => {
if (!valid) {
return false;
}
?
loginFormState.loading = true;
?
let params = { name: loginFormState.name, pwd: loginFormState.pwd };
?
setTimeout(() => {
let users = { role: loginFormState.name === "admin" ? "admin" : "", username: loginFormState.name };
Object.assign(params, users);
sessionStorage.setItem("jwt", encode(JSON.stringify(params)));
store.dispatch("setUser", params);
loginFormState.loading = false;
router.replace("/");
}, 1000);
?
// proxy.$axios
// .post("/user/login", proxy.$qs.stringify(params))
// .then(res => {
// let { code, result_data, message } = res.data;
// if (code == 1) {
// console.log("login_success", result_data);
// ElMessage.success("登录成功");
// } else {
// ElMessage.error("登录失败:" + message);
// }
// })
// .catch(err => {
// console.log("login err", err);
// ElMessage.error("登录失败");
// });
});
};
?
return { loginFormRef, loginFormState, rules, handleLogin };
}
};
<template>
<el-header height="56px">
<!-- header -->
<div class="header_left">Element-Plus Create By Vite</div>
<div class="header_right">
<!-- 退出全屏、进入全屏按钮 -->
<el-tooltip :content="isFullScreen ? '退出全屏' : '全屏'">
<i class="el-icon-full-screen" @click="handleFullScreen"></i>
</el-tooltip>
<!-- 下拉菜单 -->
<el-dropdown size="medium" @command="handleCommand">
<!-- 用户信息 -->
<div class="user_info">
<!-- 用户头像 -->
<el-avatar :size="36" :src="avatar" />
<!-- 用户名宁 -->
<span class="username">{{ userName }}</span>
</div>
<template #dropdown>
<!-- 折叠菜单 -->
<el-dropdown-menu>
<el-dropdown-item icon="el-icon-user" command="user">个人中心</el-dropdown-item>
<el-dropdown-item icon="el-icon-switch-button" command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
</template>
?
<!-- 二级路由公用路由页面 -->
<template>
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</template>
import { computed, getCurrentInstance, reactive, toRefs } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import screenfull from "screenfull";
import avatar from "@/assets/img/admin.png";
?
export default {
setup() {
const { proxy } = getCurrentInstance();
const router = useRouter();
const store = useStore();
?
const state = reactive({
isFullScreen: false,
avatar,
screenfull
});
const userName = computed(() => store.getters.getUserName);
?
const handleCommand = command => {
if (command === "user") {
router.push("/user");
} else {
proxy.$message.success("退出成功");
store.dispatch("clearUser");
router.replace("/login");
sessionStorage.clear();
localStorage.clear();
}
};
?
const handleFullScreen = () => {
if (screenfull.isEnabled) {
state.isFullScreen = !state.isFullScreen;
screenfull.toggle();
}
};
?
return {
userName,
handleCommand,
handleFullScreen,
...toRefs(state)
};
}
};
注:
<template>
<el-card shadow="never" class="index">
<template #header>
<div class="card_header">
<b>数据列表</b>
</div>
</template>
<el-empty description="暂无数据"></el-empty>
</el-card>
</template>
?
<script></script>
?
<style lang="scss" scoped>
.card_header {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>
注:没有数据时的提示信息;
<template>
<el-card shadow="never" class="index">
<template #header>
<div class="card_header">
<b>?西瓜播放器</b>
</div>
</template>
<div id="xg"></div>
</el-card>
</template>
?
<script>
import { onMounted, onBeforeUnmount, getCurrentInstance, ref } from "vue";
import Player from "xgplayer";
?
export default {
setup() {
const { proxy } = getCurrentInstance();
?
let player;
onMounted(() => {
initPlayer();
});
?
onBeforeUnmount(() => {
player.destroy();
player = null;
});
?
const initPlayer = () => {
player = new Player({
id: "xg",
url: "https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4",
poster: "https://img03.sogoucdn.com/app/a/07/f13b5c3830f02b6db698a2ae43ff6a67",
fitVideoSize: "auto",
fluid: true /* 流式布局 */,
// download: true /* 视频下载 */
// pip: true /* 画中画 */,
// errorTips: `请<span>刷新页面</span>试试` /* 自定义错误提示 */,
lang: "zh-cn"
});
};
?
return {};
}
};
</script>
?
<style lang="scss" scoped>
.card_header {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>
注:
<template>
<div ref="editor" class="editor_ref"></div>
</template>
?
<script>
import { onMounted, onBeforeUnmount, watch, getCurrentInstance, ref } from "vue";
import WEditor from "wangeditor";
?
export default {
props: {
defaultText: { type: String, default: "" }
},
setup(props, context) {
const { proxy } = getCurrentInstance();
const editor = ref();
?
let instance;
onMounted(() => {
initEditor();
});
?
onBeforeUnmount(() => {
instance.destroy();
instance = null;
});
?
watch(
() => props.defaultText,
nv => {
instance.txt.html(nv);
!!nv && context.emit("richHtml", nv);
}
);
?
const initEditor = () => {
instance = new WEditor(editor.value);
// 配置富文本
Object.assign(instance.config, {
zIndex: 100,
// placeholder: "" /* 提示文字 */,
showFullScreen: true /* 显示全屏按钮 */,
showLinkImg: true /* 显示插入网络图片 */,
showLinkVideo: true /* 显示插入网络视频 */,
onchangeTimeout: 400 /* 触发 onchange 的时间频率,默认 200ms */,
uploadImgMaxLength: 1 /* 单次上传图片数量限制 */,
uploadImgMaxSize: 5 * 1024 * 1024 /* 上传图片大小限制 */,
uploadVideoAccept: ["mp4", "mov"] /* 上传视频格式限制 */,
uploadVideoMaxSize: 1024 * 1024 * 1024 /* 上传视频大小限制1024m */,
excludeMenus: ["strikeThrough", "todo", "code"] /* 移除系统菜单 */,
customAlert(msg, type) {
type == "success" ? proxy.$message.success(msg) : proxy.$message.error(msg);
},
customUploadImg(resultFiles, insertImgFn) {
/**
* @param {Object} file - 文件对象
* @param {String} rootPath - 文件根路径(默认为空、例:“filepath/”)
* @param {Array} fileType - 文件类型限制(默认 [] 不限制,例:['.png','.jpeg'])
* @param {Number} size - 文件大小限制(单位:兆、默认 0 不限制、例:1)
**/
proxy.$oss(resultFiles[0]).then(imgUrl => !!imgUrl && insertImgFn(imgUrl));
},
?
customUploadVideo(resultFiles, insertVideoFn) {
proxy.$oss(resultFiles[0]).then(videoUrl => !!videoUrl && insertVideoFn(videoUrl)); /* 参数同上 */
},
onchange(nv) {
context.emit("richHtml", nv);
}
});
instance.create();
};
?
return { editor };
}
};
</script>
?
<style scoped>
div.editor_ref :deep(iframe) {
max-width: 100%;
max-height: auto;
width: 360px;
height: 180px;
}
</style>
<template>
<el-card shadow="never" class="index">
<template #header>
<div class="card_header">
<b>富文本编辑器</b>
</div>
</template>
<!-- 富文本 -->
<WEditor :defaultText="defaultText" @richHtml="getRichHtml" />
</el-card>
</template>
?
<script>
import { onMounted, ref } from "vue";
import WEditor from "../../components/WEditor.vue";
?
export default {
components: { WEditor },
setup() {
const defaultText = ref("");
const richText = ref("");
?
onMounted(() => {
// 初始化数据
defaultText.value = "<h1>Editor</h1>";
});
?
const getRichHtml = nv => {
richText.value = nv;
};
?
return { defaultText, getRichHtml };
}
};
</script>
注:
本篇文章主要介绍使用element-plus进行页面的布局和数据展示处理,后续笔记将继续分享和介绍动态路由菜单的处理,以及用户权限的动态校验。(。??)ノ
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。