Merge branch 'abp' of https://gitee.com/ccnetcore/Yi into abp

This commit is contained in:
橙子
2024-01-02 23:35:34 +08:00
11 changed files with 435 additions and 260 deletions

View File

@@ -0,0 +1,11 @@
import request from "@/config/axios/service";
/**
* 获取图标列表
*/
export function getIconList() {
return request({
url: `/setting/icon`,
method: "get",
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,190 +1,194 @@
// @import './variables.module.scss'; /**
// @import './mixin.scss'; * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
// @import './transition.scss'; * http://cssreset.com
// @import './element-ui.scss'; * 全局初始化样式
// @import './sidebar.scss'; */
// @import './btn.scss'; html,
// @import './ruoyi.scss'; body,
div,
// body { span,
// height: 100%; applet,
// margin: 0; object,
// -moz-osx-font-smoothing: grayscale; iframe,
// -webkit-font-smoothing: antialiased; p,
// text-rendering: optimizeLegibility; blockquote,
// font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; pre,
// } a,
abbr,
// label { acronym,
// font-weight: 700; address,
// } big,
cite,
// html { code,
// height: 100%; del,
// box-sizing: border-box; dfn,
// } em,
img,
// #app { ins,
// height: 100%; kbd,
// } q,
s,
// *, samp,
// *:before, small,
// *:after { strike,
// box-sizing: inherit; strong,
// } sub,
sup,
// .no-padding { tt,
// padding: 0px !important; var,
// } b,
u,
// .padding-content { i,
// padding: 4px 0; center,
// } dl,
dt,
// a:focus, dd,
// a:active { ol,
// outline: none; ul,
// } li,
fieldset,
// a, form,
// a:focus, label,
// a:hover { legend,
// cursor: pointer; table,
// color: inherit; caption,
// text-decoration: none; tbody,
// } tfoot,
thead,
// div:focus { tr,
// outline: none; th,
// } td,
article,
// .fr { aside,
// float: right; canvas,
// } details,
embed,
// .fl { figure,
// float: left; figcaption,
// } footer,
header,
// .pr-5 { menu,
// padding-right: 5px; nav,
// } output,
ruby,
// .pl-5 { section,
// padding-left: 5px; summary,
// } time,
mark,
// .block { audio,
// display: block; video,
// } input {
margin: 0;
// .pointer { padding: 0;
// cursor: pointer; border: 0;
// } font-size: 100%;
font-weight: normal;
// .inlineBlock { vertical-align: baseline;
// display: block; outline: none;
// }
// .clearfix {
// &:after {
// visibility: hidden;
// display: block;
// font-size: 0;
// content: " ";
// clear: both;
// height: 0;
// }
// }
// aside {
// background: #eef1f6;
// padding: 8px 24px;
// margin-bottom: 20px;
// border-radius: 2px;
// display: block;
// line-height: 32px;
// font-size: 16px;
// font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
// color: #2c3e50;
// -webkit-font-smoothing: antialiased;
// -moz-osx-font-smoothing: grayscale;
// a {
// color: #337ab7;
// cursor: pointer;
// &:hover {
// color: rgb(32, 160, 255);
// }
// }
// }
// .components-container {
// margin: 30px 50px;
// position: relative;
// }
// .pagination-container {
// margin-top: 30px;
// }
.text-center {
text-align: center
} }
// .sub-navbar { body {
// height: 50px; /* px-to-viewport-ignore-next */
// line-height: 50px; min-width: 1024px; /* px-to-viewport-ignore */
// position: relative; }
// width: 100%; html {
// text-align: right; /* px-to-viewport-ignore-next */
// padding-right: 20px; min-width: 1024px; /* px-to-viewport-ignore */
// transition: 600ms ease position; }
// background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
// .subtitle { /* HTML5 display-role reset for older browsers */
// font-size: 20px; article,
// color: #fff; aside,
// } details,
figcaption,
figure,
footer,
header,
menu,
nav,
section {
display: block;
}
// &.draft { blockquote,
// background: #d0d0d0; q {
// } quotes: none;
}
// &.deleted { blockquote::before,
// background: #d0d0d0; blockquote::after,
// } q::before,
// } q::after {
content: none;
}
// .link-type, table {
// .link-type:focus { border-collapse: collapse;
// color: #337ab7; border-spacing: 0;
// cursor: pointer; }
// &:hover { /* custom */
// color: rgb(32, 160, 255); li {
// } list-style: none;
// } }
// .filter-container { button {
// padding-bottom: 10px; outline: none;
cursor: pointer;
background-color: transparent;
border-style: none;
}
// .filter-item { .el-textarea__inner::placeholder {
// display: inline-block; color: #999 !important;
// vertical-align: middle; }
// margin-bottom: 10px;
// }
// }
// //refine vue-multiselect plugin .el-input__inner::placeholder {
// .multiselect { color: #999 !important;
// line-height: 16px; }
// }
// .multiselect--active { input::-webkit-input-placeholder {
// z-index: 1000 !important; color: #999 !important;
// } }
.el-input__inner::-webkit-input-placeholder {
color: #999 !important;
}
input::placeholder {
color: #999 !important;
}
textarea::placeholder {
color: #999 !important;
}
* {
box-sizing: border-box;
}
html,
body,
#app {
width: 100%;
height: 100%;
font-family: Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbo;
}
/* //滚动条样式 */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
border-radius: 10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #ccc;
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(249, 222, 222, 0.6);
}

