<template>
    <span class="sc-material-picker">
        <span class="sc-material-picker__trigger" @click="open">
            <slot>
                <el-button type="primary">{{ buttonText }}</el-button>
            </slot>
        </span>

        <el-dialog
            v-model="visible"
            :title="dialogTitle"
            width="1120px"
            class="sc-material-picker-dialog"
            append-to-body
            destroy-on-close
            :close-on-click-modal="false"
            @closed="onDialogClosed"
        >
            <el-tabs
                v-if="!lockFileType"
                v-model="fileType"
                class="picker-tabs"
                @tab-change="onFileTypeChange"
            >
                <el-tab-pane label="图片" name="1" />
                <el-tab-pane label="视频" name="2" />
            </el-tabs>

            <div class="picker-body">
                <aside class="picker-side" v-loading="categoryLoading">
                    <el-input
                        v-model="categoryKeyword"
                        size="small"
                        clearable
                        placeholder="请输入分类名称"
                        class="category-search"
                        @keyup.enter="onCategorySearchEnter"
                        @clear="onCategorySearchClear"
                    >
                        <template #prefix>
                            <el-icon><el-icon-search /></el-icon>
                        </template>
                    </el-input>
                    <el-scrollbar class="category-scroll">
                        <ul class="category-list">
                            <li
                                :class="{ active: activeCategoryId === '' }"
                                @click="selectCategory('')"
                            >
                                <el-icon><el-icon-folder /></el-icon>
                                <span>{{ allCategoryLabel }}</span>
                            </li>
                            <li
                                v-for="item in filteredCategories"
                                :key="item.id"
                                :class="{ active: String(activeCategoryId) === String(item.id) }"
                                :style="{ paddingLeft: 12 + item.level * 14 + 'px' }"
                                @click="selectCategory(item.id)"
                            >
                                <el-icon><el-icon-folder /></el-icon>
                                <span class="category-name" :title="item.name">{{ item.name }}</span>
                            </li>
                            <li v-if="categoryKeyword && !filteredCategories.length" class="category-empty">
                                无匹配分类
                            </li>
                        </ul>
                    </el-scrollbar>
                </aside>

                <section class="picker-main">
                    <div class="picker-toolbar">
                        <div class="picker-toolbar-left">
                            <el-button type="primary" :disabled="!selectedCount" @click="confirmUse">
                                {{ confirmText }}
                            </el-button>
                            <el-upload
                                class="picker-upload"
                                action=""
                                multiple
                                :show-file-list="false"
                                :accept="uploadAccept"
                                :http-request="uploadRequest"
                            >
                                <el-button>{{ uploadBtnText }}</el-button>
                            </el-upload>
                            <el-button
                                type="danger"
                                plain
                                :disabled="!selectedCount"
                                @click="removeSelected"
                            >
                                {{ deleteBtnText }}
                            </el-button>
                        </div>
                        <el-input
                            v-model="keyword"
                            clearable
                            :placeholder="searchPlaceholder"
                            style="width: 240px"
                            @keyup.enter="searchFiles"
                            @clear="searchFiles"
                        >
                            <template #append>
                                <el-button :icon="Search" @click="searchFiles" />
                            </template>
                        </el-input>
                    </div>

                    <div v-loading="listLoading" class="picker-grid-wrap">
                        <el-empty v-if="!fileList.length && !listLoading" description="暂无素材" />
                        <el-scrollbar v-else class="picker-grid-scroll">
                            <div class="picker-grid">
                                <div
                                    v-for="item in fileList"
                                    :key="item.id"
                                    class="picker-item"
                                    :class="{ selected: isSelectedItem(item) }"
                                >
                                    <div class="picker-thumb" @click="toggleSelect(item)">
                                        <el-image
                                            v-if="!isVideoItem(item)"
                                            :src="item.url"
                                            fit="cover"
                                            lazy
                                        />
                                        <div v-else class="video-thumb">
                                            <video v-if="item.url" :src="item.url" preload="metadata" />
                                            <el-icon v-else class="video-icon"><el-icon-video-play /></el-icon>
                                        </div>
                                        <div class="picker-check">
                                            <el-icon v-if="isSelectedItem(item)"><el-icon-check /></el-icon>
                                        </div>
                                    </div>
                                    <p class="picker-name" :title="item.name">{{ item.name }}</p>
                                    <div class="picker-actions">
                                        <span @click.stop="previewItem(item)">查看</span>
                                        <span class="danger" @click.stop="removeOne(item)">删除</span>
                                    </div>
                                </div>
                            </div>
                        </el-scrollbar>
                    </div>

                    <div class="picker-footer">
                        <div class="picker-footer-left">
                            <el-checkbox
                                :model-value="allSelected"
                                :indeterminate="indeterminate"
                                @change="onSelectAllChange"
                            >
                                全选
                            </el-checkbox>
                            <span class="selected-tip">{{ selectedCountTip }}</span>
                        </div>
                        <el-pagination
                            v-if="total > 0"
                            background
                            layout="total, prev, pager, next, jumper"
                            :total="total"
                            :current-page="query.page"
                            :page-size="query.limit"
                            @current-change="handlePageChange"
                        />
                    </div>
                </section>
            </div>
        </el-dialog>

        <el-dialog
            v-model="previewVisible"
            :title="previewTitle"
            width="auto"
            class="material-preview-dialog"
            append-to-body
            destroy-on-close
            align-center
            @closed="onPreviewClosed"
        >
            <img v-if="previewType === 'image' && previewUrl" :src="previewUrl" class="preview-media" alt="" />
            <video
                v-else-if="previewType === 'video' && previewUrl"
                :src="previewUrl"
                class="preview-media"
                controls
                autoplay
            />
        </el-dialog>
    </span>
