parent
7160ed97ac
commit
ca7751ce6a
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
// Place your shop-admin 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||||
|
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||||
|
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||||
|
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||||
|
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||||
|
// Placeholders with the same ids are connected.
|
||||||
|
// Example:
|
||||||
|
// "Print to console": {
|
||||||
|
// "scope": "javascript,typescript",
|
||||||
|
// "prefix": "log",
|
||||||
|
// "body": [
|
||||||
|
// "console.log('$1');",
|
||||||
|
// "$2"
|
||||||
|
// ],
|
||||||
|
// "description": "Log output to console"
|
||||||
|
// }
|
||||||
|
"import element plus component": {
|
||||||
|
"scope": "javascript,typescript",
|
||||||
|
"prefix": "import ele",
|
||||||
|
"body": [
|
||||||
|
"import { ${1:El} } from 'element-plus/es/components/$2/index';",
|
||||||
|
"import 'element-plus/es/components/$2/style/css';"
|
||||||
|
],
|
||||||
|
"description": "按需引用ElementPlus组件及其样式"
|
||||||
|
},
|
||||||
|
"vbase extra component": {
|
||||||
|
"scope": "vue",
|
||||||
|
"prefix": "vbase extra",
|
||||||
|
"body": [
|
||||||
|
"<template>\n<component :is=\"render\" />\n</template>",
|
||||||
|
"<script setup lang=\"jsx\">",
|
||||||
|
"import { ${1:El} } from 'element-plus/es/components/$2/index';",
|
||||||
|
"import 'element-plus/es/components/$2/style/css';",
|
||||||
|
"const props = defineProps({});",
|
||||||
|
"const attrs = useAttrs();",
|
||||||
|
"const slots = useSlots();",
|
||||||
|
"const render = () => <$1 {...props} {...attrs} v-slots={slots} />;",
|
||||||
|
"</script>",
|
||||||
|
"<style lang=\"less\" scoped></style>"
|
||||||
|
],
|
||||||
|
"description": "快速二次封装ElementPlus组件"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="render" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="jsx">
|
||||||
|
import { ElImage } from 'element-plus/es/components/image/index';
|
||||||
|
import 'element-plus/es/components/image/style/css';
|
||||||
|
const props = defineProps({
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
alt: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: 'auto',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: 'auto',
|
||||||
|
},
|
||||||
|
fit: {
|
||||||
|
type: String,
|
||||||
|
default: 'contain',
|
||||||
|
},
|
||||||
|
zIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 9999,
|
||||||
|
},
|
||||||
|
hideOnClickModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
lazy: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
previewSrcList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
previewTeleported: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const slots = useSlots();
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const imageSlots = {
|
||||||
|
placeholder: () => (
|
||||||
|
<div class="image-slot">
|
||||||
|
<ElIcon name="Picture" size="20" />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
error: () => (
|
||||||
|
<div class="image-slot">
|
||||||
|
<ElIcon name="file-damage-fill" size="20" />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
...slots,
|
||||||
|
};
|
||||||
|
if (props.previewSrcList?.length === 0) {
|
||||||
|
props.previewSrcList.push(props.src);
|
||||||
|
}
|
||||||
|
const width = computed(() => props.width);
|
||||||
|
const height = computed(() => props.height);
|
||||||
|
const render = () => <ElImage {...props} {...attrs} v-slots={imageSlots} />;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.el-image {
|
||||||
|
width: v-bind(width);
|
||||||
|
height: v-bind(height);
|
||||||
|
:deep(.image-slot) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="render" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="jsx">
|
||||||
|
import { ElInput } from 'element-plus/es/components/input/index';
|
||||||
|
import 'element-plus/es/components/input/style/css';
|
||||||
|
const props = defineProps({
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showWordLimit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'text',
|
||||||
|
},
|
||||||
|
showPassword: {
|
||||||
|
type: Boolean,
|
||||||
|
default(props) {
|
||||||
|
return props.type === 'password';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
showWordLimit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: Number,
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
autosize: {
|
||||||
|
type: [Boolean, Object],
|
||||||
|
default() {
|
||||||
|
return { minRows: 5, maxRows: 10 };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resize: {
|
||||||
|
type: String,
|
||||||
|
default: 'none',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const slots = useSlots();
|
||||||
|
const render = () => <ElInput {...props} {...attrs} v-slots={slots}></ElInput>;
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped></style>
|
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="render" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="jsx">
|
||||||
|
import { ElRadioGroup, ElRadioButton, ElRadio } from 'element-plus/es/components/radio/index';
|
||||||
|
import 'element-plus/es/components/radio/style/css';
|
||||||
|
const props = defineProps({
|
||||||
|
opts: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
label: 'label',
|
||||||
|
value: 'value',
|
||||||
|
disabled: 'disabled',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const slots = useSlots();
|
||||||
|
let config = {
|
||||||
|
label: 'label',
|
||||||
|
value: 'value',
|
||||||
|
disabled: 'disabled',
|
||||||
|
...props.config,
|
||||||
|
};
|
||||||
|
function handleItemDisabled(item, index) {
|
||||||
|
let res = false;
|
||||||
|
if (config.disabled instanceof Function) {
|
||||||
|
res = config.disabled(item, index);
|
||||||
|
} else {
|
||||||
|
res = !!item[config.disabled];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const Component = props.button ? ElRadioButton : ElRadio;
|
||||||
|
const render = () => (
|
||||||
|
<ElRadioGroup
|
||||||
|
{...props}
|
||||||
|
{...attrs}
|
||||||
|
v-slots={{
|
||||||
|
default: () =>
|
||||||
|
props.opts.map((item, index) => (
|
||||||
|
<Component label={item[config.value]} disabled={handleItemDisabled(item, index)}>
|
||||||
|
{item[config.label]}
|
||||||
|
</Component>
|
||||||
|
)),
|
||||||
|
...slots,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped></style>
|
@ -0,0 +1,202 @@
|
|||||||
|
<template>
|
||||||
|
<component :is="render" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="jsx">
|
||||||
|
import config from '@/configs';
|
||||||
|
import { ElMessage } from '@/plugins/element-plus';
|
||||||
|
import { ElUpload } from 'element-plus/es/components/upload/index';
|
||||||
|
import 'element-plus/es/components/upload/style/css';
|
||||||
|
import { ElImage } from 'element-plus/es/components/image/index';
|
||||||
|
import 'element-plus/es/components/image/style/css';
|
||||||
|
const store = useStore();
|
||||||
|
const props = defineProps({
|
||||||
|
action: {
|
||||||
|
type: String,
|
||||||
|
default: config.baseURL + '/edu-oss/oss/fileUpload',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return { service: 'msb-edu-course' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
drag: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 1024 * 1024 * 20,
|
||||||
|
},
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: '*.*',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const emits = defineEmits(['update:modelValue']);
|
||||||
|
props.headers['Authorization'] = 'Bearer ' + store.state.local.token;
|
||||||
|
let imgList = ref([]);
|
||||||
|
const refsUpload = ref(null);
|
||||||
|
watch(
|
||||||
|
() => imgList,
|
||||||
|
() => {
|
||||||
|
if (props.limit === 1) {
|
||||||
|
emits('update:modelValue', unref(imgList)[0]?.response.data);
|
||||||
|
} else {
|
||||||
|
emits('update:modelValue', unref(imgList));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
const handleSuccess = (res, file, list) => {
|
||||||
|
console.info('[upload] success', list);
|
||||||
|
imgList.value = list;
|
||||||
|
};
|
||||||
|
const handleRemove = (file, list) => {
|
||||||
|
console.info('[upload] remove', list);
|
||||||
|
imgList.value = list;
|
||||||
|
};
|
||||||
|
const handleExceed = (list) => {
|
||||||
|
console.info('[upload] exceed', list);
|
||||||
|
ElMessage.error('超出最大上传数量');
|
||||||
|
};
|
||||||
|
const handleBeforeUpload = (file) => {
|
||||||
|
console.info('[upload] upload', file);
|
||||||
|
if (file.size >= props.size) {
|
||||||
|
ElMessage.error('超出文件大小限制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fmtSize = computed(() => {
|
||||||
|
const units = ['byte', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
let res = props.size,
|
||||||
|
unit = 0;
|
||||||
|
while (res >= 800) {
|
||||||
|
res /= 1024;
|
||||||
|
unit++;
|
||||||
|
}
|
||||||
|
return res + units[unit];
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => attrs.modelValue,
|
||||||
|
(value) => {
|
||||||
|
if (props.limit === 1 && value) {
|
||||||
|
imgList.value = [
|
||||||
|
{
|
||||||
|
name: value,
|
||||||
|
response: {
|
||||||
|
data: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
imgList.value = value || [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
|
const handleDeleteImage = (index) => {
|
||||||
|
if (unref(refsUpload)) {
|
||||||
|
unref(refsUpload).handleRemove(imgList[index]);
|
||||||
|
} else {
|
||||||
|
unref(imgList).splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const render = () => (
|
||||||
|
<div class="upload-box">
|
||||||
|
<div class="upload-image">
|
||||||
|
{unref(imgList).map((item, index) => (
|
||||||
|
<div class="img-li">
|
||||||
|
<ElImage src={item?.response?.data} alt={item.name} />
|
||||||
|
<div class="img-li-cover" onClick={() => handleDeleteImage(index)}>
|
||||||
|
<ElIcon class="upload-del-icon" name="delete-bin-fill" size="20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{props.limit != unref(imgList).length ? (
|
||||||
|
<ElUpload
|
||||||
|
ref={refsUpload}
|
||||||
|
{...props}
|
||||||
|
{...attrs}
|
||||||
|
before-upload={handleBeforeUpload}
|
||||||
|
on-exceed={handleExceed}
|
||||||
|
on-remove={handleRemove}
|
||||||
|
on-success={handleSuccess}
|
||||||
|
show-file-list={false}
|
||||||
|
>
|
||||||
|
<ElIcon class="el-icon--upload" name="add-fill" size="20" />
|
||||||
|
</ElUpload>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="el-upload__tip">支持小于 {unref(fmtSize)} 的 文件</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.upload-box {
|
||||||
|
:deep(.upload-image) {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.img-li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
margin-right: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
.img-li-cover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.img-li-cover {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
cursor: pointer;
|
||||||
|
.upload-del-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.el-upload) {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
.el-upload-dragger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.el-icon--upload {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in new issue