Merge remote-tracking branch 'origin/main' into gitee

This commit is contained in:
xiaoxian521 2024-01-21 23:31:02 +08:00
commit 46cec38fe6
17 changed files with 1329 additions and 359 deletions

View File

@ -60,6 +60,11 @@ menus:
hsDatePicker: Date Picker hsDatePicker: Date Picker
hsDateTimePicker: DateTimePicker hsDateTimePicker: DateTimePicker
hsTimePicker: TimePicker hsTimePicker: TimePicker
hsTag: Tag
hsStatistic: Statistic
hsCollapse: Collapse
hsProgress: Progress
hsUpload: File Upload
hsmenus: MultiLevel Menu hsmenus: MultiLevel Menu
hsmenu1: Menu1 hsmenu1: Menu1
hsmenu1-1: Menu1-1 hsmenu1-1: Menu1-1

View File

@ -60,6 +60,11 @@ menus:
hsDatePicker: 日期选择器 hsDatePicker: 日期选择器
hsDateTimePicker: 日期时间选择器 hsDateTimePicker: 日期时间选择器
hsTimePicker: 时间选择器 hsTimePicker: 时间选择器
hsTag: 标签
hsStatistic: 统计组件
hsCollapse: 折叠面板
hsProgress: 进度条
hsUpload: 文件上传
hsmenus: 多级菜单 hsmenus: 多级菜单
hsmenu1: 菜单1 hsmenu1: 菜单1
hsmenu1-1: 菜单1-1 hsmenu1-1: 菜单1-1

View File

@ -54,8 +54,8 @@
"@logicflow/extension": "^1.2.19", "@logicflow/extension": "^1.2.19",
"@pureadmin/descriptions": "^1.2.0", "@pureadmin/descriptions": "^1.2.0",
"@pureadmin/table": "^3.0.1", "@pureadmin/table": "^3.0.1",
"@pureadmin/utils": "^2.4.0", "@pureadmin/utils": "^2.4.3",
"@vueuse/core": "^10.7.1", "@vueuse/core": "^10.7.2",
"@vueuse/motion": "^2.0.0", "@vueuse/motion": "^2.0.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
@ -77,20 +77,20 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinyin-pro": "^3.19.0", "pinyin-pro": "^3.19.3",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"qs": "^6.11.2", "qs": "^6.11.2",
"responsive-storage": "^2.2.0", "responsive-storage": "^2.2.0",
"sortablejs": "^1.15.1", "sortablejs": "^1.15.2",
"swiper": "^11.0.5", "swiper": "^11.0.5",
"typeit": "8.7.1", "typeit": "8.7.1",
"v-contextmenu": "3.0.0", "v-contextmenu": "3.0.0",
"v3-infinite-loading": "^1.3.1", "v3-infinite-loading": "^1.3.1",
"version-rocket": "^1.7.1", "version-rocket": "^1.7.1",
"vue": "^3.4.10", "vue": "3.4.14",
"vue-i18n": "^9.9.0", "vue-i18n": "^9.9.0",
"vue-json-pretty": "^2.3.0", "vue-json-pretty": "^2.3.0",
"vue-pdf-embed": "^1.2.1", "vue-pdf-embed": "1.2.1",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vue-tippy": "^6.4.1", "vue-tippy": "^6.4.1",
"vue-types": "^5.1.1", "vue-types": "^5.1.1",
@ -153,10 +153,10 @@
"svgo": "^3.2.0", "svgo": "^3.2.0",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.11", "vite": "^5.0.12",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "2.0.0", "vite-plugin-fake-server": "^2.1.1",
"vite-plugin-remove-console": "^2.2.0", "vite-plugin-remove-console": "^2.2.0",
"vite-plugin-router-warn": "^1.0.0", "vite-plugin-router-warn": "^1.0.0",
"vite-svg-loader": "^5.1.0", "vite-svg-loader": "^5.1.0",