View File

@@ -117,17 +117,17 @@ const Init = () => {
const statusTypeList = [ const statusTypeList = [
{ {
label: "正常", label: "正常",
value: 0, value: "Normal",
type: "success", type: "success",
}, },
{ {
label: "危险", label: "危险",
value: 1, value: "Dangerous",
type: "warning", type: "warning",
}, },
{ {
label: "已禁止", label: "已禁止",
value: 2, value: "Ban",
type: "danger", type: "danger",
}, },
]; ];

View File

@@ -1,38 +1,59 @@
<template> <template>
<div class="botton-div"> <div class="botton-div">
<a><el-icon><UserFilled /></el-icon>站长{{configStore.author}}</a> <a
<a><el-icon><Search /></el-icon>{{configStore.bottom}}</a> ><el-icon><UserFilled /></el-icon>站长{{ configStore.author }}</a
<a><el-icon><View /></el-icon>关于本站</a> >
<a><el-icon><Message /></el-icon>建议反馈</a> <a
<p></p> ><el-icon><Search /></el-icon>{{ configStore.bottom }}</a
<a ><el-icon><Position /></el-icon>{{configStore.icp}}</a> >
</div> <a
><el-icon><View /></el-icon>关于本站</a
>
<a @click="handleContact"
><el-icon><Link /></el-icon>联系我们</a
>
<a
><el-icon><Message /></el-icon>建议反馈</a
>
<p></p>
<a
><el-icon><Position /></el-icon>{{ configStore.icp }}</a
>
</div>
</template> </template>
<script setup> <script setup>
import useConfigStore from "@/stores/config"; import useConfigStore from "@/stores/config";
const configStore= useConfigStore(); import { useRouter } from "vue-router";
const router = useRouter();
const configStore = useConfigStore();
// 联系我们跳转对应页面
const handleContact = () => {
router.push("/contact");
};
</script> </script>
<style scoped> <style scoped>
.el-icon .el-icon {
{margin: 0 0.2rem;} margin: 0 0.2rem;
a{ }
margin-right: 2rem; a {
line-height: 1.8rem; margin-right: 2rem;
line-height: 1.8rem;
} }
a:hover { a:hover {
color: #40a9ff; color: #40a9ff;
cursor:pointer; cursor: pointer;
} }
.botton-div .botton-div {
{ background: transparent;
background: transparent; color: rgba(0, 0, 0, 0.45);
color: rgba(0,0,0,.45); font-size: 14px;
font-size: 14px; display: flex;
display: flex; flex-wrap: wrap;
flex-wrap: wrap; height: auto;
height: auto; width: auto;
width: auto; justify-content: center;
justify-content: center; margin: 0.5rem auto;
margin: 0.5rem auto;
} }
</style> </style>

View File

@@ -63,6 +63,11 @@
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<div class="author" @click="handleGitClick">
<el-tooltip effect="dark" content="在gitee找到我们" placement="bottom">
<img src="@/assets/common/icons/gitee.png" alt="" />
</el-tooltip>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -119,19 +124,33 @@ const search = () => {
}; };
const isLogin = getToken("AccessToken") ? true : false; const isLogin = getToken("AccessToken") ? true : false;
const handleGitClick = () => {
window.open("https://gitee.com/ccnetcore/Yi");
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.header { .header {
padding: 0 100px;
width: 1300px; width: 1300px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.user { .user {
display: flex; display: flex;
align-items: center; align-items: center;
.author {
cursor: pointer;
width: 25px;
height: 25px;
margin-left: 20px;
img {
width: 100%;
height: 100%;
}
}
.el-dropdown-link { .el-dropdown-link {
cursor: pointer; cursor: pointer;
display: flex; display: flex;

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="common-layout"> <div class="common-layout">
<el-container> <el-container class="common-container">
<el-header <el-header
class="common-header" class="common-header"
ref="header" ref="header"
@@ -46,6 +46,10 @@ const handleScroll = () => {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
&-container {
width: 100%;
height: 100%;
}
&-header { &-header {
width: 100%; width: 100%;
background-color: #fff; background-color: #fff;

View File

@@ -47,17 +47,17 @@ const props = defineProps({
const statusTypeList = [ const statusTypeList = [
{ {
label: "正常", label: "正常",
value: 0, value: "Normal",
type: "success", type: "success",
}, },
{ {
label: "危险", label: "危险",
value: 1, value: "Dangerous",
type: "warning", type: "warning",
}, },
{ {
label: "已禁止", label: "已禁止",
value: 2, value: "Ban",
type: "danger", type: "danger",
}, },
]; ];

View File

@@ -45,17 +45,17 @@ const props = defineProps({
const statusTypeList = [ const statusTypeList = [
{ {
label: "正常", label: "正常",
value: 0, value: "Normal",
type: "success", type: "success",
}, },
{ {
label: "危险", label: "危险",
value: 1, value: "Dangerous",
type: "warning", type: "warning",
}, },
{ {
label: "已禁止", label: "已禁止",
value: 2, value: "Ban",
type: "danger", type: "danger",
}, },
]; ];

View File

@@ -126,4 +126,9 @@ onMounted(() => {
.app-container { .app-container {
padding: 20px; padding: 20px;
} }
.text-center {
display: flex;
justify-content: center;
margin-bottom: 10px;
}
</style> </style>

View File

@@ -1,12 +1,28 @@
<template> <template>
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" /> <div class="user-info-head" @click="editCropper()">
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
</div> </div>
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog"> <el-dialog
:title="title"
v-model="open"
width="800px"
append-to-body
@opened="modalOpened"
@close="closeDialog"
>
<el-row> <el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop" <vue-cropper
:autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox" ref="cropper"
@realTime="realTime" v-if="visible" /> :img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
@realTime="realTime"
v-if="visible"
/>
</el-col> </el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview"> <div class="avatar-upload-preview">
@@ -16,16 +32,28 @@
</el-row> </el-row>
<br /> <br />
<el-row> <el-row>
<el-col :lg="2" :md="2"> <el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload"> <el-upload
action="#"
:http-request="requestUpload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<el-button> <el-button>
选择 本地选择
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
<Upload /> <Upload />
</el-icon> </el-icon>
</el-button> </el-button>
</el-upload> </el-upload>
</el-col> </el-col>
<el-col :lg="{ span: 1, offset: 3 }" :md="2">
<el-button @click="handleSelectOnline"
>在线选择<el-icon class="el-icon--right"> <Upload /> </el-icon>
</el-button>
</el-col>
</el-row>
<el-row style="margin-top: 10px">
<el-col :lg="{ span: 1, offset: 2 }" :md="2"> <el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button> <el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col> </el-col>
@@ -38,22 +66,49 @@
<el-col :lg="{ span: 1, offset: 1 }" :md="2"> <el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button> <el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col> </el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2"> <el-col :lg="{ span: 2, offset: 8 }" :md="2">
<el-button type="primary" @click="uploadImg()">上传</el-button> <el-button type="primary" @click="uploadImg()">上传</el-button>
</el-col> </el-col></el-row
</el-row> >
</el-dialog>
<!-- 选择已有弹窗 -->
<el-dialog
v-model="isOnlineVisible"
title="在线选择"
append-to-body
width="800px"
>
<div class="imageList">
<template v-for="(item, index) in iconList">
<el-image
style="width: 100px; height: 100px"
:src="item"
:fit="fit"
:class="{ selected: index === selectedImageIndex, imageBox: true }"
@click="selectImage(index)"
/>
</template>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="isOnlineVisible = false">取消</el-button>
<el-button type="primary" @click="confirmImage"> 确认 </el-button>
</span>
</template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import "vue-cropper/dist/index.css"; import "vue-cropper/dist/index.css";
import { ref, reactive, onMounted } from "vue";
import { VueCropper } from "vue-cropper"; import { VueCropper } from "vue-cropper";
import { upload } from "@/apis/fileApi"; import { upload } from "@/apis/fileApi";
import { updateUserIcon } from "@/apis/userApi"; import { updateUserIcon } from "@/apis/userApi";
import useUserStore from '@/stores/user' import { getIconList } from "@/apis/settingApi";
import { ref, reactive } from "vue"; import useUserStore from "@/stores/user";
import axios from "axios";
const userStore = useUserStore() const userStore = useUserStore();
const cropper = ref(null); const cropper = ref(null);
@@ -68,79 +123,125 @@ const options = reactive({
autoCropWidth: 200, // 默认生成截图框宽度 autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度 autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变 fixedBox: true, // 固定截图框大小 不允许改变
previews: {} //预览数据 previews: {}, //预览数据
}); });
/** 编辑头像 */ /** 编辑头像 */
function editCropper() { function editCropper() {
open.value = true; open.value = true;
}; }
/** 打开弹出层结束时的回调 */ /** 打开弹出层结束时的回调 */
function modalOpened() { function modalOpened() {
visible.value = true; visible.value = true;
}; }
/** 覆盖默认上传行为 */ /** 覆盖默认上传行为 */
function requestUpload() { function requestUpload() {}
};
/** 向左旋转 */ /** 向左旋转 */
function rotateLeft() { function rotateLeft() {
cropper.value.rotateLeft(); cropper.value.rotateLeft();
}; }
/** 向右旋转 */ /** 向右旋转 */
function rotateRight() { function rotateRight() {
cropper.value.rotateRight(); cropper.value.rotateRight();
}; }
/** 图片缩放 */ /** 图片缩放 */
function changeScale(num) { function changeScale(num) {
num = num || 1; num = num || 1;
cropper.value.changeScale(num); cropper.value.changeScale(num);
}; }
/** 上传预处理 */ /** 上传预处理 */
function beforeUpload(file) { function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) { if (file.type.indexOf("image/") == -1) {
ElMessage.error("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。") ElMessage.error("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。");
} else { } else {
console.log(file, "file");
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
reader.onload = () => { reader.onload = () => {
options.img = reader.result; options.img = reader.result;
}; };
} }
}; }
/** 上传图片 */ /** 上传图片 */
async function uploadImg() { async function uploadImg() {
await cropper.value.getCropBlob(async data => { await cropper.value.getCropBlob(async (data) => {
let formData = new FormData(); let formData = new FormData();
formData.append("file", data); formData.append("file", data);
const response = await upload(formData) const response = await upload(formData);
open.value = false; open.value = false;
options.img = import.meta.env.VITE_APP_BASEAPI + "/file/" + response.data[0].id; options.img =
import.meta.env.VITE_APP_BASEAPI + "/file/" + response.data[0].id;
userStore.icon = options.img; userStore.icon = options.img;
const iconResponse= await updateUserIcon(response.data[0].id); const iconResponse = await updateUserIcon(response.data[0].id);
if(iconResponse.status==200) if (iconResponse.status == 200) {
{ ElMessage({
ElMessage({ type: "success",
type: "success", message: "头像更新成功",
message: "头像更新成功", });
}); }
}
}); });
}; }
/** 实时预览 */ /** 实时预览 */
function realTime(data) { function realTime(data) {
options.previews = data; options.previews = data;
}; }
/** 关闭窗口 */ /** 关闭窗口 */
function closeDialog() { function closeDialog() {
options.img = userStore.icon; options.img = userStore.icon;
options.visible = false; options.visible = false;
}
// 在线选择相关逻辑
const saveOnlineImage = async (imageUrl) => {
try {
// 发起网络请求下载在线图片
const response = await axios.get(imageUrl, {
responseType: "blob", // 设置响应类型为blob
});
// 创建blob对象
const blob = new Blob([response.data], {
type: response.headers["content-type"],
});
// 创建File对象设置文件名为当前时间戳
const file = new File([blob], `${Date.now()}.jpg`, {
type: response.headers["content-type"],
});
// 可以将file对象传递给其他处理逻辑或进行文件上传操作
return file;
} catch (error) {
console.error("下载图片失败:", error);
}
}; };
const iconList = ref([]);
const isOnlineVisible = ref(false);
const handleSelectOnline = () => {
isOnlineVisible.value = true;
};
const selectedImageIndex = ref(null);
const selectImage = (index) => {
selectedImageIndex.value = index;
};
const confirmImage = async () => {
if (selectedImageIndex !== null) {
// 在预览区域中显示选择的图片
const selectedImage = iconList.value[selectedImageIndex.value];
const file = await saveOnlineImage(selectedImage);
beforeUpload(file);
}
selectedImageIndex.value = null;
isOnlineVisible.value = false;
};
onMounted(async () => {
const { data: iconListData } = await getIconList();
iconList.value = iconListData.map(
(item) => import.meta.env.VITE_APP_BASEAPI + "/" + item
);
});
</script> </script>
<style lang="scss" scoped>
<style lang='scss' scoped>
.user-info-head { .user-info-head {
position: relative; position: relative;
display: inline-block; display: inline-block;
@@ -149,6 +250,7 @@ function closeDialog() {
.user-info-head:hover:after { .user-info-head:hover:after {
content: "+"; content: "+";
text-align: center;
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
@@ -184,4 +286,13 @@ function closeDialog() {
box-shadow: 0 0 4px #ccc; box-shadow: 0 0 4px #ccc;
overflow: hidden; overflow: hidden;
} }
</style>
/* 添加选中图片的边框样式 */
.imageBox {
margin: 10px;
}
.selected {
padding: 5px;
border: 2px solid #40a9ff;
}
</style>