PageSourceSearch

https://lsenglish.com.tw/assets/vendor/@windwalker-io/unicorn-next/dist/chunks/field-multi-uploader.js

js lsenglish.com.tw collected 2026-10-06 02:26:35 UTC 10,959 bytes, 263 lines download raw bytes

1import { Modal } from "bootstrap";
2import { createApp, defineComponent, ref, getCurrentInstance, useTemplateRef, computed, onMounted, nextTick } from "vue";
3import { VueDraggable } from "vue-draggable-plus";
4import { ItemCardPlaceholder, ItemCard, MultiUploader, createItem } from "vue-multi-uploader";
5import { a8 as injectCssToDocument, a7 as mergeDeep, ad as forceArray, ac as data, m as useStack } from "./unicorn.js";
6const css = ".vue-drag-uploader {\n  --vmu-img-size: 155px;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 1rem;\n  border: 1px dotted #999;\n  border-radius: 4px;\n  cursor: pointer;\n  color: #999;\n  min-height: 250px;\n}\n.vue-drag-uploader--readonly {\n  border: 1px solid #999;\n}\n.vue-drag-uploader.h-ondrag {\n  border: 1px dotted #666;\n  background-color: rgba(0, 0, 0, 0.05);\n  color: #666;\n}\n.vue-drag-uploader__wrapper {\n  width: 100%;\n}\n.vue-drag-uploader__draggable-wrapper, .vue-drag-uploader__transition-wrapper {\n  display: flex;\n  flex-wrap: wrap;\n}\n.vue-drag-uploader-item {\n  width: var(--vmu-img-size);\n  height: var(--vmu-img-size);\n  border: 1px solid rgba(0, 0, 0, 0.25);\n  border-radius: 3px;\n  cursor: pointer;\n  padding: 0.5rem;\n}\n.vue-drag-uploader .add-button {\n  display: flex;\n  align-items: center;\n  text-align: center;\n  transition: background-color 0.5s;\n}\n.vue-drag-uploader .add-button:hover {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n.vue-drag-uploader .add-button__body {\n  margin: 0 auto;\n}\n.vue-drag-uploader .add-button__icon {\n  margin-bottom: 10px;\n}\n.vue-drag-uploader .add-button__text {\n  font-size: 14px;\n}\n.vue-drag-uploader .preview-img {\n  position: relative;\n  cursor: pointer;\n  padding: 1px;\n}\n.vue-drag-uploader .preview-img > * {\n  position: absolute;\n}\n.vue-drag-uploader .preview-img:hover .error-message__message {\n  display: block;\n  padding: 10px;\n}\n.vue-drag-uploader .preview-img__body {\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  background: transparent no-repeat center center;\n  background-size: cover;\n}\n.vue-drag-uploader .preview-img__title {\n  margin: 0 auto;\n}\n.vue-drag-uploader .preview-img__overlay {\n  display: flex;\n  align-items: center;\n  left: 0;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  background-color: rgba(0, 0, 0, 0.35);\n  opacity: 0;\n  transition: opacity 0.5s;\n}\n.vue-drag-uploader .preview-img__overlay:hover {\n  opacity: 1;\n}\n.vue-drag-uploader .preview-img__remove-icon {\n  position: absolute;\n  top: 5px;\n  right: 5px;\n  color: white;\n  opacity: 0.75;\n  transition: opacity 0.5s;\n}\n.vue-drag-uploader .preview-img__remove-icon:hover {\n  opacity: 1;\n}\n.vue-drag-uploader .preview-img__progress {\n  height: 5px;\n  background-color: rgba(255, 255, 255, 0.8);\n  width: 100%;\n  bottom: 0;\n  left: 0;\n}\n.vue-drag-uploader .preview-img__progress-bar {\n  background-color: var(--bs-primary, #007bff);\n  width: 0;\n  height: 100%;\n}\n.vue-drag-uploader .error-message {\n  width: 100%;\n  background-color: #dc3545;\n  color: white;\n  font-size: 14px;\n  word-break: break-all;\n  top: calc(100% - 26px);\n  min-height: 26px;\n}\n.vue-drag-uploader .error-message__notice {\n  display: inline-block;\n  padding: 3px;\n  text-align: center;\n  width: 100%;\n}\n.vue-drag-uploader .error-message__message {\n  display: none;\n}";
7const defaultOptions = {
8  readonly: false,
9  disabled: false,
10  sortable: false,
11  thumbSize: 150,
12  maxConcurrent: 5,
13  canReplace: false,
14  tmplSelector: "#multi-uploader-field-tmpl"
15};
16class MultiUploaderElement extends HTMLElement {
17  static is = "uni-multi-uploader";
18  modalElement;
19  vm;
20  async connectedCallback() {
21    let options = JSON.parse(
22      this.getAttribute("options") || "{}"
23    );
24    const resolvedOptions = mergeDeep({}, defaultOptions, options);
25    resolvedOptions.thumbSize ??= 150;
26    this.modalElement = this.querySelector(".modal");
27    const tmplSelector = resolvedOptions.tmplSelector;
28    const app = createApp(
29      createAppInstance(resolvedOptions, document.querySelector(tmplSelector).innerHTML, this)
30    );
31    this.vm = app.mount(this);
32  }
33}
34async function init() {
35  injectCssToDocument(css);
36  customElements.define(MultiUploaderElement.is, MultiUploaderElement);
37}
38const ready = /* @__PURE__ */ init();
39function createAppInstance(opt, tmpl, el) {
40  return defineComponent({
41    name: "MultiUploaderFieldApp",
42    template: tmpl,
43    components: {
44      VueDraggable,
45      MultiUploader,
46      ItemCard,
47      ItemCardPlaceholder
48    },
49    props: {
50      stackName: String
51    },
52    setup(props, { expose }) {
53      const options = ref(opt);
54      const current = ref();
55      const currentIndex = ref();
56      const loading = ref(false);
57      const dragarea = ref();
58      const modal = ref();
59      const app = getCurrentInstance();
60      const uploader = useTemplateRef("uploader");
61      const canModify = computed(() => !options.value.disabled && !options.value.readonly);
62      const instance = ref();
63      onMounted(() => {
64        instance.value = uploader.value.instance;
65        domEmit("multi-uploader:mounted", { app, uploader });
66      });
67      const items = ref([]);
68      for (let item of forceArray(options.value.value)) {
69        if (typeof item === "string") {
70          item = {
71            url: item
72          };
73        }
74        const uploadItem = createItem({
75          url: item.url || "",
76          thumbUrl: item.thumbUrl || item.thumb_url || item.url || "",
77          data: item
78        });
79        items.value.push(uploadItem);
80      }
81      const uploadUrl = options.value.uploadUrl;
82      const value = items.value;
83      const uploaderOptions = ref({
84        maxFiles: () => options.value.maxFiles,
85        readonly: () => options.value.readonly,
86        disabled: () => options.value.disabled,
87        accept: () => options.value.accept,
88        maxConcurrent: () => options.value.maxConcurrent,
89        prepareXhr(xhr) {
90          xhr.setRequestHeader(
91            "X-CSRF-TOKEN",
92            data("csrf-token")
93          );
94        },
95        onItemUploadSuccess(item, xhr) {
96          const res = JSON.parse(xhr.responseText);
97          item.url = res.data.url;
98          item.thumbUrl = res.data.thumbUrl || res.data.thumb_url || res.data.url;
99          item.data = res.data;
100          item.data.title ??= item.url.split("/").pop()?.split("?").shift() || "";
101        }
102      });
103      const draggableOptions = {
104        draggable: ".c-drag-item",
105        animation: 300,
106        disabled: !canModify.value
107      };
108      function openFile(item) {
109        if (options.value.openFileHandler) {
110          options.value.openFileHandler(item);
111        } else {
112          window.open(item.download_url || item.url);
113        }
114      }
115      async function itemClick(item, i, event) {
116        current.value = item;
117        currentIndex.value = i;
118        domEmit("item-click", { item, i });
119        nextTick().then(() => {
120          Modal.getOrCreateInstance(modal.value).show();
121        });
122      }
123      function metaSave() {
124        current.value = void 0;
125        currentIndex.value = void 0;
126      }
127      function isImage(url) {
128        const ext = url.split(".").pop().split("?").shift() || "";
129        const allow = ["png", "jpeg", "jpg", "gif", "bmp", "webp"];
130        return allow.indexOf(ext.toLowerCase()) !== -1;
131      }
132      function dragover(e) {
133        if (!options.value.canReplace) {
134          return;
135        }
136        dragarea.value.style.opacity = "0.75";
137      }
138      function dragleave(e) {
139        if (!options.value.canReplace) {
140          return;
141        }
142        dragarea.value.style.opacity = "1";
143      }
144      function drop(event) {
145        if (!options.value.canReplace) {
146          return;
147        }
148        dragarea.value.style.opacity = "1";
149        const item = current.value;
150        const file = event.dataTransfer?.files[0] || null;
151        if (!file) {
152          return;
153        }
154        instance.value.checkFile(file);
155        if (instance.value.isReadonly) {
156          return;
157        }
158        item.file = file;
159        loading.value = true;
160        try {
161          instance.value.uploadFile(item);
162        } finally {
163          loading.value = false;
164        }
165      }
166      function uploading() {
167        useStack(props.stackName).push(true);
168        domEmit("uploading");
169      }
170      function uploaded() {
171        useStack(props.stackName).pop();
172        domEmit("uploaded");
173      }
174      function onChange(item) {
175        domEmit("change", item);
176      }
177      function domEmit(event, detail) {
178        el.dispatchEvent(new CustomEvent(event, { detail }));
179      }
180      const foo = ref();
181      foo.value = "Bar";
182      const icons = ref({
183        default: "fas fa-file",
184        pdf: "fas fa-file-pdf text-danger",
185        xls: "fas fa-file-excel text-success",
186        xlsx: "fas fa-file-excel text-success",
187        doc: "fas fa-file-word text-primary",
188        docx: "fas fa-file-word text-primary",
189        ppt: "fas fa-file-powerpoint text-warning",
190        pptx: "fas fa-file-powerpoint text-warning",
191        zip: "fas fa-file-archive text-dark",
192        "7z": "fas fa-file-archive text-dark",
193        rar: "fas fa-file-archive text-dark",
194        mp4: "fas fa-file-video text-dark",
195        avi: "fas fa-file-video text-dark",
196        flv: "fas fa-file-video text-dark",
197        mov: "fas fa-file-video text-dark",
198        ogg: "fas fa-file-video text-dark",
199        webm: "fas fa-file-video text-dark",
200        mpg: "fas fa-file-video text-dark",
201        mp3: "fas fa-file-audio text-dark",
202        acc: "fas fa-file-audio text-dark",
203        wav: "fas fa-file-audio text-dark"
204      });
205      function setIcons(newIcons, merge = true) {
206        if (merge) {
207          icons.value = { ...icons.value, ...newIcons };
208          return;
209        }
210        icons.value = newIcons;
211      }
212      expose({
213        uploader,
214        instance,
215        value,
216        canModify,
217        openFile,
218        itemClick,
219        isImage,
220        setIcons
221      });
222      function fileIcon(item) {
223        let path = item.file ? item.file.name : item.url;
224        path = String(path).split("?")[0];
225        const ext = path.split(".").pop() || "";
226        const def = "default" in icons ? icons.default : "fas fa-file";
227        return icons[String(ext || "default").toLowerCase()] || def;
228      }
229      return {
230        uploader,
231        uploadUrl,
232        value,
233        uploaderOptions,
234        draggableOptions,
235        modal,
236        dragarea,
237        options,
238        current,
239        currentIndex,
240        loading,
241        instance,
242        canModify,
243        openFile,
244        itemClick,
245        metaSave,
246        isImage,
247        dragover,
248        dragleave,
249        drop,
250        uploading,
251        uploaded,
252        onChange,
253        domEmit,
254        fileIcon
255      };
256    }
257  });
258}
259export {
260  MultiUploaderElement,
261  ready
262};
263//# sourceMappingURL=field-multi-uploader.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.