691
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -9,3 +9,17 @@ type Result = {
export const mapJson = (params?: object) => { export const mapJson = (params?: object) => {
return http.request<Result>("get", "/get-map-info", { params }); return http.request<Result>("get", "/get-map-info", { params });
}; };
/** 文件上传 */
export const formUpload = data => {
return http.request<Result>(
"post",
"https://run.mocky.io/v3/3aa761d7-b0b3-4a03-96b3-6168d4f7467b",
{ data },
{
headers: {
"Content-Type": "multipart/form-data"
}
}
);
};

View File

@ -26,6 +26,15 @@ export default {
title: $t("menus.hsmessage") title: $t("menus.hsmessage")
} }
}, },
{
path: "/components/upload",
name: "PureUpload",
component: () => import("@/views/components/upload/index.vue"),
meta: {
title: $t("menus.hsUpload"),
extraIcon: "IF-pure-iconfont-new svg"
}
},
{ {
path: "/components/date-picker", path: "/components/date-picker",
name: "DatePicker", name: "DatePicker",
@ -107,6 +116,38 @@ export default {
title: $t("menus.hsbutton") title: $t("menus.hsbutton")
} }
}, },
{
path: "/components/progress",
name: "PureProgress",
component: () => import("@/views/components/progress.vue"),
meta: {
title: $t("menus.hsProgress")
}
},
{
path: "/components/tag",
name: "PureTag",
component: () => import("@/views/components/tag.vue"),
meta: {
title: $t("menus.hsTag")
}
},
{
path: "/components/statistic",
name: "Statistic",
component: () => import("@/views/components/statistic.vue"),
meta: {
title: $t("menus.hsStatistic")
}
},
{
path: "/components/collapse",
name: "Collapse",
component: () => import("@/views/components/collapse.vue"),
meta: {
title: $t("menus.hsCollapse")
}
},
{ {
path: "/components/cascader", path: "/components/cascader",
name: "Cascader", name: "Cascader",

View File

@ -7,35 +7,10 @@
font-weight: 400 !important; font-weight: 400 !important;
} }
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
.el-dropdown-menu { .el-dropdown-menu {
padding: 0 !important; padding: 0 !important;
} }
.el-range-separator {
box-sizing: content-box;
}
.is-dark { .is-dark {
z-index: 9999 !important; z-index: 9999 !important;
} }

View File

@ -0,0 +1,86 @@
<script setup lang="ts">
import { ref } from "vue";
defineOptions({
name: "Collapse"
});
const radio = ref();
const collapseRef = ref();
const activeNames = ref(["1", "2", "3", "4", "5"]);
const isOpen = ref(true);
function onClick() {
isOpen.value
? (activeNames.value = [])
: radio.value === "accordion"
? (activeNames.value = ["5"])
: (activeNames.value = ["1", "2", "3", "4", "5"]);
isOpen.value = !isOpen.value;
}
const handleChange = (val: string[]) => {
console.log(val);
};
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<el-space wrap :size="40">
<el-link
v-tippy="{
content: '点击查看详细文档'
}"
href="https://element-plus.org/zh-CN/component/collapse.html"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
折叠面板
</el-link>
</el-space>
</div>
</template>
<p class="mb-2">基础用法</p>
<el-radio-group v-model="radio" class="mb-3">
<el-radio label="">可同时展开多个面板</el-radio>
<el-radio label="accordion">每次只能展开一个面板</el-radio>
</el-radio-group>
<el-button size="small" text bg class="ml-8 mb-1" @click="onClick">
外部触发打开关闭
</el-button>
<el-collapse
ref="collapseRef"
v-model="activeNames"
class="w-[360px]"
:accordion="radio === 'accordion' ? true : false"
@change="handleChange"
>
<el-collapse-item title="周一" name="1">
周一启航新的篇章
</el-collapse-item>
<el-collapse-item title="周二" name="2">
周二律动携手共进
</el-collapse-item>
<el-collapse-item title="周三" name="3">
周三昂扬激情不减
</el-collapse-item>
<el-collapse-item title="周四" name="4">
周四精进事半功倍
</el-collapse-item>
<el-collapse-item name="5">
<template #title>
周五
<IconifyIconOnline
icon="streamline-emojis:beaming-face-with-smiling-eyes"
class="ml-1"
width="30"
/>
</template>
周五喜悦收尾归档
</el-collapse-item>
</el-collapse>
</el-card>
</template>

