|
@@ -48,7 +48,7 @@
|
|
|
@click="selectCategory('')"
|
|
@click="selectCategory('')"
|
|
|
>
|
|
>
|
|
|
<el-icon><el-icon-folder /></el-icon>
|
|
<el-icon><el-icon-folder /></el-icon>
|
|
|
- <span>{{ fileType === '1' ? '全部图片' : '全部视频' }}</span>
|
|
|
|
|
|
|
+ <span>{{ allCategoryLabel }}</span>
|
|
|
</li>
|
|
</li>
|
|
|
<li
|
|
<li
|
|
|
v-for="item in filteredCategories"
|
|
v-for="item in filteredCategories"
|
|
@@ -81,7 +81,7 @@
|
|
|
:accept="uploadAccept"
|
|
:accept="uploadAccept"
|
|
|
:http-request="uploadRequest"
|
|
:http-request="uploadRequest"
|
|
|
>
|
|
>
|
|
|
- <el-button>{{ fileType === '1' ? '上传图片' : '上传视频' }}</el-button>
|
|
|
|
|
|
|
+ <el-button>{{ uploadBtnText }}</el-button>
|
|
|
</el-upload>
|
|
</el-upload>
|
|
|
<el-button
|
|
<el-button
|
|
|
type="danger"
|
|
type="danger"
|
|
@@ -89,13 +89,13 @@
|
|
|
:disabled="!selectedCount"
|
|
:disabled="!selectedCount"
|
|
|
@click="removeSelected"
|
|
@click="removeSelected"
|
|
|
>
|
|
>
|
|
|
- {{ fileType === '1' ? '删除图片' : '删除视频' }}
|
|
|
|
|
|
|
+ {{ deleteBtnText }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<el-input
|
|
<el-input
|
|
|
v-model="keyword"
|
|
v-model="keyword"
|
|
|
clearable
|
|
clearable
|
|
|
- :placeholder="fileType === '1' ? '搜索图片名称' : '搜索视频名称'"
|
|
|
|
|
|
|
+ :placeholder="searchPlaceholder"
|
|
|
style="width: 240px"
|
|
style="width: 240px"
|
|
|
@keyup.enter="searchFiles"
|
|
@keyup.enter="searchFiles"
|
|
|
@clear="searchFiles"
|
|
@clear="searchFiles"
|
|
@@ -114,13 +114,13 @@
|
|
|
v-for="item in fileList"
|
|
v-for="item in fileList"
|
|
|
:key="item.id"
|
|
:key="item.id"
|
|
|
class="picker-item"
|
|
class="picker-item"
|
|
|
- :class="{ selected: isSelected(item.id) }"
|
|
|
|
|
|
|
+ :class="{ selected: isSelectedItem(item) }"
|
|
|
>
|
|
>
|
|
|
<div class="picker-thumb" @click="toggleSelect(item)">
|
|
<div class="picker-thumb" @click="toggleSelect(item)">
|
|
|
<el-image
|
|
<el-image
|
|
|
- v-if="fileType === '1'"
|
|
|
|
|
|
|
+ v-if="!isVideoItem(item)"
|
|
|
:src="item.url"
|
|
:src="item.url"
|
|
|
- fit="contain"
|
|
|
|
|
|
|
+ fit="cover"
|
|
|
lazy
|
|
lazy
|
|
|
/>
|
|
/>
|
|
|
<div v-else class="video-thumb">
|
|
<div v-else class="video-thumb">
|
|
@@ -128,7 +128,7 @@
|
|
|
<el-icon v-else class="video-icon"><el-icon-video-play /></el-icon>
|
|
<el-icon v-else class="video-icon"><el-icon-video-play /></el-icon>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="picker-check">
|
|
<div class="picker-check">
|
|
|
- <el-icon v-if="isSelected(item.id)"><el-icon-check /></el-icon>
|
|
|
|
|
|
|
+ <el-icon v-if="isSelectedItem(item)"><el-icon-check /></el-icon>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<p class="picker-name" :title="item.name">{{ item.name }}</p>
|
|
<p class="picker-name" :title="item.name">{{ item.name }}</p>
|
|
@@ -150,7 +150,7 @@
|
|
|
>
|
|
>
|
|
|
全选
|
|
全选
|
|
|
</el-checkbox>
|
|
</el-checkbox>
|
|
|
- <span class="selected-tip">已选 {{ selectedCount }} 个</span>
|
|
|
|
|
|
|
+ <span class="selected-tip">{{ selectedCountTip }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<el-pagination
|
|
<el-pagination
|
|
|
v-if="total > 0"
|
|
v-if="total > 0"
|
|
@@ -207,6 +207,8 @@ export default {
|
|
|
buttonText: { type: String, default: "选择素材" },
|
|
buttonText: { type: String, default: "选择素材" },
|
|
|
/** 锁定类型:'1' 仅图片,'2' 仅视频,空则 Tab 切换 */
|
|
/** 锁定类型:'1' 仅图片,'2' 仅视频,空则 Tab 切换 */
|
|
|
lockFileType: { type: String, default: "" },
|
|
lockFileType: { type: String, default: "" },
|
|
|
|
|
+ /** 是否允许同时选中图片与视频(切换 Tab 不清空已选) */
|
|
|
|
|
+ acceptBoth: { type: Boolean, default: true },
|
|
|
/** 返回值类型 object | url */
|
|
/** 返回值类型 object | url */
|
|
|
valueType: { type: String, default: "object" },
|
|
valueType: { type: String, default: "object" },
|
|
|
},
|
|
},
|
|
@@ -233,16 +235,47 @@ export default {
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
|
|
|
+ /** 有 Tab 且未锁定单一类型时,可混选图片+视频 */
|
|
|
|
|
+ isMixedMode() {
|
|
|
|
|
+ return this.acceptBoth && !this.lockFileType;
|
|
|
|
|
+ },
|
|
|
dialogTitle() {
|
|
dialogTitle() {
|
|
|
if (this.title) return this.title;
|
|
if (this.title) return this.title;
|
|
|
|
|
+ if (this.isMixedMode) return "选择素材";
|
|
|
return this.fileType === "2" ? "上传视频" : "上传商品图";
|
|
return this.fileType === "2" ? "上传视频" : "上传商品图";
|
|
|
},
|
|
},
|
|
|
confirmText() {
|
|
confirmText() {
|
|
|
|
|
+ if (this.isMixedMode) return "使用选中素材";
|
|
|
return this.fileType === "2" ? "使用选中视频" : "使用选中图片";
|
|
return this.fileType === "2" ? "使用选中视频" : "使用选中图片";
|
|
|
},
|
|
},
|
|
|
|
|
+ allCategoryLabel() {
|
|
|
|
|
+ return this.fileType === "1" ? "全部图片" : "全部视频";
|
|
|
|
|
+ },
|
|
|
|
|
+ uploadBtnText() {
|
|
|
|
|
+ if (this.isMixedMode) return "上传素材";
|
|
|
|
|
+ return this.fileType === "1" ? "上传图片" : "上传视频";
|
|
|
|
|
+ },
|
|
|
|
|
+ deleteBtnText() {
|
|
|
|
|
+ if (this.isMixedMode) return "删除素材";
|
|
|
|
|
+ return this.fileType === "1" ? "删除图片" : "删除视频";
|
|
|
|
|
+ },
|
|
|
|
|
+ searchPlaceholder() {
|
|
|
|
|
+ if (this.isMixedMode) return "搜索素材名称";
|
|
|
|
|
+ return this.fileType === "1" ? "搜索图片名称" : "搜索视频名称";
|
|
|
|
|
+ },
|
|
|
uploadAccept() {
|
|
uploadAccept() {
|
|
|
|
|
+ if (this.isMixedMode) return "image/*,video/*";
|
|
|
return this.fileType === "1" ? "image/*" : "video/*";
|
|
return this.fileType === "1" ? "image/*" : "video/*";
|
|
|
},
|
|
},
|
|
|
|
|
+ selectedCountTip() {
|
|
|
|
|
+ const items = this.getUniqueSelectedItems();
|
|
|
|
|
+ const n = items.length;
|
|
|
|
|
+ if (!n) return "已选 0 个";
|
|
|
|
|
+ if (!this.isMixedMode) return `已选 ${n} 个`;
|
|
|
|
|
+ const video = items.filter((i) => this.isVideoItem(i)).length;
|
|
|
|
|
+ const image = n - video;
|
|
|
|
|
+ return `已选 ${n} 个(图片 ${image} / 视频 ${video})`;
|
|
|
|
|
+ },
|
|
|
filteredCategories() {
|
|
filteredCategories() {
|
|
|
const kw = (this.categoryKeyword || "").trim().toLowerCase();
|
|
const kw = (this.categoryKeyword || "").trim().toLowerCase();
|
|
|
if (!kw) return this.flatCategories;
|
|
if (!kw) return this.flatCategories;
|
|
@@ -258,19 +291,24 @@ export default {
|
|
|
}
|
|
}
|
|
|
return matched;
|
|
return matched;
|
|
|
},
|
|
},
|
|
|
- selectedIds() {
|
|
|
|
|
- return Object.keys(this.selectedMap);
|
|
|
|
|
- },
|
|
|
|
|
selectedCount() {
|
|
selectedCount() {
|
|
|
- return this.selectedIds.length;
|
|
|
|
|
|
|
+ return this.getUniqueSelectedItems().length;
|
|
|
},
|
|
},
|
|
|
allSelected() {
|
|
allSelected() {
|
|
|
if (!this.fileList.length) return false;
|
|
if (!this.fileList.length) return false;
|
|
|
- return this.fileList.every((f) => this.isSelected(f.id));
|
|
|
|
|
|
|
+ return this.fileList.every((f) => this.isSelectedItem(f));
|
|
|
},
|
|
},
|
|
|
indeterminate() {
|
|
indeterminate() {
|
|
|
if (!this.fileList.length || this.allSelected) return false;
|
|
if (!this.fileList.length || this.allSelected) return false;
|
|
|
- return this.fileList.some((f) => this.isSelected(f.id));
|
|
|
|
|
|
|
+ return this.fileList.some((f) => this.isSelectedItem(f));
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ watch: {
|
|
|
|
|
+ modelValue: {
|
|
|
|
|
+ deep: true,
|
|
|
|
|
+ handler() {
|
|
|
|
|
+ if (this.visible) this.initSelectionFromModel();
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
@@ -294,26 +332,70 @@ export default {
|
|
|
onDialogClosed() {
|
|
onDialogClosed() {
|
|
|
this.$emit("close");
|
|
this.$emit("close");
|
|
|
},
|
|
},
|
|
|
|
|
+ urlSelectKey(url) {
|
|
|
|
|
+ return url ? `url:${url}` : "";
|
|
|
|
|
+ },
|
|
|
|
|
+ getItemSelectKey(item) {
|
|
|
|
|
+ if (!item) return "";
|
|
|
|
|
+ if (item.id != null && item.id !== "") return String(item.id);
|
|
|
|
|
+ if (item.url) return this.urlSelectKey(item.url);
|
|
|
|
|
+ return "";
|
|
|
|
|
+ },
|
|
|
|
|
+ putSelectedItem(n) {
|
|
|
|
|
+ const key = this.getItemSelectKey(n);
|
|
|
|
|
+ if (!key) return;
|
|
|
|
|
+ this.selectedMap = { ...this.selectedMap, [key]: n };
|
|
|
|
|
+ },
|
|
|
|
|
+ removeSelectedByItem(item) {
|
|
|
|
|
+ const key = this.findSelectedKeyForItem(item);
|
|
|
|
|
+ if (!key) return;
|
|
|
|
|
+ const next = { ...this.selectedMap };
|
|
|
|
|
+ delete next[key];
|
|
|
|
|
+ this.selectedMap = next;
|
|
|
|
|
+ },
|
|
|
|
|
+ findSelectedKeyForItem(item) {
|
|
|
|
|
+ if (!item) return "";
|
|
|
|
|
+ if (item.id != null && item.id !== "" && this.selectedMap[String(item.id)]) {
|
|
|
|
|
+ return String(item.id);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.url) {
|
|
|
|
|
+ const uk = this.urlSelectKey(item.url);
|
|
|
|
|
+ if (this.selectedMap[uk]) return uk;
|
|
|
|
|
+ const hit = Object.entries(this.selectedMap).find(
|
|
|
|
|
+ ([, s]) => s.url && s.url === item.url
|
|
|
|
|
+ );
|
|
|
|
|
+ if (hit) return hit[0];
|
|
|
|
|
+ }
|
|
|
|
|
+ return "";
|
|
|
|
|
+ },
|
|
|
initSelectionFromModel() {
|
|
initSelectionFromModel() {
|
|
|
const raw = this.modelValue;
|
|
const raw = this.modelValue;
|
|
|
const list = Array.isArray(raw) ? raw : raw ? [raw] : [];
|
|
const list = Array.isArray(raw) ? raw : raw ? [raw] : [];
|
|
|
this.selectedMap = {};
|
|
this.selectedMap = {};
|
|
|
list.forEach((item) => {
|
|
list.forEach((item) => {
|
|
|
if (typeof item === "string") {
|
|
if (typeof item === "string") {
|
|
|
- const row = { att_id: item, att_dir: item, real_name: item };
|
|
|
|
|
- const n = this.normalizeFileRow(row);
|
|
|
|
|
- this.selectedMap[String(n.id)] = n;
|
|
|
|
|
- } else if (item && typeof item === "object") {
|
|
|
|
|
|
|
+ const url = item.trim();
|
|
|
|
|
+ if (!url) return;
|
|
|
|
|
+ const row = { att_dir: url, real_name: url.split(/[/\\]/).pop() || url };
|
|
|
|
|
+ this.putSelectedItem(this.normalizeFileRow(row));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item && typeof item === "object") {
|
|
|
const n = this.normalizeFileRow({
|
|
const n = this.normalizeFileRow({
|
|
|
att_id: item.id ?? item.att_id,
|
|
att_id: item.id ?? item.att_id,
|
|
|
att_dir: item.url ?? item.att_dir,
|
|
att_dir: item.url ?? item.att_dir,
|
|
|
real_name: item.name ?? item.real_name,
|
|
real_name: item.name ?? item.real_name,
|
|
|
|
|
+ file_type: item.file_type,
|
|
|
...item,
|
|
...item,
|
|
|
});
|
|
});
|
|
|
- if (n.id != null) this.selectedMap[String(n.id)] = n;
|
|
|
|
|
|
|
+ this.putSelectedItem(n);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
|
|
+ /** 编辑回显:按 att_id 或 url 与已选记录匹配 */
|
|
|
|
|
+ isSelectedItem(item) {
|
|
|
|
|
+ return !!this.findSelectedKeyForItem(item);
|
|
|
|
|
+ },
|
|
|
async bootstrap() {
|
|
async bootstrap() {
|
|
|
await this.loadCategories();
|
|
await this.loadCategories();
|
|
|
await this.loadFiles();
|
|
await this.loadFiles();
|
|
@@ -323,12 +405,15 @@ export default {
|
|
|
this.keyword = "";
|
|
this.keyword = "";
|
|
|
this.categoryKeyword = "";
|
|
this.categoryKeyword = "";
|
|
|
this.query.page = 1;
|
|
this.query.page = 1;
|
|
|
- if (this.multiple) {
|
|
|
|
|
|
|
+ if (!this.isMixedMode && this.multiple) {
|
|
|
this.selectedMap = {};
|
|
this.selectedMap = {};
|
|
|
}
|
|
}
|
|
|
this.loadCategories();
|
|
this.loadCategories();
|
|
|
this.loadFiles();
|
|
this.loadFiles();
|
|
|
},
|
|
},
|
|
|
|
|
+ isVideoItem(item) {
|
|
|
|
|
+ return String(item?.file_type ?? "") === "2";
|
|
|
|
|
+ },
|
|
|
onCategorySearchClear() {
|
|
onCategorySearchClear() {
|
|
|
this.categoryKeyword = "";
|
|
this.categoryKeyword = "";
|
|
|
},
|
|
},
|
|
@@ -380,16 +465,17 @@ export default {
|
|
|
const total = Number(d.count ?? d.total ?? list.length ?? 0);
|
|
const total = Number(d.count ?? d.total ?? list.length ?? 0);
|
|
|
return { list, total };
|
|
return { list, total };
|
|
|
},
|
|
},
|
|
|
- normalizeFileRow(row) {
|
|
|
|
|
|
|
+ normalizeFileRow(row, defaultType) {
|
|
|
const rawId = row.att_id ?? row.id;
|
|
const rawId = row.att_id ?? row.id;
|
|
|
const id = rawId != null && rawId !== "" ? String(rawId) : "";
|
|
const id = rawId != null && rawId !== "" ? String(rawId) : "";
|
|
|
const url = row.att_dir || row.satt_dir || row.url || row.src || "";
|
|
const url = row.att_dir || row.satt_dir || row.url || row.src || "";
|
|
|
const name = row.real_name || row.name || row.file_name || `素材#${id || ""}`;
|
|
const name = row.real_name || row.name || row.file_name || `素材#${id || ""}`;
|
|
|
- return { id, name, url, raw: row };
|
|
|
|
|
- },
|
|
|
|
|
- isSelected(id) {
|
|
|
|
|
- if (id == null || id === "") return false;
|
|
|
|
|
- return !!this.selectedMap[String(id)];
|
|
|
|
|
|
|
+ const ft =
|
|
|
|
|
+ row.file_type != null && row.file_type !== ""
|
|
|
|
|
+ ? String(row.file_type)
|
|
|
|
|
+ : String(defaultType ?? this.fileType);
|
|
|
|
|
+ const file_type = ft === "2" ? "2" : "1";
|
|
|
|
|
+ return { id, name, url, file_type, raw: row };
|
|
|
},
|
|
},
|
|
|
async loadCategories() {
|
|
async loadCategories() {
|
|
|
this.categoryLoading = true;
|
|
this.categoryLoading = true;
|
|
@@ -429,7 +515,9 @@ export default {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
const { list, total } = this.parseFileRows(res.data);
|
|
const { list, total } = this.parseFileRows(res.data);
|
|
|
- this.fileList = list.map((row) => this.normalizeFileRow(row)).filter((x) => x.id != null);
|
|
|
|
|
|
|
+ this.fileList = list
|
|
|
|
|
+ .map((row) => this.normalizeFileRow(row, this.fileType))
|
|
|
|
|
+ .filter((x) => x.id != null);
|
|
|
this.total = total;
|
|
this.total = total;
|
|
|
} finally {
|
|
} finally {
|
|
|
this.listLoading = false;
|
|
this.listLoading = false;
|
|
@@ -443,58 +531,66 @@ export default {
|
|
|
this.query.page = page;
|
|
this.query.page = page;
|
|
|
this.loadFiles();
|
|
this.loadFiles();
|
|
|
},
|
|
},
|
|
|
|
|
+ getUniqueSelectedItems() {
|
|
|
|
|
+ const items = [];
|
|
|
|
|
+ const seen = new Set();
|
|
|
|
|
+ Object.values(this.selectedMap).forEach((item) => {
|
|
|
|
|
+ const dedupeKey = item.id ? `id:${item.id}` : item.url ? `url:${item.url}` : "";
|
|
|
|
|
+ if (!dedupeKey || seen.has(dedupeKey)) return;
|
|
|
|
|
+ seen.add(dedupeKey);
|
|
|
|
|
+ items.push(item);
|
|
|
|
|
+ });
|
|
|
|
|
+ return items;
|
|
|
|
|
+ },
|
|
|
toggleSelect(item) {
|
|
toggleSelect(item) {
|
|
|
- const key = String(item.id);
|
|
|
|
|
- if (this.isSelected(item.id)) {
|
|
|
|
|
- const next = { ...this.selectedMap };
|
|
|
|
|
- delete next[key];
|
|
|
|
|
- this.selectedMap = next;
|
|
|
|
|
|
|
+ if (this.isSelectedItem(item)) {
|
|
|
|
|
+ this.removeSelectedByItem(item);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (!this.multiple) {
|
|
if (!this.multiple) {
|
|
|
- this.selectedMap = { [key]: item };
|
|
|
|
|
|
|
+ this.selectedMap = {};
|
|
|
|
|
+ this.putSelectedItem(item);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- if (this.max > 0 && this.selectedCount >= this.max) {
|
|
|
|
|
|
|
+ if (this.max > 0 && this.getUniqueSelectedItems().length >= this.max) {
|
|
|
this.$message.warning(`最多选择 ${this.max} 个`);
|
|
this.$message.warning(`最多选择 ${this.max} 个`);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.selectedMap = { ...this.selectedMap, [key]: item };
|
|
|
|
|
|
|
+ this.putSelectedItem(item);
|
|
|
},
|
|
},
|
|
|
onSelectAllChange(checked) {
|
|
onSelectAllChange(checked) {
|
|
|
if (!checked) {
|
|
if (!checked) {
|
|
|
const next = { ...this.selectedMap };
|
|
const next = { ...this.selectedMap };
|
|
|
this.fileList.forEach((f) => {
|
|
this.fileList.forEach((f) => {
|
|
|
- delete next[String(f.id)];
|
|
|
|
|
|
|
+ const k = this.findSelectedKeyForItem(f);
|
|
|
|
|
+ if (k) delete next[k];
|
|
|
});
|
|
});
|
|
|
this.selectedMap = next;
|
|
this.selectedMap = next;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (!this.multiple) {
|
|
if (!this.multiple) {
|
|
|
if (this.fileList[0]) {
|
|
if (this.fileList[0]) {
|
|
|
- const f = this.fileList[0];
|
|
|
|
|
- this.selectedMap = { [String(f.id)]: f };
|
|
|
|
|
|
|
+ this.selectedMap = {};
|
|
|
|
|
+ this.putSelectedItem(this.fileList[0]);
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const next = { ...this.selectedMap };
|
|
|
|
|
let hitMax = false;
|
|
let hitMax = false;
|
|
|
for (const f of this.fileList) {
|
|
for (const f of this.fileList) {
|
|
|
- if (this.max > 0 && Object.keys(next).length >= this.max) {
|
|
|
|
|
|
|
+ if (this.max > 0 && this.getUniqueSelectedItems().length >= this.max) {
|
|
|
hitMax = true;
|
|
hitMax = true;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- if (!this.isSelected(f.id)) {
|
|
|
|
|
- next[String(f.id)] = f;
|
|
|
|
|
|
|
+ if (!this.isSelectedItem(f)) {
|
|
|
|
|
+ this.putSelectedItem(f);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- this.selectedMap = next;
|
|
|
|
|
if (hitMax) {
|
|
if (hitMax) {
|
|
|
this.$message.warning(`最多选择 ${this.max} 个`);
|
|
this.$message.warning(`最多选择 ${this.max} 个`);
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
confirmUse() {
|
|
confirmUse() {
|
|
|
- const items = Object.values(this.selectedMap);
|
|
|
|
|
|
|
+ const items = this.getUniqueSelectedItems();
|
|
|
if (!items.length) {
|
|
if (!items.length) {
|
|
|
this.$message.warning("请先选择素材");
|
|
this.$message.warning("请先选择素材");
|
|
|
return;
|
|
return;
|
|
@@ -517,7 +613,7 @@ export default {
|
|
|
}
|
|
}
|
|
|
this.previewUrl = item.url;
|
|
this.previewUrl = item.url;
|
|
|
this.previewTitle = item.name || "预览";
|
|
this.previewTitle = item.name || "预览";
|
|
|
- this.previewType = this.fileType === "2" ? "video" : "image";
|
|
|
|
|
|
|
+ this.previewType = this.isVideoItem(item) ? "video" : "image";
|
|
|
this.previewVisible = true;
|
|
this.previewVisible = true;
|
|
|
},
|
|
},
|
|
|
onPreviewClosed() {
|
|
onPreviewClosed() {
|
|
@@ -534,9 +630,7 @@ export default {
|
|
|
this.$message.error(res.msg || res.message || "删除失败");
|
|
this.$message.error(res.msg || res.message || "删除失败");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const next = { ...this.selectedMap };
|
|
|
|
|
- delete next[String(item.id)];
|
|
|
|
|
- this.selectedMap = next;
|
|
|
|
|
|
|
+ this.removeSelectedByItem(item);
|
|
|
this.$message.success(res.msg || res.message || "删除成功");
|
|
this.$message.success(res.msg || res.message || "删除成功");
|
|
|
this.loadFiles();
|
|
this.loadFiles();
|
|
|
},
|
|
},
|
|
@@ -548,19 +642,22 @@ export default {
|
|
|
{ type: "warning" }
|
|
{ type: "warning" }
|
|
|
).catch(() => {});
|
|
).catch(() => {});
|
|
|
if (confirm !== "confirm") return;
|
|
if (confirm !== "confirm") return;
|
|
|
- const ids = this.selectedIds;
|
|
|
|
|
|
|
+ const items = this.getUniqueSelectedItems();
|
|
|
|
|
+ const ids = items.map((i) => i.id).filter((id) => id != null && id !== "");
|
|
|
const res = await this.$API.material.fileDelete.delete(ids);
|
|
const res = await this.$API.material.fileDelete.delete(ids);
|
|
|
if (!this.apiOk(res)) {
|
|
if (!this.apiOk(res)) {
|
|
|
this.$message.error(res.msg || res.message || "删除失败");
|
|
this.$message.error(res.msg || res.message || "删除失败");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const next = { ...this.selectedMap };
|
|
|
|
|
- ids.forEach((id) => delete next[String(id)]);
|
|
|
|
|
- this.selectedMap = next;
|
|
|
|
|
|
|
+ this.selectedMap = {};
|
|
|
this.$message.success(res.msg || res.message || "删除成功");
|
|
this.$message.success(res.msg || res.message || "删除成功");
|
|
|
this.loadFiles();
|
|
this.loadFiles();
|
|
|
},
|
|
},
|
|
|
uploadRequest(param) {
|
|
uploadRequest(param) {
|
|
|
|
|
+ const uploadType =
|
|
|
|
|
+ this.isMixedMode && param.file?.type?.startsWith("video/")
|
|
|
|
|
+ ? "2"
|
|
|
|
|
+ : this.fileType;
|
|
|
const data = new FormData();
|
|
const data = new FormData();
|
|
|
data.append(uploadConfig.filename || "file", param.file);
|
|
data.append(uploadConfig.filename || "file", param.file);
|
|
|
if (this.activeCategoryId !== "") {
|
|
if (this.activeCategoryId !== "") {
|
|
@@ -568,7 +665,7 @@ export default {
|
|
|
data.append("category_id", String(this.activeCategoryId));
|
|
data.append("category_id", String(this.activeCategoryId));
|
|
|
}
|
|
}
|
|
|
this.$API.material.upload
|
|
this.$API.material.upload
|
|
|
- .post(this.fileType, data, {
|
|
|
|
|
|
|
+ .post(uploadType, data, {
|
|
|
onUploadProgress: (e) => {
|
|
onUploadProgress: (e) => {
|
|
|
if (e.total) {
|
|
if (e.total) {
|
|
|
const percent = Math.round((e.loaded / e.total) * 100);
|
|
const percent = Math.round((e.loaded / e.total) * 100);
|
|
@@ -697,31 +794,41 @@ export default {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
}
|
|
}
|
|
|
.picker-grid {
|
|
.picker-grid {
|
|
|
|
|
+ --picker-thumb-size: 120px;
|
|
|
display: grid;
|
|
display: grid;
|
|
|
- grid-template-columns: repeat(6, 1fr);
|
|
|
|
|
|
|
+ grid-template-columns: repeat(auto-fill, var(--picker-thumb-size));
|
|
|
gap: 12px;
|
|
gap: 12px;
|
|
|
padding: 4px 4px 12px;
|
|
padding: 4px 4px 12px;
|
|
|
|
|
+ justify-content: start;
|
|
|
}
|
|
}
|
|
|
.picker-item {
|
|
.picker-item {
|
|
|
|
|
+ width: var(--picker-thumb-size);
|
|
|
user-select: none;
|
|
user-select: none;
|
|
|
}
|
|
}
|
|
|
.picker-thumb {
|
|
.picker-thumb {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- width: 100%;
|
|
|
|
|
- aspect-ratio: 1;
|
|
|
|
|
|
|
+ width: var(--picker-thumb-size);
|
|
|
|
|
+ height: var(--picker-thumb-size);
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
border: 1px solid var(--el-border-color-lighter);
|
|
border: 1px solid var(--el-border-color-lighter);
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
background: var(--el-fill-color-lighter);
|
|
background: var(--el-fill-color-lighter);
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
-.picker-thumb .el-image {
|
|
|
|
|
|
|
+.picker-thumb :deep(.el-image) {
|
|
|
|
|
+ display: block;
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
}
|
|
}
|
|
|
-.video-thumb {
|
|
|
|
|
|
|
+.picker-thumb :deep(.el-image__inner) {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
|
|
+ object-fit: cover;
|
|
|
|
|
+}
|
|
|
|
|
+.video-thumb {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ inset: 0;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
@@ -730,7 +837,10 @@ export default {
|
|
|
.video-thumb video {
|
|
.video-thumb video {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ max-height: 100%;
|
|
|
object-fit: cover;
|
|
object-fit: cover;
|
|
|
|
|
+ display: block;
|
|
|
}
|
|
}
|
|
|
.video-icon {
|
|
.video-icon {
|
|
|
font-size: 32px;
|
|
font-size: 32px;
|
|
@@ -767,6 +877,7 @@ export default {
|
|
|
display: none;
|
|
display: none;
|
|
|
}
|
|
}
|
|
|
.picker-name {
|
|
.picker-name {
|
|
|
|
|
+ width: var(--picker-thumb-size);
|
|
|
margin: 6px 0 0;
|
|
margin: 6px 0 0;
|
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
|
text-align: center;
|
|
text-align: center;
|