123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- <template>
- <div class="material" v-loading="pager.loading">
- <div class="material__left">
- <div class="flex-1 min-h-0">
- <el-scrollbar>
- <div class="material-left__content pt-4 p-b-4">
- <el-tree
- ref="treeRef"
- node-key="id"
- :data="cateLists"
- empty-text="''"
- :highlight-current="true"
- :expand-on-click-node="false"
- :current-node-key="cateId"
- @node-click="handleCatSelect"
- >
- <template v-slot="{ data }">
- <div class="flex flex-1 items-center min-w-0 pr-4">
- <img
- class="w-[20px] h-[16px] mr-3"
- src="@/assets/images/icon_folder.png"
- />
- <span class="flex-1 truncate mr-2">
- <overflow-tooltip :content="data.name" />
- </span>
- <el-dropdown
- v-perms="['albums:cateRename', 'albums:cateDel']"
- v-if="data.id > 0"
- :hide-on-click="false"
- >
- <span class="muted m-r-10">···</span>
- <template #dropdown>
- <el-dropdown-menu>
- <popover-input
- v-perms="['albums:cateRename']"
- @confirm="handleEditCate($event, data.id)"
- size="default"
- :value="data.name"
- width="400px"
- :limit="20"
- show-limit
- teleported
- >
- <div>
- <el-dropdown-item>
- 命名分组
- </el-dropdown-item>
- </div>
- </popover-input>
- <div
- v-perms="['albums:cateDel']"
- @click="handleDeleteCate(data.id)"
- >
- <el-dropdown-item>删除分组</el-dropdown-item>
- </div>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- </template>
- </el-tree>
- </div>
- </el-scrollbar>
- </div>
- <div class="flex justify-center p-2 border-t border-br">
- <popover-input
- v-perms="['albums:cateAdd']"
- @confirm="handleAddCate"
- size="default"
- width="400px"
- :limit="20"
- show-limit
- teleported
- >
- <el-button> 添加分组 </el-button>
- </popover-input>
- </div>
- </div>
- <div class="material__center flex flex-col">
- <div class="operate-btn flex">
- <div class="flex-1 flex">
- <upload
- v-if="type == 'image'"
- v-perms="['upload:image']"
- class="mr-3"
- :data="{ cid: cateId,brandId:brandId,houseId:houseId }"
- :type="type"
- :show-progress="true"
- @change="refresh"
- >
- <el-button type="primary">本地上传</el-button>
- </upload>
- <upload
- v-if="type == 'video'"
- v-perms="['upload:video']"
- class="mr-3"
- :data="{ cid: cateId,brandId:brandId,houseId:houseId }"
- :type="type"
- :show-progress="true"
- @change="refresh"
- >
- <el-button type="primary">本地上传</el-button>
- </upload>
- <el-button
- v-perms="['albums:albumDel']"
- v-if="mode == 'page'"
- :disabled="!select.length"
- @click.stop="batchFileDelete()"
- >
- 删除
- </el-button>
- <popup
- v-perms="['albums:albumMove']"
- v-if="mode == 'page'"
- class="ml-3"
- @confirm="batchFileMove"
- :disabled="!select.length"
- title="移动文件"
- >
- <template #trigger>
- <el-button :disabled="!select.length">移动</el-button>
- </template>
- <div>
- <span class="mr-5">移动文件至</span>
- <el-select v-model="moveId" placeholder="请选择">
- <template v-for="item in cateLists" :key="item.id">
- <el-option
- v-if="item.id !== ''"
- :label="item.name"
- :value="item.id"
- ></el-option>
- </template>
- </el-select>
- </div>
- </popup>
- </div>
- <el-input
- class="w-60"
- placeholder="请输入名称"
- v-model="fileParams.name"
- @keyup.enter="refresh"
- >
- <template #append>
- <el-button @click="refresh">
- <template #icon>
- <icon name="el-icon-Search" />
- </template>
- </el-button>
- </template>
- </el-input>
- <div class="flex items-center ml-2">
- <el-tooltip content="列表视图" placement="top">
- <div
- class="list-icon"
- :class="{
- select: listShowType == 'table'
- }"
- @click="listShowType = 'table'"
- >
- <icon name="local-icon-list-2" :size="18" />
- </div>
- </el-tooltip>
- <el-tooltip content="平铺视图" placement="top">
- <div
- class="list-icon"
- :class="{
- select: listShowType == 'normal'
- }"
- @click="listShowType = 'normal'"
- >
- <icon name="el-icon-Menu" :size="18" />
- </div>
- </el-tooltip>
- </div>
- </div>
- <div class="mt-3" v-if="mode == 'page'">
- <el-checkbox
- :disabled="!pager.lists.length"
- v-model="isCheckAll"
- @change="selectAll"
- :indeterminate="isIndeterminate"
- >
- 当页全选
- </el-checkbox>
- </div>
- <div class="material-center__content flex flex-col flex-1 mb-1 min-h-0">
- <el-scrollbar v-if="pager.lists.length" v-show="listShowType == 'normal'">
- <ul class="file-list flex flex-wrap mt-4">
- <li
- class="file-item-wrap"
- v-for="item in pager.lists"
- :key="item.id"
- :style="{ width: fileSize }"
- >
- <del-wrap @close="batchFileDelete([item.id])">
- <file-item
- :uri="item.uri"
- :file-size="fileSize"
- :type="type"
- @click="selectFile(item)"
- >
- <div class="item-selected" v-if="isSelect(item.id)">
- <icon :size="24" name="el-icon-Check" color="#fff" />
- </div>
- </file-item>
- </del-wrap>
- <overflow-tooltip class="mt-1" :content="item.name" />
- <div class="operation-btns flex items-center">
- <popover-input
- v-perms="['albums:albumRename']"
- @confirm="handleFileRename($event, item.id)"
- size="default"
- :value="item.name"
- width="400px"
- :limit="50"
- show-limit
- teleported
- >
- <el-button type="primary" link> 重命名 </el-button>
- </popover-input>
- <el-button type="primary" link @click="handlePreview(item.uri)">
- 查看
- </el-button>
- </div>
- </li>
- </ul>
- </el-scrollbar>
- <el-table
- ref="tableRef"
- class="mt-4"
- v-show="listShowType == 'table'"
- :data="pager.lists"
- width="100%"
- height="100%"
- size="large"
- @row-click="selectFile"
- >
- <el-table-column width="55">
- <template #default="{ row }">
- <el-checkbox :modelValue="isSelect(row.id)" @change="selectFile(row)" />
- </template>
- </el-table-column>
- <el-table-column label="图片" width="100">
- <template #default="{ row }">
- <file-item :uri="row.uri" file-size="50px" :type="type"></file-item>
- </template>
- </el-table-column>
- <el-table-column label="名称" min-width="100" show-overflow-tooltip>
- <template #default="{ row }">
- <el-link @click.stop="handlePreview(row.uri)" :underline="false">
- {{ row.name }}
- </el-link>
- </template>
- </el-table-column>
- <el-table-column prop="createTime" label="上传时间" min-width="100" />
- <el-table-column label="操作" width="150" fixed="right">
- <template #default="{ row }">
- <div class="inline-block" v-perms="['albums:albumRename']">
- <popover-input
- @confirm="handleFileRename($event, row.id)"
- size="default"
- :value="row.name"
- width="400px"
- :limit="50"
- show-limit
- teleported
- >
- <el-button type="primary" link> 重命名 </el-button>
- </popover-input>
- </div>
- <div class="inline-block">
- <el-button type="primary" link @click.stop="handlePreview(row.uri)">
- 查看
- </el-button>
- </div>
- <div class="inline-block" v-perms="['albums:albumDel']">
- <el-button
- type="primary"
- link
- @click.stop="batchFileDelete([row.id])"
- >
- 删除
- </el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- <div
- class="flex flex-1 justify-center items-center"
- v-if="!pager.loading && !pager.lists.length"
- >
- 暂无数据~
- </div>
- </div>
- <div class="material-center__footer flex justify-between items-center mt-2">
- <div class="flex">
- <template v-if="mode == 'page'">
- <span class="mr-3">
- <el-checkbox
- :disabled="!pager.lists.length"
- v-model="isCheckAll"
- @change="selectAll"
- :indeterminate="isIndeterminate"
- >
- 当页全选
- </el-checkbox>
- </span>
- <el-button
- v-perms="['albums:albumDel']"
- :disabled="!select.length"
- @click="batchFileDelete()"
- >
- 删除
- </el-button>
- <popup
- v-perms="['albums:albumMove']"
- class="ml-3 inline"
- @confirm="batchFileMove"
- :disabled="!select.length"
- title="移动文件"
- >
- <template #trigger>
- <el-button :disabled="!select.length">移动</el-button>
- </template>
- <div>
- <span class="mr-5">移动文件至</span>
- <el-select v-model="moveId" placeholder="请选择">
- <template v-for="item in cateLists" :key="item.id">
- <el-option
- v-if="item.id !== ''"
- :label="item.name"
- :value="item.id"
- ></el-option>
- </template>
- </el-select>
- </div>
- </popup>
- </template>
- </div>
- <pagination
- v-model="pager"
- @change="getFileList"
- layout="total, prev, pager, next, jumper"
- />
- </div>
- </div>
- <div class="material__right" v-if="mode == 'picker'">
- <div class="flex justify-between p-2 flex-wrap">
- <div class="sm flex items-center">
- 已选择 {{ select.length }}
- <span v-if="limit">/{{ limit }}</span>
- </div>
- <el-button type="primary" link @click="clearSelect">清空</el-button>
- </div>
- <div class="flex-1 min-h-0">
- <el-scrollbar class="ls-scrollbar">
- <ul class="select-lists flex flex-col p-t-3">
- <li class="mb-4" v-for="item in select" :key="item.id">
- <div class="select-item">
- <del-wrap @close="cancelSelete(item.id)">
- <file-item
- :uri="item.uri"
- file-size="100px"
- :type="type"
- ></file-item>
- </del-wrap>
- </div>
- </li>
- </ul>
- </el-scrollbar>
- </div>
- </div>
- <preview v-model="showPreview" :url="previewUrl" :type="type" />
- </div>
- </template>
- <script lang="ts" setup>
- import { useCate, useFile } from './hook'
- import FileItem from './file.vue'
- import Preview from './preview.vue'
- import type { Ref } from 'vue'
- import {getBrandId,getHouseId } from '@/utils/auth'
- const props = defineProps({
- fileSize: {
- type: String,
- default: '100px'
- },
- limit: {
- type: Number,
- default: 1
- },
- type: {
- type: String,
- default: 'image'
- },
- mode: {
- type: String,
- default: 'picker'
- },
- pageSize: {
- type: Number,
- default: 15
- }
- })
- const brandId = ref(getBrandId())
- const houseId = ref(getHouseId())
- const emit = defineEmits(['change'])
- const { limit } = toRefs(props)
- const typeValue = computed<number>(() => {
- switch (props.type) {
- case 'image':
- return 10
- case 'video':
- return 20
- case 'file':
- return 30
- default:
- return 0
- }
- })
- const visible: Ref<boolean> = inject('visible')!
- const previewUrl = ref('')
- const showPreview = ref(false)
- const {
- treeRef,
- cateId,
- cateLists,
- handleAddCate,
- handleEditCate,
- handleDeleteCate,
- getCateLists,
- handleCatSelect
- } = useCate(typeValue.value)
- const {
- tableRef,
- listShowType,
- moveId,
- pager,
- fileParams,
- select,
- isCheckAll,
- isIndeterminate,
- getFileList,
- refresh,
- batchFileDelete,
- batchFileMove,
- selectFile,
- isSelect,
- clearSelect,
- cancelSelete,
- selectAll,
- handleFileRename
- } = useFile(cateId, typeValue, limit, props.pageSize)
- const getData = async () => {
- await getCateLists()
- treeRef.value?.setCurrentKey(cateId.value)
- getFileList()
- }
- const handlePreview = (url: string) => {
- previewUrl.value = url
- showPreview.value = true
- }
- watch(
- visible,
- async (val: boolean) => {
- if (val) {
- getData()
- }
- },
- {
- immediate: true
- }
- )
- watch(cateId, () => {
- fileParams.name = ''
- refresh()
- })
- watch(
- select,
- (val: any[]) => {
- emit('change', val)
- if (val.length == pager.lists.length && val.length !== 0) {
- isIndeterminate.value = false
- isCheckAll.value = true
- return
- }
- if (val.length > 0) {
- isIndeterminate.value = true
- } else {
- isCheckAll.value = false
- isIndeterminate.value = false
- }
- },
- {
- deep: true
- }
- )
- onMounted(() => {
- props.mode == 'page' && getData()
- })
- defineExpose({
- clearSelect
- })
- </script>
- <style scoped lang="scss">
- .material {
- @apply h-full min-h-0 flex flex-1;
- &__left {
- @apply border-r border-br flex flex-col w-[200px];
- :deep(.el-tree-node__content) {
- height: 36px;
- }
- }
- &__center {
- flex: 1;
- min-width: 0;
- min-height: 0;
- padding: 16px 16px 0;
- .list-icon {
- border-radius: 3px;
- display: flex;
- padding: 5px;
- cursor: pointer;
- &.select {
- @apply text-primary bg-primary-light-8;
- }
- }
- .file-list {
- .file-item-wrap {
- margin-right: 16px;
- line-height: 1.3;
- cursor: pointer;
- .item-selected {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- border-radius: 4px;
- background-color: rgba(0, 0, 0, 0.5);
- box-sizing: border-box;
- }
- .operation-btns {
- height: 28px;
- visibility: hidden;
- }
- &:hover .operation-btns {
- visibility: visible;
- }
- }
- }
- }
- &__right {
- @apply border-l border-br flex flex-col;
- width: 130px;
- .select-lists {
- padding: 10px;
- .select-item {
- width: 100px;
- height: 100px;
- }
- }
- }
- }
- </style>
|