View File

@ -198,7 +198,7 @@ watch(size, val =>
@change="value1 = ''" @change="value1 = ''"
> >
<el-radio label="">Date</el-radio> <el-radio label="">Date</el-radio>
<el-radio label="YYYY-MM-DD h:m:s a">年月日 时分秒</el-radio> <el-radio label="YYYY-MM-DD HH:mm:ss">年月日 时分秒</el-radio>
<el-radio label="x">时间戳</el-radio> <el-radio label="x">时间戳</el-radio>
</el-radio-group> </el-radio-group>
<br /> <br />

View File

@ -0,0 +1,140 @@
<script setup lang="ts">
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
defineOptions({
name: "PureProgress"
});
const format = percentage => (percentage === 100 ? "Full" : `${percentage}%`);
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<el-link
v-tippy="{
content: '点击查看详细文档'
}"
href="https://element-plus.org/zh-CN/component/progress.html"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
进度条
</el-link>
</div>
</template>
<p class="mb-4">直线进度条动画</p>
<div class="w-1/4">
<el-progress indeterminate :percentage="50" class="mb-4" />
<el-progress
indeterminate
:percentage="100"
:format="format"
class="mb-4"
/>
<el-progress
indeterminate
:percentage="100"
status="success"
class="mb-4"
/>
<el-progress
indeterminate
:percentage="100"
status="warning"
class="mb-4"
/>
<el-progress
indeterminate
:percentage="50"
status="exception"
class="mb-4"
/>
</div>
<p class="mb-4">进度条内显示百分比标识</p>
<div class="w-1/4">
<el-progress
:text-inside="true"
:stroke-width="26"
:percentage="70"
class="mb-4"
/>
<el-progress
:text-inside="true"
:stroke-width="24"
:percentage="100"
status="success"
striped
striped-flow
:duration="70"
class="mb-4"
/>
<el-progress
:text-inside="true"
:stroke-width="22"
:percentage="80"
status="warning"
class="mb-4"
/>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="50"
status="exception"
striped
striped-flow
class="mb-4"
/>
</div>
<p class="mb-4">自定义内容</p>
<div class="w-1/4 demo-progress">
<el-progress :percentage="50">
<el-button text>自定义内容</el-button>
</el-progress>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="50"
status="exception"
>
<span>自定义内容</span>
</el-progress>
<el-progress type="circle" :percentage="100" status="success">
<el-button type="success" :icon="useRenderIcon('ep:check')" circle />
</el-progress>
<el-progress type="dashboard" :percentage="80">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">上升率</span>
</template>
</el-progress>
</div>
</el-card>
</template>
<style scoped>
.percentage-value {
display: block;
margin-top: 10px;
font-size: 28px;
}
.percentage-label {
display: block;
margin-top: 10px;
font-size: 12px;
}
.demo-progress .el-progress--line {
width: 350px;
margin-bottom: 15px;
}
.demo-progress .el-progress--circle {
margin-right: 15px;
}
</style>

View File

