文件选择器支持image类型的多选

This commit is contained in:
阿辉
2025-01-22 15:53:01 +08:00
parent 1fe0a89338
commit c781d1f559
2 changed files with 280 additions and 250 deletions

View File

@@ -8,7 +8,27 @@
<el-option v-for="item, index in listAllData" :key="index" :value="String(item[props.valueKey])" <el-option v-for="item, index in listAllData" :key="index" :value="String(item[props.valueKey])"
:label="item.name" /> :label="item.name" />
</el-select> </el-select>
<div v-if="props.inputType === 'image'" style="position: relative;" class="form-display"
<div v-if="props.inputType === 'image' && props.multiple"
style="width: 100%; display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 4px;">
<el-image v-for="item, index in (data || [])" :src="item" :key="index" fit="scale-down" class="itemList"
:style="{ width: props.inputSize + 'px', aspectRatio: '1 / 1' }" />
<div style="position: relative;" :style="{ width: props.inputSize + 'px', height: props.inputSize + 'px' }">
<div
style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">
<el-icon :size="24">
<Plus />
</el-icon>
</div>
<div @click="selectVisiable = true && !props.disabled" class="addControllorHover"
:style="{ cursor: props.disabled ? 'not-allowed' : 'pointer' }"></div>
<el-icon v-show="(!!data && !props.disabled) && !props.multiple" class="closeHover" :size="16"
@click="clear">
<Close />
</el-icon>
</div>
</div>
<div v-if="props.inputType === 'image' && !props.multiple" class="form-display" style="position: relative;"
@mouseenter="formDisplayEnter" @mouseleave="formDisplayLeave" @mouseenter="formDisplayEnter" @mouseleave="formDisplayLeave"
:style="{ width: props.inputSize + 'px', height: props.inputSize + 'px' }"> :style="{ width: props.inputSize + 'px', height: props.inputSize + 'px' }">
<el-image :src="data" fit="scale-down" :style="{ width: props.inputSize + 'px', aspectRatio: '1 / 1' }"> <el-image :src="data" fit="scale-down" :style="{ width: props.inputSize + 'px', aspectRatio: '1 / 1' }">
@@ -24,10 +44,11 @@
</div> </div>
<div @click="selectVisiable = true && !props.disabled" class="addControllorHover" <div @click="selectVisiable = true && !props.disabled" class="addControllorHover"
:style="{ cursor: props.disabled ? 'not-allowed' : 'pointer' }"></div> :style="{ cursor: props.disabled ? 'not-allowed' : 'pointer' }"></div>
<el-icon v-show="!!data && !props.disabled" class="closeHover" :size="16" @click="clear"> <el-icon v-show="(!!data && !props.disabled) && !props.multiple" class="closeHover" :size="16" @click="clear">
<Close /> <Close />
</el-icon> </el-icon>
</div> </div>
<div v-if="props.inputType === 'video'" class="form-display" @mouseenter="formDisplayEnter" <div v-if="props.inputType === 'video'" class="form-display" @mouseenter="formDisplayEnter"
@mouseleave="formDisplayLeave" @mouseleave="formDisplayLeave"
style="position: relative; display: flex; align-items: center; justify-items: center;" style="position: relative; display: flex; align-items: center; justify-items: center;"
@@ -46,6 +67,7 @@
<Close /> <Close />
</el-icon> </el-icon>
</div> </div>
<div v-if="props.inputType === 'audio'" class="form-display" @mouseenter="formDisplayEnter" <div v-if="props.inputType === 'audio'" class="form-display" @mouseenter="formDisplayEnter"
@mouseleave="formDisplayLeave" @mouseleave="formDisplayLeave"
style="position: relative; display: flex; align-items: center; justify-items: center;" style="position: relative; display: flex; align-items: center; justify-items: center;"
@@ -199,7 +221,7 @@ const props = defineProps({
tabsShow: { type: Number, default: SHOW.ALL }, tabsShow: { type: Number, default: SHOW.ALL },
// 是否可以多选,默认单选 // 是否可以多选,默认单选
// 该值为true时inputType必须是selector暂不支持其他type的多选 // 该值为true时inputType必须是selector或image暂不支持其他type的多选
multiple: { type: Boolean, default: false }, multiple: { type: Boolean, default: false },
// 是否可选该参数用于只上传和展示而不选择和绑定model的情况 // 是否可选该参数用于只上传和展示而不选择和绑定model的情况
@@ -274,6 +296,7 @@ const onItemClick = async (e: MouseEvent) => {
while (!target.dataset.id) target = target.parentElement as HTMLElement; while (!target.dataset.id) target = target.parentElement as HTMLElement;
let fileId = target.dataset.id; let fileId = target.dataset.id;
if (props.multiple) { if (props.multiple) {
if (!!!data.value) data.value = [];
if (target.classList.contains('active')) { target.classList.remove('active'); flat = -1; } if (target.classList.contains('active')) { target.classList.remove('active'); flat = -1; }
else { target.classList.add('active'); flat = 1; } else { target.classList.add('active'); flat = 1; }
if (data.value.length) { if (data.value.length) {
@@ -394,7 +417,8 @@ const onDataChange = (value: any) => {
defineExpose({ data, onDataChange, selectVisiable, clearState, clear }); defineExpose({ data, onDataChange, selectVisiable, clearState, clear });
onMounted(() => { onMounted(() => {
if (props.multiple && props.inputType !== 'selector')
if (props.multiple && !['selector', 'image'].includes(props.inputType))
throw new Error('FileSelector组件属性multiple为true时inputType必须为selector'); throw new Error('FileSelector组件属性multiple为true时inputType必须为selector');
listRequestAll(); listRequestAll();
console.log('fileselector tenentmdoe', isTenentMode); console.log('fileselector tenentmdoe', isTenentMode);
@@ -475,4 +499,9 @@ onMounted(() => {
top: 2px; top: 2px;
cursor: pointer; cursor: pointer;
} }
.itemList {
border: 1px solid #dcdfe6;
border-radius: 8px;
}
</style> </style>

View File

@@ -1,253 +1,254 @@
import * as api from './api'; import * as api from './api';
import { import {
UserPageQuery, UserPageQuery,
AddReq, AddReq,
DelReq, DelReq,
EditReq, EditReq,
CrudExpose, CrudExpose,
CrudOptions, CrudOptions,
CreateCrudOptionsProps, CreateCrudOptionsProps,
CreateCrudOptionsRet, CreateCrudOptionsRet,
dict dict
} from '@fast-crud/fast-crud'; } from '@fast-crud/fast-crud';
import fileSelector from '/@/components/fileSelector/index.vue'; import fileSelector from '/@/components/fileSelector/index.vue';
import { shallowRef } from 'vue';
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery) => { const pageRequest = async (query: UserPageQuery) => {
return await api.GetList(query); return await api.GetList(query);
}; };
const editRequest = async ({ form, row }: EditReq) => { const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id; form.id = row.id;
return await api.UpdateObj(form); return await api.UpdateObj(form);
}; };
const delRequest = async ({ row }: DelReq) => { const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id); return await api.DelObj(row.id);
}; };
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form); return await api.AddObj(form);
}; };
return { return {
crudOptions: { crudOptions: {
actionbar: { actionbar: {
buttons: { buttons: {
add: { add: {
show: true, show: true,
click: () => context.openAddHandle?.() click: () => context.openAddHandle?.()
}, },
}, },
}, },
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest, delRequest,
}, },
tabs: { tabs: {
show: true, show: true,
name: 'file_type', name: 'file_type',
type: '', type: '',
options: [ options: [
{ value: 0, label: '图片' }, { value: 0, label: '图片' },
{ value: 1, label: '视频' }, { value: 1, label: '视频' },
{ value: 2, label: '音频' }, { value: 2, label: '音频' },
{ value: 3, label: '其他' }, { value: 3, label: '其他' },
] ]
}, },
rowHandle: { rowHandle: {
//固定右侧 //固定右侧
fixed: 'right', fixed: 'right',
width: 200, width: 200,
show: false, show: false,
buttons: { buttons: {
view: { view: {
show: false, show: false,
}, },
edit: { edit: {
iconRight: 'Edit', iconRight: 'Edit',
type: 'text', type: 'text',
}, },
remove: { remove: {
iconRight: 'Delete', iconRight: 'Delete',
type: 'text', type: 'text',
}, },
}, },
}, },
columns: { columns: {
_index: { _index: {
title: '序号', title: '序号',
form: { show: false }, form: { show: false },
column: { column: {
//type: 'index', //type: 'index',
align: 'center', align: 'center',
width: '70px', width: '70px',
columnSetDisabled: true, //禁止在列设置中选择 columnSetDisabled: true, //禁止在列设置中选择
formatter: (context) => { formatter: (context) => {
//计算序号,你可以自定义计算规则,此处为翻页累加 //计算序号,你可以自定义计算规则,此处为翻页累加
let index = context.index ?? 1; let index = context.index ?? 1;
let pagination = crudExpose!.crudBinding.value.pagination; let pagination = crudExpose!.crudBinding.value.pagination;
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1; return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
}, },
}, },
}, },
search: { search: {
title: '关键词', title: '关键词',
column: { column: {
show: false, show: false,
}, },
search: { search: {
show: true, show: true,
component: { component: {
props: { props: {
clearable: true, clearable: true,
}, },
placeholder: '请输入关键词', placeholder: '请输入关键词',
}, },
}, },
form: { form: {
show: false, show: false,
component: { component: {
props: { props: {
clearable: true, clearable: true,
}, },
}, },
}, },
}, },
name: { name: {
title: '文件名称', title: '文件名称',
search: { search: {
show: true, show: true,
}, },
type: 'input', type: 'input',
column: { column: {
minWidth: 200, minWidth: 200,
}, },
form: { form: {
component: { component: {
placeholder: '请输入文件名称', placeholder: '请输入文件名称',
clearable: true clearable: true
}, },
}, },
}, },
preview: { preview: {
title: '预览', title: '预览',
column: { column: {
minWidth: 120, minWidth: 120,
align: 'center' align: 'center'
}, },
form: { form: {
show: false show: false
} }
}, },
url: { url: {
title: '文件地址', title: '文件地址',
type: 'file-uploader', type: 'file-uploader',
search: { search: {
disabled: true, disabled: true,
}, },
column: { column: {
minWidth: 360, minWidth: 360,
}, },
}, },
md5sum: { md5sum: {
title: '文件MD5', title: '文件MD5',
search: { search: {
disabled: true, disabled: true,
}, },
column: { column: {
minWidth: 300, minWidth: 300,
}, },
form: { form: {
disabled: false disabled: false
}, },
}, },
mime_type: { mime_type: {
title: '文件类型', title: '文件类型',
type: 'input', type: 'input',
form: { form: {
show: false, show: false,
}, },
column: { column: {
minWidth: 160 minWidth: 160
} }
}, },
file_type: { file_type: {
title: '文件类型', title: '文件类型',
type: 'dict-select', type: 'dict-select',
dict: dict({ dict: dict({
data: [ data: [
{ label: '图片', value: 0, color: 'success' }, { label: '图片', value: 0, color: 'success' },
{ label: '视频', value: 1, color: 'warning' }, { label: '视频', value: 1, color: 'warning' },
{ label: '音频', value: 2, color: 'danger' }, { label: '音频', value: 2, color: 'danger' },
{ label: '其他', value: 3, color: 'primary' }, { label: '其他', value: 3, color: 'primary' },
] ]
}), }),
column: { column: {
show: false show: false
}, },
search: { search: {
show: true show: true
}, },
form: { form: {
show: false, show: false,
component: { component: {
placeholder: '请选择文件类型' placeholder: '请选择文件类型'
} }
} }
}, },
size: { size: {
title: '文件大小', title: '文件大小',
column: { column: {
minWidth: 120 minWidth: 120
}, },
form: { form: {
show: false show: false
} }
}, },
upload_method: { upload_method: {
title: '上传方式', title: '上传方式',
type: 'dict-select', type: 'dict-select',
dict: dict({ dict: dict({
data: [ data: [
{ label: '默认上传', value: 0, color: 'primary' }, { label: '默认上传', value: 0, color: 'primary' },
{ label: '文件选择器上传', value: 1, color: 'warning' }, { label: '文件选择器上传', value: 1, color: 'warning' },
] ]
}), }),
column: { column: {
minWidth: 140 minWidth: 140
}, },
search: { search: {
show: true show: true
} }
}, },
create_datetime: { create_datetime: {
title: '创建时间', title: '创建时间',
column: { column: {
minWidth: 160 minWidth: 160
}, },
form: { form: {
show: false show: false
} }
}, },
// fileselectortest: { fileselectortest: {
// title: '文件选择器测试', title: '文件选择器测试',
// type: 'file-selector', type: 'file-selector',
// width: 200, column: {
// form: { minWidth: 200
// component: { },
// name: shallowRef(fileSelector), form: {
// vModel: 'modelValue', component: {
// tabsShow: 0b0100, name: fileSelector,
// itemSize: 100, vModel: 'modelValue',
// multiple: false, tabsShow: 0b1111,
// selectable: true, itemSize: 100,
// showInput: true, multiple: true,
// inputType: 'video', selectable: true,
// valueKey: 'url', showInput: true,
// } inputType: 'image',
// } valueKey: 'url',
// } }
}, }
}, }
}; },
},
};
}; };