feat: add drop image product mgmt

This commit is contained in:
Methapon2001 2024-12-23 17:17:39 +07:00
parent bd3a7bc9a4
commit 3eda26bbf7
3 changed files with 126 additions and 21 deletions

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { QSelect } from 'quasar';
import useOptionStore from 'src/stores/options';
import { createEditorImageDrop } from 'src/utils/ui';
import { selectFilterOptionRefMod } from 'stores/utils';
import { ref, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n';
@ -66,6 +67,8 @@ watch(
);
},
);
const detailEditorImageDrop = createEditorImageDrop(detail);
</script>
<template>
@ -209,6 +212,7 @@ watch(
@update:model-value="
(v) => (typeof v === 'string' ? (detail = v) : '')
"
@drop="detailEditorImageDrop"
min-height="5rem"
class="q-mt-sm q-mb-xs"
:flat="!readonly"

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import { createEditorImageDrop } from 'src/utils/ui';
import SelectBranch from '../shared/select/SelectBranch.vue';
const remark = defineModel<string>('remark');
@ -22,6 +23,8 @@ defineProps<{
disableCode?: boolean;
service?: boolean;
}>();
const detailEditorImageDrop = createEditorImageDrop(detail);
</script>
<template>
@ -88,18 +91,40 @@ defineProps<{
v-model="name"
:rules="[(val: string) => !!val || $t('form.error.required')]"
/>
<q-input
for="input-detail"
:dense="dense"
<q-field
class="full-width"
outlined
for="input-detail"
id="input-detail"
:readonly="readonly"
hide-bottom-space
type="textarea"
class="col-12"
:borderless="readonly"
:label="$t('general.detail')"
:model-value="readonly ? detail || '-' : detail"
@update:model-value="(v) => (typeof v === 'string' ? (detail = v) : '')"
/>
stack-label
dense
>
<q-editor
dense
:model-value="readonly ? detail || '-' : detail || ''"
@update:model-value="
(v) => (typeof v === 'string' ? (detail = v) : '')
"
@drop="detailEditorImageDrop"
min-height="5rem"
class="q-mt-sm q-mb-xs"
:flat="!readonly"
:readonly="readonly"
:toolbar-color="
readonly ? 'disabled' : $q.dark.isActive ? 'white' : ''
"
:toolbar-toggle-color="readonly ? 'disabled' : 'primary'"
style="
cursor: auto;
color: var(--foreground);
border-color: var(--surface-3);
"
:style="`width: ${$q.screen.gt.xs ? '100%' : '63vw'}`"
/>
</q-field>
<q-input
:dense="dense"
outlined
@ -142,21 +167,40 @@ defineProps<{
:rules="[(val: string) => !!val || $t('form.error.required')]"
/>
<q-input
id="input-service-description"
for="input-service-description"
:dense="dense"
<q-field
class="full-width"
outlined
for="input-service-description"
id="input-service-description"
:readonly="readonly"
hide-bottom-space
type="textarea"
class="col-12"
:borderless="readonly"
:label="$t('general.detail')"
:model-value="readonly ? serviceDescription || '-' : serviceDescription"
@update:model-value="
(v) => (typeof v === 'string' ? (serviceDescription = v) : '')
"
/>
stack-label
dense
>
<q-editor
dense
:model-value="readonly ? detail || '-' : detail || ''"
@update:model-value="
(v) => (typeof v === 'string' ? (detail = v) : '')
"
@drop="detailEditorImageDrop"
min-height="5rem"
class="q-mt-sm q-mb-xs"
:flat="!readonly"
:readonly="readonly"
:toolbar-color="
readonly ? 'disabled' : $q.dark.isActive ? 'white' : ''
"
:toolbar-toggle-color="readonly ? 'disabled' : 'primary'"
style="
cursor: auto;
color: var(--foreground);
border-color: var(--surface-3);
"
:style="`width: ${$q.screen.gt.xs ? '100%' : '63vw'}`"
/>
</q-field>
</div>
</div>
</template>

View file

@ -1,6 +1,8 @@
import { ModelRef, Ref } from 'vue';
import { Dark } from 'quasar';
import { setLocale as setDateTimeLocale } from './datetime';
import { i18n } from 'src/boot/i18n';
import { fileToBase64 } from './file';
export enum Theme {
Light = 'light',
@ -75,3 +77,58 @@ export function setLang(lang: Lang): Lang {
return lang;
}
/**
* This is for use with ContentEditable element and with q-editor
*/
export function createEditorImageDrop(ref: Ref<string> | ModelRef<string>) {
return async (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
const target = e.target;
if (!target || !(target instanceof HTMLElement)) return;
const items = e.dataTransfer?.items;
const promises: Promise<string>[] = [];
if (!items) return;
for (let i = 0; i < items.length; i++) {
const file = items[i].getAsFile();
if (!file || file.type.indexOf('image') === -1) continue;
promises.push(fileToBase64(file));
}
const getCaret = () => {
if (target.isContentEditable || document.designMode === 'on') {
target.focus();
const range = document.getSelection()?.getRangeAt(0);
if (!range?.collapsed) return null;
const tmp = document.createTextNode('\0');
range.insertNode(tmp);
const pos = target.innerHTML.indexOf('\0');
tmp.parentNode?.removeChild(tmp);
return pos;
}
return null;
};
for (const base64 of await Promise.all(promises)) {
const image = document.createElement('img') as HTMLImageElement;
const caret = getCaret();
image.src = base64;
if (caret) {
ref.value =
ref.value.substring(0, caret) +
image.outerHTML +
ref.value.substring(caret, ref.value.length);
} else {
ref.value += image.outerHTML;
}
}
};
}