@ -0,0 +1,84 @@
<script setup lang="ts">
import { ref } from "vue";
import dayjs from "dayjs";
import ReCol from "@/components/ReCol";
import { useTransition } from "@vueuse/core";
defineOptions({
name: "Statistic"
});
const value = ref(Date.now() + 1000 * 60 * 60 * 7);
const value1 = ref(Date.now() + 1000 * 60 * 60 * 24 * 2);
const value2 = ref(dayjs().add(1, "month").startOf("month"));
const source = ref(0);
const outputValue = useTransition(source, {
duration: 1500
});
source.value = 36000;
function reset() {
value1.value = Date.now() + 1000 * 60 * 60 * 24 * 2;
}
</script>
<template>
<div>
<el-card shadow="never">
<template #header>
<div class="card-header">
<el-link
v-tippy="{
content: '点击查看详细文档'
}"
href="https://element-plus.org/zh-CN/component/statistic.html"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
统计组件
</el-link>
</div>
</template>
<el-row :gutter="24">
<re-col :value="6" :xs="24" :sm="24">
<el-statistic title="需求人数" :value="outputValue" />
</re-col>
<re-col :value="6" :xs="24" :sm="24">
<el-countdown title="距离答疑结束还剩" :value="value" />
</re-col>
<re-col :value="6" :xs="24" :sm="24">
<el-countdown
title="VIP到期时间还剩"
format="HH:mm:ss"
:value="value1"
/>
<el-button class="mt-2" type="primary" text bg @click="reset">
重置
</el-button>
</re-col>
<re-col :value="6" :xs="24" :sm="24">
<el-countdown format="DD天 HH时 mm分 ss秒" :value="value2">
<template #title>
<div style="display: inline-flex; align-items: center">
<IconifyIconOnline icon="ep:calendar" class="mr-2" />
距离下个月还剩
</div>
</template>
</el-countdown>
<div class="mt-2">{{ value2.format("YYYY-MM-DD") }}</div>
</re-col>
</el-row>
</el-card>
</div>
</template>
<style scoped>
.el-col {
text-align: center;
}
</style>

View File

@ -0,0 +1,172 @@
<script setup lang="ts">
import { ref, nextTick } from "vue";
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
defineOptions({
name: "PureTag"
});
const size = ref("default");
const checked1 = ref(false);
const checked2 = ref(false);
const baseTag = ref("dark");
const tagList = ref([
{
type: "",
text: "Default"
},
{
type: "success",
text: "Success"
},
{
type: "info",
text: "Info"
},
{
type: "warning",
text: "Warning"
},
{
type: "danger",
text: "Danger"
}
]);
const handleClose = tag => {
tagList.value.splice(tagList.value.indexOf(tag), 1);
};
const copyTagList = cloneDeep(tagList.value);
function onReset() {
tagList.value = cloneDeep(copyTagList);
}
/** 动态编辑标签 */
const inputValue = ref("");
const dynamicTags = ref(["Tag 1", "Tag 2", "Tag 3"]);
const inputVisible = ref(false);
const InputRef = ref();
const handleDynamicClose = (tag: string) => {
dynamicTags.value.splice(dynamicTags.value.indexOf(tag), 1);
};
const showInput = () => {
inputVisible.value = true;
nextTick(() => {
InputRef.value!.input!.focus();
});
};
const handleInputConfirm = () => {
if (!isAllEmpty(inputValue.value)) {
dynamicTags.value.push(inputValue.value);
}
inputVisible.value = false;
inputValue.value = "";
};
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<el-space wrap :size="40">
<el-link
v-tippy="{
content: '点击查看详细文档'
}"
href="https://element-plus.org/zh-CN/component/tag.html"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
Tag 标签
</el-link>
<el-radio-group v-model="size" size="small">
<el-radio label="large">大尺寸</el-radio>
<el-radio label="default">默认尺寸</el-radio>
<el-radio label="small">小尺寸</el-radio>
</el-radio-group>
</el-space>
</div>
</template>
<p class="mb-2">基础按钮</p>
<el-radio-group v-model="baseTag" class="mb-3">
<el-radio label="dark" />
<el-radio label="light" />
<el-radio label="plain" />
</el-radio-group>
<br />
<el-space class="mb-3">
<el-checkbox
v-if="tagList.length > 0"
v-model="checked1"
label="可移除"
/>
<el-button v-else size="small" text bg class="mr-6" @click="onReset">
重置
</el-button>
<el-button
v-if="checked1 && tagList.length > 0"
size="small"
text
bg
class="mr-6 ml-4"
@click="tagList = []"
>
移除全部
</el-button>
<el-checkbox v-model="checked2" label="圆形" />
</el-space>
<br />
<el-space wrap>
<el-tag
v-for="(tag, index) in tagList"
:key="index"
:type="tag.type as any"
:effect="baseTag as any"
:closable="checked1"
:round="checked2"
:size="size as any"
:disabled="size === 'disabled'"
@close="handleClose(tag)"
>
{{ tag.text }}
</el-tag>
</el-space>
<el-divider />
<p class="mb-2">动态编辑标签</p>
<el-tag
v-for="tag in dynamicTags"
:key="tag"
class="mx-1"
closable
:size="size as any"
:disable-transitions="false"
@close="handleDynamicClose(tag)"
>
{{ tag }}
</el-tag>
<el-input
v-if="inputVisible"
ref="InputRef"
v-model="inputValue"
class="ml-1 !w-20"
size="small"
@keyup.enter="handleInputConfirm"
@blur="handleInputConfirm"
/>
<el-button
v-else
class="button-new-tag ml-1"
size="small"
@click="showInput"
>
新建标签
</el-button>
</el-card>
</template>
<style lang="scss" scoped>
:deep(.el-divider--horizontal) {
margin: 17px 0;
}
</style>