</template>

<script>
import { Search } from "@element-plus/icons-vue";
import uploadConfig from "@/config/upload";

export default {
    name: "ScMaterialPicker",
    props: {
        /** 选中值：对象数组 { id, url, name } 或 url 字符串/数组（由 valueType 决定） */
        modelValue: { type: [Array, Object, String], default: () => [] },
        /** 是否多选 */
        multiple: { type: Boolean, default: true },
        /** 最多可选数量，0 不限制 */
        max: { type: Number, default: 0 },
        /** 弹窗标题 */
        title: { type: String, default: "" },
        /** 默认触发按钮文案 */
        buttonText: { type: String, default: "选择素材" },
        /** 锁定类型：'1' 仅图片，'2' 仅视频，空则 Tab 切换 */
        lockFileType: { type: String, default: "" },
        /** 是否允许同时选中图片与视频（切换 Tab 不清空已选） */
        acceptBoth: { type: Boolean, default: true },
        /** 返回值类型 object | url */
        valueType: { type: String, default: "object" },
    },
    emits: ["update:modelValue", "confirm", "open", "close"],
    data() {
        return {
            Search,
            visible: false,
            fileType: "1",
            keyword: "",
            categoryKeyword: "",
            activeCategoryId: "",
            categoryLoading: false,
            listLoading: false,
            flatCategories: [],
            fileList: [],
            selectedMap: {},
            total: 0,
            query: { page: 1, limit: 18 },
            previewVisible: false,
            previewUrl: "",
            previewTitle: "",
            previewType: "image",
        };
    },
    computed: {
        /** 有 Tab 且未锁定单一类型时，可混选图片+视频 */
        isMixedMode() {
            return this.acceptBoth && !this.lockFileType;
        },
        dialogTitle() {
            if (this.title) return this.title;
            if (this.isMixedMode) return "选择素材";
            return this.fileType === "2" ? "上传视频" : "上传商品图";
        },
        confirmText() {
            if (this.isMixedMode) return "使用选中素材";
            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() {
            if (this.isMixedMode) return "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() {
            const kw = (this.categoryKeyword || "").trim().toLowerCase();
            if (!kw) return this.flatCategories;
            const matched = this.flatCategories.filter((c) =>
                String(c.name).toLowerCase().includes(kw)
            );
            if (!this.activeCategoryId) return matched;
            const active = this.flatCategories.find(
                (c) => String(c.id) === String(this.activeCategoryId)
            );
            if (active && !matched.some((c) => String(c.id) === String(active.id))) {
                return [active, ...matched];
            }
            return matched;
        },
        selectedCount() {
            return this.getUniqueSelectedItems().length;
        },
        allSelected() {
            if (!this.fileList.length) return false;
            return this.fileList.every((f) => this.isSelectedItem(f));
        },
        indeterminate() {
            if (!this.fileList.length || this.allSelected) return false;
            return this.fileList.some((f) => this.isSelectedItem(f));
        },
    },
    watch: {
        modelValue: {
            deep: true,
            handler() {
                if (this.visible) this.initSelectionFromModel();
            },
        },
    },
    methods: {
        apiOk(res) {
            if (!res) return false;
            const code = res.code ?? res.status;
            return code === 1 || code === 200;
        },
        open() {
            if (this.lockFileType) {
                this.fileType = String(this.lockFileType);
            }
            this.visible = true;
            this.initSelectionFromModel();
            this.$emit("open");
            this.bootstrap();
        },
        close() {
            this.visible = false;
        },
        onDialogClosed() {
            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() {
            const raw = this.modelValue;
            const list = Array.isArray(raw) ? raw : raw ? [raw] : [];
            this.selectedMap = {};
            list.forEach((item) => {
                if (typeof item === "string") {
                    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({
                        att_id: item.id ?? item.att_id,
                        att_dir: item.url ?? item.att_dir,
                        real_name: item.name ?? item.real_name,
                        file_type: item.file_type,
                        ...item,
                    });
                    this.putSelectedItem(n);
                }
            });
        },
        /** 编辑回显：按 att_id 或 url 与已选记录匹配 */
        isSelectedItem(item) {
            return !!this.findSelectedKeyForItem(item);
        },
        async bootstrap() {
            await this.loadCategories();
            await this.loadFiles();
        },
        onFileTypeChange() {
            this.activeCategoryId = "";
            this.keyword = "";
            this.categoryKeyword = "";
            this.query.page = 1;
            if (!this.isMixedMode && this.multiple) {
                this.selectedMap = {};
            }
            this.loadCategories();
            this.loadFiles();
        },
        isVideoItem(item) {
            return String(item?.file_type ?? "") === "2";
        },
        onCategorySearchClear() {
            this.categoryKeyword = "";
        },
        onCategorySearchEnter() {
            const kw = (this.categoryKeyword || "").trim();
            if (!kw) {
                this.selectCategory("");
                return;
            }
            const list = this.filteredCategories;
            if (!list.length) return;
            const exact = list.find((c) => String(c.name).toLowerCase() === kw.toLowerCase());
            this.selectCategory((exact || list[0]).id);
        },
        selectCategory(id) {
            this.activeCategoryId = id === "" ? "" : String(id);
            this.categoryKeyword = "";
            this.query.page = 1;
            this.loadFiles();
        },
        flattenCategories(nodes, level = 0, out = []) {
            (nodes || []).forEach((node) => {
                const id = node.id ?? node.cate_id ?? node.value;
                const name = node.name ?? node.title ?? node.label ?? node.cate_name ?? `分类#${id}`;
                if (id != null && id !== "") {
                    out.push({
                        id,
                        name,
                        level,
                        raw: node,
                    });
                }
                const children = node.children || node.child || [];
                if (children.length) this.flattenCategories(children, level + 1, out);
            });
            return out;
        },
        parseCategoryRows(data) {
            if (Array.isArray(data)) return data;
            const d = data || {};
            if (Array.isArray(d.list)) return d.list;
            if (Array.isArray(d.data)) return d.data;
            return d.children || [];
        },
        parseFileRows(data) {
            if (Array.isArray(data)) return { list: data, total: data.length };
            const d = data || {};
            const list = d.list || d.data || d.rows || [];
            const total = Number(d.count ?? d.total ?? list.length ?? 0);
            return { list, total };
        },
        normalizeFileRow(row, defaultType) {
            const rawId = row.att_id ?? row.id;
            const id = rawId != null && rawId !== "" ? String(rawId) : "";
            const url = row.att_dir || row.satt_dir || row.url || row.src || "";
            const name = row.real_name || row.name || row.file_name || `素材#${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() {
            this.categoryLoading = true;
            try {
                const res = await this.$API.material.categoryList.get(this.fileType);
                if (!this.apiOk(res)) {
                    this.flatCategories = [];
                    return;
                }
                this.flatCategories = this.flattenCategories(this.parseCategoryRows(res.data));
            } finally {
                this.categoryLoading = false;
            }
        },
        buildFileQuery() {
            const params = {
                file_type: this.fileType,
                real_name: this.keyword || "",
                page: this.query.page,
                limit: this.query.limit,
            };
            if (this.activeCategoryId !== "") {
                params.pid = this.activeCategoryId;
                params.category_id = this.activeCategoryId;
                params.cate_id = this.activeCategoryId;
                params.relation_id = this.activeCategoryId;
            }
            return params;
        },
        async loadFiles() {
            this.listLoading = true;
            try {
                const res = await this.$API.material.fileList.get(this.buildFileQuery());
                if (!this.apiOk(res)) {
                    this.fileList = [];
                    this.total = 0;
                    return;
                }
                const { list, total } = this.parseFileRows(res.data);
                this.fileList = list
                    .map((row) => this.normalizeFileRow(row, this.fileType))
                    .filter((x) => x.id != null);
                this.total = total;
            } finally {
                this.listLoading = false;
            }
        },
        searchFiles() {
            this.query.page = 1;
            this.loadFiles();
        },
        handlePageChange(page) {
            this.query.page = page;
            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) {
            if (this.isSelectedItem(item)) {
                this.removeSelectedByItem(item);
                return;
            }
            if (!this.multiple) {
                this.selectedMap = {};
                this.putSelectedItem(item);
                return;
            }
            if (this.max > 0 && this.getUniqueSelectedItems().length >= this.max) {
                this.$message.warning(`最多选择 ${this.max} 个`);
                return;
            }
            this.putSelectedItem(item);
        },
        onSelectAllChange(checked) {
            if (!checked) {
                const next = { ...this.selectedMap };
                this.fileList.forEach((f) => {
                    const k = this.findSelectedKeyForItem(f);
                    if (k) delete next[k];
                });
                this.selectedMap = next;
                return;
            }
            if (!this.multiple) {
                if (this.fileList[0]) {
                    this.selectedMap = {};
                    this.putSelectedItem(this.fileList[0]);
                }
                return;
            }
            let hitMax = false;
            for (const f of this.fileList) {
                if (this.max > 0 && this.getUniqueSelectedItems().length >= this.max) {
                    hitMax = true;
                    break;
                }
                if (!this.isSelectedItem(f)) {
                    this.putSelectedItem(f);
                }
            }
            if (hitMax) {
                this.$message.warning(`最多选择 ${this.max} 个`);
            }
        },
        confirmUse() {
            const items = this.getUniqueSelectedItems();
            if (!items.length) {
                this.$message.warning("请先选择素材");
                return;
            }
            let value;
            if (this.valueType === "url") {
                const urls = items.map((i) => i.url);
                value = this.multiple ? urls : urls[0];
            } else {
                value = this.multiple ? items : items[0];
            }
            this.$emit("update:modelValue", value);
            this.$emit("confirm", value, items);
            this.close();
        },
        previewItem(item) {
            if (!item?.url) {
                this.$message.warning("暂无预览地址");
                return;
            }
            this.previewUrl = item.url;
            this.previewTitle = item.name || "预览";
            this.previewType = this.isVideoItem(item) ? "video" : "image";
            this.previewVisible = true;
        },
        onPreviewClosed() {
            this.previewUrl = "";
            this.previewTitle = "";
        },
        async removeOne(item) {
            const confirm = await this.$confirm(`确认删除「${item.name}」吗？`, "提示", {
                type: "warning",
            }).catch(() => {});
            if (confirm !== "confirm") return;
            const res = await this.$API.material.fileDelete.delete([item.id]);
            if (!this.apiOk(res)) {
                this.$message.error(res.msg || res.message || "删除失败");
                return;
            }
            this.removeSelectedByItem(item);
            this.$message.success(res.msg || res.message || "删除成功");
            this.loadFiles();
        },
        async removeSelected() {
            if (!this.selectedCount) return;
            const confirm = await this.$confirm(
                `确认删除选中的 ${this.selectedCount} 个素材吗？`,
                "提示",
                { type: "warning" }
            ).catch(() => {});
            if (confirm !== "confirm") return;
            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);
            if (!this.apiOk(res)) {
                this.$message.error(res.msg || res.message || "删除失败");
                return;
            }
            this.selectedMap = {};
            this.$message.success(res.msg || res.message || "删除成功");
            this.loadFiles();
        },
        uploadRequest(param) {
            const uploadType =
                this.isMixedMode && param.file?.type?.startsWith("video/")
                    ? "2"
                    : this.fileType;
            const data = new FormData();
            data.append(uploadConfig.filename || "file", param.file);
            if (this.activeCategoryId !== "") {
                data.append("pid", String(this.activeCategoryId));
                data.append("category_id", String(this.activeCategoryId));
            }
            this.$API.material.upload
                .post(uploadType, data, {
                    onUploadProgress: (e) => {
                        if (e.total) {
                            const percent = Math.round((e.loaded / e.total) * 100);
                            param.onProgress({ percent });
                        }
                    },
                })
                .then((res) => {
                    const parsed = uploadConfig.parseData(res);
                    const uploadCode = parsed.code ?? res.status ?? res.code;
                    if (uploadCode == uploadConfig.successCode || this.apiOk(res)) {
                        param.onSuccess(res);
                        this.loadFiles();
                    } else {
                        param.onError(parsed.msg || res.msg || "上传失败");
                    }
                })
                .catch((err) => {
                    param.onError(err);
                });
        },
    },
};
</script>

<style scoped>
.sc-material-picker {
    display: inline-block;
}
.sc-material-picker__trigger {
    display: inline-block;
}
.sc-material-picker-dialog :deep(.el-dialog__body) {
    padding: 0 20px 16px;
}
.picker-tabs {
    margin-bottom: 0;
}
.picker-body {
    display: flex;
    height: 560px;
    border-top: 1px solid var(--el-border-color-lighter);
    margin-top: 8px;
}
.picker-side {
    width: 168px;
    flex-shrink: 0;
    border-right: 1px solid var(--el-border-color-lighter);
    display: flex;
    flex-direction: column;
    padding: 10px 0;
}
.category-search {
    margin: 0 8px 8px;
    width: calc(100% - 16px);
}
.category-search :deep(.el-input__wrapper) {
    padding-left: 8px;
    padding-right: 8px;
}
.category-empty {
    padding: 12px;
    font-size: 12px;
    color: var(--el-text-color-secondary);
    text-align: center;
    cursor: default;
}
.category-scroll {
    flex: 1;
    height: 0;
}
.category-list {
    list-style: none;
    margin: 0;
    padding: 0 0 8px;
}
.category-list li {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 9px 12px;
    cursor: pointer;
    font-size: 13px;
}
.category-list li:hover {
    background: var(--el-fill-color-light);
}
.category-list li.active {
    background: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
}
.category-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.picker-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    padding: 12px 0 0 16px;
}
.picker-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}
.picker-toolbar-left {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}
.picker-upload {
    display: inline-block;
}
.picker-grid-wrap {
    flex: 1;
    min-height: 0;
}
.picker-grid-scroll {
    height: 100%;
}
.picker-grid {
    --picker-thumb-size: 120px;
    display: grid;
    grid-template-columns: repeat(auto-fill, var(--picker-thumb-size));
    gap: 12px;
    padding: 4px 4px 12px;
    justify-content: start;
}
.picker-item {
    width: var(--picker-thumb-size);
    user-select: none;
}
.picker-thumb {
    position: relative;
    width: var(--picker-thumb-size);
    height: var(--picker-thumb-size);
    flex-shrink: 0;
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 4px;
    overflow: hidden;
    background: var(--el-fill-color-lighter);
    cursor: pointer;
}
.picker-thumb :deep(.el-image) {
    display: block;
    width: 100%;
    height: 100%;
}
.picker-thumb :deep(.el-image__inner) {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.video-thumb {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #1a1a1a;
}
.video-thumb video {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: cover;
    display: block;
}
.video-icon {
    font-size: 32px;
    color: #fff;
}
.picker-check {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
    width: 18px;
    height: 18px;
    border: 1px solid #fff;
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 12px;
    box-sizing: border-box;
}
.picker-item.selected .picker-thumb {
    border-color: var(--el-color-primary);
    box-shadow: 0 0 0 2px var(--el-color-primary-light-7);
}
.picker-item.selected .picker-check {
    background: var(--el-color-primary);
    border-color: var(--el-color-primary);
}
.picker-item.selected .picker-check .el-icon {
    display: flex;
}
.picker-check .el-icon {
    display: none;
}
.picker-name {
    width: var(--picker-thumb-size);
    margin: 6px 0 0;
    font-size: 12px;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--el-text-color-regular);
}
.picker-actions {
    display: none;
    justify-content: center;
    gap: 12px;
    margin-top: 4px;
    font-size: 12px;
}
.picker-item:hover .picker-actions {
    display: flex;
}
.picker-actions span {
    color: var(--el-color-primary);
    cursor: pointer;
}
.picker-actions span.danger {
    color: var(--el-color-danger);
}
.picker-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-top: 12px;
    border-top: 1px solid var(--el-border-color-lighter);
    flex-wrap: wrap;
    gap: 8px;
}
.picker-footer-left {
    display: flex;
    align-items: center;
    gap: 16px;
}
.selected-tip {
    font-size: 13px;
    color: var(--el-text-color-secondary);
}
.material-preview-dialog :deep(.el-dialog__body) {
    padding: 12px 20px 20px;
    text-align: center;
}
.preview-media {
    display: block;
    max-width: min(90vw, 960px);
    max-height: 75vh;
    margin: 0 auto;
    object-fit: contain;
}
</style>
