mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
commit
4ddf2bf81e
@ -21,8 +21,7 @@ export const sqliteTypeMap: Record<string, KnexColumnType> = {
|
||||
DynamicLink: 'text',
|
||||
Password: 'text',
|
||||
Select: 'text',
|
||||
File: 'binary',
|
||||
Attach: 'text',
|
||||
Attachment: 'text',
|
||||
AttachImage: 'text',
|
||||
Color: 'text',
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ import { Money } from 'pesa';
|
||||
import { Field, FieldTypeEnum, RawValue, TargetField } from 'schemas/types';
|
||||
import { getIsNullOrUndef } from 'utils';
|
||||
import { DatabaseHandler } from './dbHandler';
|
||||
import { DocValue, DocValueMap, RawValueMap } from './types';
|
||||
import { Attachment, DocValue, DocValueMap, RawValueMap } from './types';
|
||||
|
||||
/**
|
||||
* # Converter
|
||||
@ -71,6 +71,8 @@ export class Converter {
|
||||
return toDocFloat(value, field);
|
||||
case FieldTypeEnum.Check:
|
||||
return toDocCheck(value, field);
|
||||
case FieldTypeEnum.Attachment:
|
||||
return toDocAttachment(value, field);
|
||||
default:
|
||||
return toDocString(value, field);
|
||||
}
|
||||
@ -92,6 +94,8 @@ export class Converter {
|
||||
return toRawCheck(value, field);
|
||||
case FieldTypeEnum.Link:
|
||||
return toRawLink(value, field);
|
||||
case FieldTypeEnum.Attachment:
|
||||
return toRawAttachment(value, field);
|
||||
default:
|
||||
return toRawString(value, field);
|
||||
}
|
||||
@ -273,6 +277,24 @@ function toDocCheck(value: RawValue, field: Field): boolean {
|
||||
throwError(value, field, 'doc');
|
||||
}
|
||||
|
||||
function toDocAttachment(value: RawValue, field: Field): null | Attachment {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
console.log('being thrown doc1', typeof value, value);
|
||||
throwError(value, field, 'doc');
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(value) || null;
|
||||
} catch {
|
||||
console.log('being thrown doc2', typeof value, value);
|
||||
throwError(value, field, 'doc');
|
||||
}
|
||||
}
|
||||
|
||||
function toRawCurrency(value: DocValue, fyo: Fyo, field: Field): string {
|
||||
if (isPesa(value)) {
|
||||
return (value as Money).store;
|
||||
@ -394,6 +416,23 @@ function toRawLink(value: DocValue, field: Field): string | null {
|
||||
throwError(value, field, 'raw');
|
||||
}
|
||||
|
||||
function toRawAttachment(value: DocValue, field: Field): null | string {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
(value as Attachment)?.name &&
|
||||
(value as Attachment)?.data &&
|
||||
(value as Attachment)?.type
|
||||
) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
console.log('being thrown raw', typeof value, value);
|
||||
throwError(value, field, 'raw');
|
||||
}
|
||||
|
||||
function throwError<T>(value: T, field: Field, type: 'raw' | 'doc'): never {
|
||||
throw new ValueError(
|
||||
`invalid ${type} conversion '${value}' of type ${typeof value} found, field: ${JSON.stringify(
|
||||
|
@ -6,7 +6,7 @@ import Observable from 'fyo/utils/observable';
|
||||
import { Schema } from 'schemas/types';
|
||||
import { getRandomString } from 'utils';
|
||||
import { Fyo } from '..';
|
||||
import { DocValueMap } from './types';
|
||||
import { DocValueMap, RawValueMap } from './types';
|
||||
|
||||
export class DocHandler {
|
||||
fyo: Fyo;
|
||||
@ -79,10 +79,11 @@ export class DocHandler {
|
||||
|
||||
getNewDoc(
|
||||
schemaName: string,
|
||||
data: DocValueMap = {},
|
||||
data: DocValueMap | RawValueMap = {},
|
||||
cacheDoc: boolean = true,
|
||||
schema?: Schema,
|
||||
Model?: typeof Doc
|
||||
Model?: typeof Doc,
|
||||
isRawValueMap: boolean = true
|
||||
): Doc {
|
||||
if (!this.models[schemaName] && Model) {
|
||||
this.models[schemaName] = Model;
|
||||
@ -95,7 +96,7 @@ export class DocHandler {
|
||||
throw new NotFoundError(`Schema not found for ${schemaName}`);
|
||||
}
|
||||
|
||||
const doc = new Model!(schema, data, this.fyo);
|
||||
const doc = new Model!(schema, data, this.fyo, isRawValueMap);
|
||||
doc.name ??= getRandomString();
|
||||
if (cacheDoc) {
|
||||
this.#addToCache(doc);
|
||||
|
@ -4,6 +4,7 @@ import { RawValue } from 'schemas/types';
|
||||
import { AuthDemuxBase } from 'utils/auth/types';
|
||||
import { DatabaseDemuxBase } from 'utils/db/types';
|
||||
|
||||
export type Attachment = { name: string; type: string; data: string };
|
||||
export type DocValue =
|
||||
| string
|
||||
| number
|
||||
@ -11,6 +12,7 @@ export type DocValue =
|
||||
| Date
|
||||
| Money
|
||||
| null
|
||||
| Attachment
|
||||
| undefined;
|
||||
export type DocValueMap = Record<string, DocValue | Doc[] | DocValueMap[]>;
|
||||
export type RawValueMap = Record<string, RawValue | RawValueMap[]>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { Converter } from 'fyo/core/converter';
|
||||
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||
import { DocValue, DocValueMap, RawValueMap } from 'fyo/core/types';
|
||||
import { Verb } from 'fyo/telemetry/types';
|
||||
import { DEFAULT_USER } from 'fyo/utils/consts';
|
||||
import { ConflictError, MandatoryError, NotFoundError } from 'fyo/utils/errors';
|
||||
@ -66,7 +66,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
_notInserted: boolean = true;
|
||||
|
||||
_syncing = false;
|
||||
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
|
||||
constructor(
|
||||
schema: Schema,
|
||||
data: DocValueMap,
|
||||
fyo: Fyo,
|
||||
convertToDocValue: boolean = true
|
||||
) {
|
||||
super();
|
||||
this.fyo = markRaw(fyo);
|
||||
this.schema = schema;
|
||||
@ -77,7 +82,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
this._setDefaults();
|
||||
this._setValuesWithoutChecks(data);
|
||||
this._setValuesWithoutChecks(data, convertToDocValue);
|
||||
}
|
||||
|
||||
get schemaName(): string {
|
||||
@ -152,7 +157,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return false;
|
||||
}
|
||||
|
||||
_setValuesWithoutChecks(data: DocValueMap) {
|
||||
_setValuesWithoutChecks(data: DocValueMap, convertToDocValue: boolean) {
|
||||
for (const field of this.schema.fields) {
|
||||
const fieldname = field.fieldname;
|
||||
const value = data[field.fieldname];
|
||||
@ -161,6 +166,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
for (const row of value) {
|
||||
this.push(fieldname, row);
|
||||
}
|
||||
} else if (value !== undefined && !convertToDocValue) {
|
||||
this[fieldname] = value;
|
||||
} else if (value !== undefined) {
|
||||
this[fieldname] = Converter.toDocValue(
|
||||
value as RawValue,
|
||||
@ -362,6 +369,9 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
const childDoc = this.fyo.doc.getNewDoc(
|
||||
childSchemaName,
|
||||
docValueMap,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
childDoc.parentdoc = this;
|
||||
@ -578,7 +588,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
|
||||
async _syncValues(data: DocValueMap) {
|
||||
this._clearValues();
|
||||
this._setValuesWithoutChecks(data);
|
||||
this._setValuesWithoutChecks(data, false);
|
||||
await this._setComputedValuesFromFormulas();
|
||||
this._dirty = false;
|
||||
this.trigger('change', {
|
||||
@ -911,7 +921,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
updateMap.name = updateMap.name + ' CPY';
|
||||
}
|
||||
|
||||
return this.fyo.doc.getNewDoc(this.schemaName, updateMap);
|
||||
const rawUpdateMap = this.fyo.db.converter.toRawValueMap(
|
||||
this.schemaName,
|
||||
updateMap
|
||||
) as RawValueMap;
|
||||
|
||||
return this.fyo.doc.getNewDoc(this.schemaName, rawUpdateMap, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -543,6 +543,9 @@ export class Payment extends Transactional {
|
||||
referenceId: () => this.paymentMethod === 'Cash',
|
||||
clearanceDate: () => this.paymentMethod === 'Cash',
|
||||
amountPaid: () => this.writeoff?.isZero() ?? true,
|
||||
attachment: () =>
|
||||
!(this.attachment || !(this.isSubmitted || this.isCancelled)),
|
||||
for: () => !((this.isSubmitted || this.isCancelled) && this.for?.length),
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
|
@ -30,7 +30,7 @@
|
||||
"luxon": "^2.0.2",
|
||||
"node-fetch": "2",
|
||||
"pesa": "^1.1.12",
|
||||
"vue": "^3.2.36",
|
||||
"vue": "^3.2.40",
|
||||
"vue-router": "^4.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -125,6 +125,12 @@
|
||||
"label": "Notes",
|
||||
"placeholder": "Add invoice terms",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "attachment",
|
||||
"placeholder": "Add attachment",
|
||||
"label": "Attachment",
|
||||
"fieldtype": "Attachment"
|
||||
}
|
||||
],
|
||||
"keywordFields": ["name", "party"]
|
||||
|
@ -101,6 +101,12 @@
|
||||
"create": true,
|
||||
"required": true,
|
||||
"default": "JV-"
|
||||
},
|
||||
{
|
||||
"fieldname": "attachment",
|
||||
"placeholder": "Add attachment",
|
||||
"label": "Attachment",
|
||||
"fieldtype": "Attachment"
|
||||
}
|
||||
],
|
||||
"keywordFields": ["name", "entryType"]
|
||||
|
@ -133,6 +133,12 @@
|
||||
"fieldtype": "Table",
|
||||
"target": "PaymentFor",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"fieldname": "attachment",
|
||||
"placeholder": "Add attachment",
|
||||
"label": "Attachment",
|
||||
"fieldtype": "Attachment"
|
||||
}
|
||||
],
|
||||
"quickEditFields": [
|
||||
@ -149,6 +155,7 @@
|
||||
"amount",
|
||||
"writeoff",
|
||||
"amountPaid",
|
||||
"attachment",
|
||||
"for"
|
||||
],
|
||||
"keywordFields": ["name", "party", "paymentType"]
|
||||
|
@ -14,6 +14,7 @@ export enum FieldTypeEnum {
|
||||
Currency = 'Currency',
|
||||
Text = 'Text',
|
||||
Color = 'Color',
|
||||
Attachment = 'Attachment',
|
||||
}
|
||||
|
||||
export type FieldType = keyof typeof FieldTypeEnum;
|
||||
|
@ -61,6 +61,7 @@
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getDataURL } from 'src/utils/misc';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
import Base from './Base';
|
||||
|
||||
@ -96,21 +97,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataURL = await this.getDataURL(name, data);
|
||||
this.triggerChange(dataURL);
|
||||
},
|
||||
getDataURL(name, data) {
|
||||
const extension = name.split('.').at(-1);
|
||||
const blob = new Blob([data], { type: 'image/' + extension });
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('loadend', () => {
|
||||
resolve(fr.result);
|
||||
});
|
||||
|
||||
fr.readAsDataURL(blob);
|
||||
});
|
||||
const type = 'image/' + extension;
|
||||
const dataURL = await getDataURL(type, data);
|
||||
this.triggerChange(dataURL);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
133
src/components/Controls/Attachment.vue
Normal file
133
src/components/Controls/Attachment.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div :class="containerClasses" class="flex gap-2 items-center">
|
||||
<label
|
||||
for="attachment"
|
||||
class="block whitespace-nowrap overflow-auto no-scrollbar"
|
||||
:class="[inputClasses, !value ? 'text-gray-600' : 'cursor-default']"
|
||||
>{{ label }}</label
|
||||
>
|
||||
<input
|
||||
ref="fileInput"
|
||||
id="attachment"
|
||||
type="file"
|
||||
accept="image/*,.pdf"
|
||||
class="hidden"
|
||||
:disabled="!!value"
|
||||
@input="selectFile"
|
||||
/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="mr-2 flex gap-2">
|
||||
<!-- Upload Button -->
|
||||
<button v-if="!value" class="bg-gray-300 p-0.5 rounded" @click="upload">
|
||||
<FeatherIcon name="upload" class="h-4 w-4 text-gray-600" />
|
||||
</button>
|
||||
|
||||
<!-- Download Button -->
|
||||
<button v-if="value" class="bg-gray-300 p-0.5 rounded" @click="download">
|
||||
<FeatherIcon name="download" class="h-4 w-4 text-gray-600" />
|
||||
</button>
|
||||
|
||||
<!-- Clear Button -->
|
||||
<button
|
||||
v-if="value && !isReadOnly"
|
||||
class="bg-gray-300 p-0.5 rounded"
|
||||
@click="clear"
|
||||
>
|
||||
<FeatherIcon name="x" class="h-4 w-4 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { t } from 'fyo';
|
||||
import { Attachment } from 'fyo/core/types';
|
||||
import { Field } from 'schemas/types';
|
||||
import { convertFileToDataURL } from 'src/utils/misc';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import FeatherIcon from '../FeatherIcon.vue';
|
||||
import Base from './Base.vue';
|
||||
|
||||
export default defineComponent({
|
||||
extends: Base,
|
||||
props: {
|
||||
df: Object as PropType<Field>,
|
||||
value: { type: Object as PropType<Attachment | null>, default: null },
|
||||
border: { type: Boolean, default: false },
|
||||
size: String,
|
||||
},
|
||||
methods: {
|
||||
upload() {
|
||||
(this.$refs.fileInput as HTMLInputElement).click();
|
||||
},
|
||||
clear() {
|
||||
// @ts-ignore
|
||||
this.triggerChange(null);
|
||||
},
|
||||
download() {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, data } = this.value;
|
||||
if (!name || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const a = document.createElement('a') as HTMLAnchorElement;
|
||||
|
||||
a.style.display = 'none';
|
||||
a.href = data;
|
||||
a.target = '_self';
|
||||
a.download = name;
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
},
|
||||
async selectFile(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const attachment = await this.getAttachment(file);
|
||||
// @ts-ignore
|
||||
this.triggerChange(attachment);
|
||||
},
|
||||
async getAttachment(file: File | null) {
|
||||
if (!file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const name = file.name;
|
||||
const type = file.type;
|
||||
const data = await convertFileToDataURL(file, type);
|
||||
return { name, type, data };
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
label() {
|
||||
if (this.value) {
|
||||
return this.value.name;
|
||||
}
|
||||
|
||||
return this.df?.placeholder ?? this.df?.label ?? t`Attachment`;
|
||||
},
|
||||
inputReadOnlyClasses() {
|
||||
if (!this.value) {
|
||||
return 'text-gray-600';
|
||||
} else if (this.isReadOnly) {
|
||||
return 'text-gray-800 cursor-default';
|
||||
}
|
||||
|
||||
return 'text-gray-900';
|
||||
},
|
||||
containerReadOnlyClasses() {
|
||||
return '';
|
||||
},
|
||||
},
|
||||
components: { FeatherIcon },
|
||||
});
|
||||
</script>
|
@ -72,46 +72,66 @@ export default {
|
||||
* These classes will be used by components that extend Base
|
||||
*/
|
||||
|
||||
const classes = [
|
||||
const classes = [];
|
||||
|
||||
classes.push(this.baseInputClasses);
|
||||
if (this.textRight ?? isNumeric(this.df)) {
|
||||
classes.push('text-right');
|
||||
}
|
||||
|
||||
classes.push(this.sizeClasses);
|
||||
classes.push(this.inputReadOnlyClasses);
|
||||
|
||||
return this.getInputClassesFromProp(classes).filter(Boolean);
|
||||
},
|
||||
baseInputClasses() {
|
||||
return [
|
||||
'text-base',
|
||||
'focus:outline-none',
|
||||
'w-full',
|
||||
'placeholder-gray-500',
|
||||
];
|
||||
|
||||
if (this.textRight ?? isNumeric(this.df)) {
|
||||
classes.push('text-right');
|
||||
}
|
||||
|
||||
},
|
||||
sizeClasses() {
|
||||
if (this.size === 'small') {
|
||||
classes.push('px-2 py-1');
|
||||
} else {
|
||||
classes.push('px-3 py-2');
|
||||
return 'px-2 py-1';
|
||||
}
|
||||
|
||||
return 'px-3 py-2';
|
||||
},
|
||||
inputReadOnlyClasses() {
|
||||
if (this.isReadOnly) {
|
||||
classes.push('text-gray-800 cursor-default');
|
||||
} else {
|
||||
classes.push('text-gray-900');
|
||||
return 'text-gray-800 cursor-default';
|
||||
}
|
||||
|
||||
return this.getInputClassesFromProp(classes);
|
||||
return 'text-gray-900';
|
||||
},
|
||||
containerClasses() {
|
||||
/**
|
||||
* Used to accomodate extending compoents where the input is contained in
|
||||
* a div eg AutoComplete
|
||||
*/
|
||||
const classes = ['rounded'];
|
||||
const classes = [];
|
||||
classes.push(this.baseContainerClasses);
|
||||
classes.push(this.containerReadOnlyClasses);
|
||||
classes.push(this.borderClasses);
|
||||
return classes.filter(Boolean);
|
||||
},
|
||||
baseContainerClasses() {
|
||||
return ['rounded'];
|
||||
},
|
||||
containerReadOnlyClasses() {
|
||||
if (!this.isReadOnly) {
|
||||
classes.push('focus-within:bg-gray-100');
|
||||
return 'focus-within:bg-gray-100';
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
borderClasses() {
|
||||
if (this.border) {
|
||||
classes.push('bg-gray-50 border border-gray-200');
|
||||
return 'bg-gray-50 border border-gray-200';
|
||||
}
|
||||
|
||||
return classes;
|
||||
return '';
|
||||
},
|
||||
inputPlaceholder() {
|
||||
return this.placeholder || this.df.placeholder || this.df.label;
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { h } from 'vue';
|
||||
import AttachImage from './AttachImage.vue';
|
||||
import Attachment from './Attachment.vue';
|
||||
import AutoComplete from './AutoComplete.vue';
|
||||
import Check from './Check.vue';
|
||||
import Color from './Color.vue';
|
||||
@ -28,6 +29,7 @@ const components = {
|
||||
DynamicLink,
|
||||
Int,
|
||||
Float,
|
||||
Attachment,
|
||||
Currency,
|
||||
Text,
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ export default {
|
||||
}
|
||||
if (this.ratio.length) {
|
||||
obj['grid-template-columns'] = this.ratio
|
||||
.map((r) => `${r}fr`)
|
||||
.map((r) => `minmax(0, ${r}fr)`)
|
||||
.join(' ');
|
||||
}
|
||||
if (this.gridTemplateColumns) {
|
||||
|
@ -307,7 +307,7 @@ export default {
|
||||
computed: {
|
||||
style() {
|
||||
let templateColumns = (this.columnRatio || [1, 1])
|
||||
.map((r) => `${r}fr`)
|
||||
.map((r) => `minmax(0, ${r}fr)`)
|
||||
.join(' ');
|
||||
return {
|
||||
'grid-template-columns': templateColumns,
|
||||
|
@ -104,6 +104,14 @@
|
||||
@change="(value) => doc.set('account', value)"
|
||||
:read-only="doc?.submitted"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="doc.attachment || !(doc.isSubmitted || doc.isCancelled)"
|
||||
:border="true"
|
||||
:df="getField('attachment')"
|
||||
:value="doc.attachment"
|
||||
@change="(value) => doc.set('attachment', value)"
|
||||
:read-only="doc?.submitted"
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
|
@ -85,6 +85,14 @@
|
||||
@change="(value) => doc.set('referenceDate', value)"
|
||||
:read-only="doc.submitted"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="doc.attachment || !(doc.isSubmitted || doc.isCancelled)"
|
||||
:border="true"
|
||||
:df="getField('attachment')"
|
||||
:value="doc.attachment"
|
||||
@change="(value) => doc.set('attachment', value)"
|
||||
:read-only="doc?.submitted"
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
|
@ -76,6 +76,7 @@
|
||||
|
||||
<!-- Rest of the form -->
|
||||
<TwoColumnForm
|
||||
class="w-full"
|
||||
ref="form"
|
||||
v-if="doc"
|
||||
:doc="doc"
|
||||
|
@ -110,3 +110,22 @@ export const docsPathMap: Record<string, string | undefined> = {
|
||||
Settings: 'miscellaneous/settings',
|
||||
ChartOfAccounts: 'miscellaneous/chart-of-accounts',
|
||||
};
|
||||
|
||||
export async function getDataURL(type: string, data: Uint8Array) {
|
||||
const blob = new Blob([data], { type });
|
||||
|
||||
return new Promise<string>((resolve) => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('loadend', () => {
|
||||
resolve(fr.result as string);
|
||||
});
|
||||
|
||||
fr.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
export async function convertFileToDataURL(file: File, type: string) {
|
||||
const buffer = await file.arrayBuffer();
|
||||
const array = new Uint8Array(buffer);
|
||||
return await getDataURL(type, array);
|
||||
}
|
||||
|
136
yarn.lock
136
yarn.lock
@ -2029,47 +2029,47 @@
|
||||
semver "^7.3.4"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
"@vue/compiler-core@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.36.tgz#2fa44595308c95610602df54dcb69063ba2c8383"
|
||||
integrity sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==
|
||||
"@vue/compiler-core@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.40.tgz#c785501f09536748121e937fb87605bbb1ada8e5"
|
||||
integrity sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.16.4"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/shared" "3.2.40"
|
||||
estree-walker "^2.0.2"
|
||||
source-map "^0.6.1"
|
||||
|
||||
"@vue/compiler-dom@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz#16d911ff163ed5fc8087a01645bf14bb7f325401"
|
||||
integrity sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==
|
||||
"@vue/compiler-dom@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz#c225418773774db536174d30d3f25ba42a33e7e4"
|
||||
integrity sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-core" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
"@vue/compiler-sfc@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz#e5065e7c0e5170ffa750e3c3dd93a29db109d0f2"
|
||||
integrity sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==
|
||||
"@vue/compiler-sfc@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz#61823283efc84d25d9d2989458f305d32a2ed141"
|
||||
integrity sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.16.4"
|
||||
"@vue/compiler-core" "3.2.36"
|
||||
"@vue/compiler-dom" "3.2.36"
|
||||
"@vue/compiler-ssr" "3.2.36"
|
||||
"@vue/reactivity-transform" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-core" "3.2.40"
|
||||
"@vue/compiler-dom" "3.2.40"
|
||||
"@vue/compiler-ssr" "3.2.40"
|
||||
"@vue/reactivity-transform" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.25.7"
|
||||
postcss "^8.1.10"
|
||||
source-map "^0.6.1"
|
||||
|
||||
"@vue/compiler-ssr@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz#314f3a9424db58142c3608f48cbda7aa05fc66cb"
|
||||
integrity sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==
|
||||
"@vue/compiler-ssr@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz#67df95a096c63e9ec4b50b84cc6f05816793629c"
|
||||
integrity sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-dom" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.1.2":
|
||||
version "3.3.0"
|
||||
@ -2104,53 +2104,53 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz#ceb924b4ecb3b9c43871c7a429a02f8423e621ab"
|
||||
integrity sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==
|
||||
|
||||
"@vue/reactivity-transform@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz#8426a941b0b09d1b94fc162d4642758183b5d133"
|
||||
integrity sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==
|
||||
"@vue/reactivity-transform@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz#dc24b9074b26f0d9dd2034c6349f5bb2a51c86ac"
|
||||
integrity sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.16.4"
|
||||
"@vue/compiler-core" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-core" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
"@vue/reactivity@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.36.tgz#026b14e716febffe80cd284fd8a2b33378968646"
|
||||
integrity sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==
|
||||
"@vue/reactivity@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.40.tgz#ae65496f5b364e4e481c426f391568ed7d133cca"
|
||||
integrity sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==
|
||||
dependencies:
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
"@vue/runtime-core@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.36.tgz#be5115e665679c26bf3807d2326675dc1d847134"
|
||||
integrity sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==
|
||||
"@vue/runtime-core@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.40.tgz#e814358bf1b0ff6d4a6b4f8f62d9f341964fb275"
|
||||
integrity sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/reactivity" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
"@vue/runtime-dom@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz#cd5d403ea23c18ee7c17767103a1b2f8263c54bb"
|
||||
integrity sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==
|
||||
"@vue/runtime-dom@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz#975119feac5ab703aa9bbbf37c9cc966602c8eab"
|
||||
integrity sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==
|
||||
dependencies:
|
||||
"@vue/runtime-core" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/runtime-core" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
csstype "^2.6.8"
|
||||
|
||||
"@vue/server-renderer@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.36.tgz#1e7c1cf63bd17df7828d04e8c780ee6ca7a9ed7c"
|
||||
integrity sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==
|
||||
"@vue/server-renderer@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.40.tgz#55eaac31f7105c3907e1895129bf4efb6b0ce393"
|
||||
integrity sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-ssr" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
"@vue/shared@3.2.36":
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.36.tgz#35e11200542cf29068ba787dad57da9bdb82f644"
|
||||
integrity sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ==
|
||||
"@vue/shared@3.2.40":
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.40.tgz#e57799da2a930b975321981fcee3d1e90ed257ae"
|
||||
integrity sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==
|
||||
|
||||
"@vue/web-component-wrapper@^1.2.0":
|
||||
version "1.3.0"
|
||||
@ -12533,16 +12533,16 @@ vue-template-es2015-compiler@^1.9.0:
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||
|
||||
vue@^3.2.36:
|
||||
version "3.2.36"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.36.tgz#8daa996e2ced521708de97d066c7c998e8bc3378"
|
||||
integrity sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==
|
||||
vue@^3.2.40:
|
||||
version "3.2.40"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.40.tgz#23f387f6f9b3a0767938db6751e4fb5900f0ee34"
|
||||
integrity sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.2.36"
|
||||
"@vue/compiler-sfc" "3.2.36"
|
||||
"@vue/runtime-dom" "3.2.36"
|
||||
"@vue/server-renderer" "3.2.36"
|
||||
"@vue/shared" "3.2.36"
|
||||
"@vue/compiler-dom" "3.2.40"
|
||||
"@vue/compiler-sfc" "3.2.40"
|
||||
"@vue/runtime-dom" "3.2.40"
|
||||
"@vue/server-renderer" "3.2.40"
|
||||
"@vue/shared" "3.2.40"
|
||||
|
||||
watchpack-chokidar2@^2.0.1:
|
||||
version "2.0.1"
|
||||
|
Loading…
Reference in New Issue
Block a user