View File

@ -0,0 +1,94 @@
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { formUpload } from "@/api/mock";
import { message } from "@/utils/message";
import { createFormData } from "@pureadmin/utils";
import UploadIcon from "@iconify-icons/ri/upload-2-line";
const formRef = ref();
const uploadRef = ref();
const validateForm = reactive({
fileList: [],
date: ""
});
const submitForm = formEl => {
if (!formEl) return;
formEl.validate(valid => {
if (valid) {
// file
const formData = createFormData({
files: validateForm.fileList.map(file => ({ raw: file.raw })), // file
date: validateForm.date //
});
formUpload(formData)
.then(({ success }) => {
if (success) {
message("提交成功", { type: "success" });
} else {
message("提交失败");
}
})
.catch(error => {
message(`提交异常 ${error}`, { type: "error" });
});
} else {
return false;
}
});
};
const resetForm = formEl => {
if (!formEl) return;
formEl.resetFields();
};
</script>
<template>
<el-form ref="formRef" :model="validateForm" label-width="82px">
<el-form-item
label="附件"
prop="fileList"
:rules="[{ required: true, message: '附件不能为空' }]"
>
<el-upload
ref="uploadRef"
v-model:file-list="validateForm.fileList"
drag
multiple
action="#"
class="!w-[200px]"
:auto-upload="false"
>
<div class="el-upload__text">
<IconifyIconOffline
:icon="UploadIcon"
width="26"
class="m-auto mb-2"
/>
可点击或拖拽上传
</div>
</el-upload>
</el-form-item>
<el-form-item
label="日期"
prop="date"
:rules="[{ required: true, message: '日期不能为空' }]"
>
<el-date-picker
v-model="validateForm.date"
type="datetime"
class="!w-[200px]"
placeholder="请选择日期时间"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" text bg @click="submitForm(formRef)">
提交
</el-button>
<el-button text bg @click="resetForm(formRef)">重置</el-button>
</el-form-item>
</el-form>
</template>

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View File

@ -0,0 +1,313 @@
<script setup lang="ts">
import axios from "axios";
import Sortable from "sortablejs";
import UploadForm from "./form.vue";
import { ref, computed } from "vue";
import { useRouter } from "vue-router";
import { message } from "@/utils/message";
import type { UploadFile } from "element-plus";
import { getKeyList, extractFields, downloadByData } from "@pureadmin/utils";
import Add from "@iconify-icons/ep/plus";
import Eye from "@iconify-icons/ri/eye-line";
import Delete from "@iconify-icons/ri/delete-bin-7-line";
defineOptions({
name: "PureUpload"
});
const fileList = ref([]);
const router = useRouter();
const curOpenImgIndex = ref(0);
const dialogVisible = ref(false);
const urlList = computed(() => getKeyList(fileList.value, "url"));
const imgInfos = computed(() => extractFields(fileList.value, "name", "size"));
const getImgUrl = name => new URL(`./imgs/${name}.jpg`, import.meta.url).href;
const srcList = Array.from({ length: 3 }).map((_, index) => {
return getImgUrl(index + 1);
});
/** 上传文件前校验 */
const onBefore = file => {
if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) {
message("只能上传图片");
return false;
}
const isExceed = file.size / 1024 / 1024 > 2;
if (isExceed) {
message(`单个图片大小不能超过2MB`);
return false;
}
};
/** 超出最大上传数时触发 */
const onExceed = () => {
message("最多上传3张图片请先删除在上传");
};
/** 移除上传的文件 */
const handleRemove = (file: UploadFile) => {
fileList.value.splice(fileList.value.indexOf(file), 1);
};
/** 大图预览 */
const handlePictureCardPreview = (file: UploadFile) => {
curOpenImgIndex.value = fileList.value.findIndex(img => img.uid === file.uid);
dialogVisible.value = true;
};
const getUploadItem = () => document.querySelectorAll("#pure-upload-item");
/** 缩略图拖拽排序 */
const imgDrop = uid => {
const CLASSNAME = "el-upload-list";
const _curIndex = fileList.value.findIndex(img => img.uid === uid);
getUploadItem()?.[_curIndex]?.classList?.add(`${CLASSNAME}__item-actions`);
const wrapper: HTMLElement = document.querySelector(`.${CLASSNAME}`);
Sortable.create(wrapper, {
handle: `.${CLASSNAME}__item`,
onEnd: ({ newIndex, oldIndex }) => {
const oldFile = fileList.value[oldIndex];
fileList.value.splice(oldIndex, 1);
fileList.value.splice(newIndex, 0, oldFile);
// fix: https://github.com/SortableJS/Sortable/issues/232 (firefox is ok, but chromium is bad. see https://bugs.chromium.org/p/chromium/issues/detail?id=410328)
getUploadItem().forEach(ele => {
ele.classList.remove(`${CLASSNAME}__item-actions`);
});
}
});
};
/** 下载图片 */
const onDownload = () => {
[
{ name: "巴旦木.jpeg", type: "img" },
{ name: "恭喜发财.png", type: "img" },
{ name: "可爱动物.gif", type: "gif" },
{ name: "pure-upload.csv", type: "other" },
{ name: "pure-upload.txt", type: "other" }
].forEach(img => {
axios
.get(`https://xiaoxian521.github.io/hyperlink/${img.type}/${img.name}`, {
responseType: "blob"
})
.then(({ data }) => {
downloadByData(data, img.name);
});
});
};
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<el-link
v-tippy="{
content: '点击查看详细文档'
}"
href="https://element-plus.org/zh-CN/component/upload.html"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
文件上传
</el-link>
<span class="header-right">
<el-button class="ml-1" text bg @click="onDownload">
点击下载安全文件进行上传测试
</el-button>
</span>
</div>
</template>
<p class="mb-4">
综合示例<span class="text-[14px]">
<span class="text-[red]">自动上传</span>
拖拽上传拖拽排序设置请求头上传进度大图预览多选文件最大文件数量文件类型限制文件大小限制删除文件
</span>
</p>
<p v-show="fileList.length > 0" class="mb-4">
{{ imgInfos }}
</p>
<el-upload
v-model:file-list="fileList"
drag
multiple
class="pure-upload"
list-type="picture-card"
accept="image/jpeg,image/png,image/gif"
action="https://run.mocky.io/v3/3aa761d7-b0b3-4a03-96b3-6168d4f7467b"
:limit="3"
:headers="{ Authorization: 'eyJhbGciOiJIUzUxMiJ9.admin' }"
:on-exceed="onExceed"
:before-upload="onBefore"
>
<IconifyIconOffline :icon="Add" class="m-auto mt-4" width="30" />
<template #file="{ file }">
<div
v-if="file.status == 'ready' || file.status == 'uploading'"
class="mt-[35%] m-auto"
>
<p class="font-medium">文件上传中</p>
<el-progress
class="mt-2"
:stroke-width="2"
:text-inside="true"
:show-text="false"
:percentage="file.percentage"
/>
</div>
<div v-else @mouseenter.stop="imgDrop(file.uid)">
<img
class="el-upload-list__item-thumbnail select-none"
:src="file.url"
/>
<span
id="pure-upload-item"
:class="[
'el-upload-list__item-actions',
fileList.length > 1 && '!cursor-move'
]"
>
<span
title="查看"
class="hover:text-primary"
@click="handlePictureCardPreview(file)"
>
<IconifyIconOffline
:icon="Eye"
class="hover:scale-125 duration-100"
/>
</span>
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<span title="移除" class="hover:text-[var(--el-color-danger)]">
<IconifyIconOffline
:icon="Delete"
class="hover:scale-125 duration-100"
/>
</span>
</span>
</span>
</div>
</template>
</el-upload>
<!-- 有时文档没写并不代表没有多看源码好处多多😝 https://github.com/element-plus/element-plus/tree/dev/packages/components/image-viewer/src emm...西🥹giao -->
<el-image-viewer
v-if="dialogVisible"
:initialIndex="curOpenImgIndex"
:url-list="urlList"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
@close="dialogVisible = false"
@switch="index => (curOpenImgIndex = index)"
/>
<!-- 将自定义内容插入到body里有了它在图片预览的时候想插入个分页器或者别的东东在预览区某个位置就很方便咯用户需求可以很灵活开源组件库几乎不可能尽善尽美很多时候寻找别的解决途径或许更好 -->
<teleport to="body">
<div
v-if="fileList[curOpenImgIndex] && dialogVisible"
effect="dark"
round
size="large"
type="info"
class="img-name"
>
<p class="text-[#fff] dark:text-black">
{{ fileList[curOpenImgIndex].name }}
</p>
</div>
</teleport>
<p class="el-upload__tip">
可拖拽上传最多3张单个不超过2MB且格式为jpeg/png/gif的图片
</p>
<el-divider />
<p class="mb-4 mt-4">
结合表单校验进行<span class="text-[red]">手动上传</span>
<span class="text-[14px]">
可先打开浏览器控制台找到Network然后填写表单内容后点击点提交观察请求变化
</span>
</p>
<div class="flex justify-between">
<UploadForm />
<div>
<p class="text-center">上传接口相关截图</p>
<el-image
class="w-[200px] rounded-md"
:src="srcList[0]"
:preview-src-list="srcList"
fit="cover"
/>
</div>
</div>
<el-divider />
<div class="flex flex-wrap">
<p>
裁剪上传头像请参考
<span
class="font-bold text-[18x] cursor-pointer hover:text-[red]"
@click="router.push({ name: 'SystemUser' })"
>
系统管理-用户管理
</span>
表格操作栏中的上传头像功能
</p>
<p class="text-[red] text-[12px] flex flex-auto items-center justify-end">
免责声明上传接口使用免费开源的
<el-link
href="https://designer.mocky.io/"
target="_blank"
style="font-size: 16px; font-weight: 800"
>
&nbsp;Mocky&nbsp;
</el-link>
<span class="font-bold text-[18x]"> 请不要上传重要信息 </span
>如果造成任何损失我们概不负责
</p>
</div>
</el-card>
</template>
<style lang="scss" scoped>
:deep(.card-header) {
display: flex;
.header-right {
display: flex;
flex: auto;
align-items: center;
justify-content: flex-end;
font-size: 14px;
}
}
:deep(.pure-upload) {
.el-upload-dragger {
background-color: transparent;
border: none;
}
}
.img-name {
position: absolute;
bottom: 80px;
left: 50%;
z-index: 4000;
padding: 5px 23px;
background-color: var(--el-text-color-regular);
border-radius: 22px;
transform: translateX(-50%);
/** left: 50%; bottom: 80px; transform: translateX(-50%);
* 解开下面 left: 40px; top: 40px; 注释体验不一样的感觉还是差强人意自己调整位置吧🥹
*/
// left: 40px;
// top: 40px;
}
</style>