import { BasicType, DBColumn, DBTable, FunctionData, ObjectData, PrimitiveData, Scope, Tag, Variable, Variables, XmlError, variable2json, } from "@cyklang/core"; import loglevel from "loglevel"; import { ComponentModel } from "./cykLang"; // import { dbRemote } from 'src/services/cyklang'; import { computed, onUnmounted, ref } from "vue"; import { useCykLang, structure } from "./cykLang"; import { componentModelParameter } from "./cykReact"; import { AlertException } from "./cykRun"; import { useI18n } from "vue-i18n"; import { RowObject } from "./cykTableEdit"; const logger = loglevel.getLogger("cykQimg.ts"); logger.setLevel("debug"); interface Derived { [key: string]: { width: number; height: number }; } interface QimgParams { table_name?: string; field_name?: string; image_format?: string; id: BasicType; key?: string; width?: number; height?: number; max_dimension?: number; derived?: Derived; onchange?: FunctionData; locked?: boolean; zoomable?: boolean; downloadable?: boolean; } const qimgOptions: { name: string; type: string }[] = [ { name: "table_name", type: "string" }, { name: "field_name", type: "string" }, { name: "image_format", type: "string" }, { name: "width", type: "number" }, { name: "height", type: "number" }, { name: "max_dimension", type: "number"}, { name: "derived", type: "object" }, { name: "id", type: "number" }, { name: "key", type: "string" }, { name: "onchange", type: "function" }, { name: "locked", type: "boolean"}, { name: "zoomable", type: "boolean"}, { name: "downloadable", type: "boolean"} ]; /** * parse le modele du composant * @param componentArg * @returns */ function parseOptions(componentArg: ComponentModel): QimgParams { const result: any = {}; qimgOptions.forEach(({ name, type }) => { const optionVar = componentArg.objectData?.variables.getVariable(name); if (optionVar) { if (optionVar.dataType.name !== type) { AlertException( new XmlError( "qimg expect " + name + " of type " + type + " but received " + optionVar.dataType.name, componentArg.objectData?.tag || new Tag("") ) ); } else { if (optionVar.dataType.isPrimitive()) { result[name] = (optionVar.data as PrimitiveData)?.value; } else if (type === "function") { result[name] = optionVar.data as FunctionData; } else { // object result[name] = variable2json(optionVar); } } } }); if (!result.key && result.id) { result.key = String(result.id); } // logger.debug('cykQimg.parseOptions -> ', result) return result; } // 13/08/2023 // model is a JSON meta of the image export function useCykQimg(props: { componentArg: ComponentModel | undefined; }) { const { structure, dbRemote } = useCykLang(); const { t } = useI18n(); const isLoading = ref(true); const visible = componentModelParameter( props.componentArg, "visible", true ); const label = componentModelParameter( props.componentArg, "label", structure.scope.xlate(props.componentArg?.objectData?.tag.attributes.LABEL) ); // let record: ObjectData const key = ref(''); const table_name = ref(); const field_name = ref(); const width = ref(); const height = ref(); const locked = ref(false) const zoomable = ref(true) const downloadable = ref(true) const image_format = ref(); const cropWidth = ref(); const cropHeight = ref(); const max_dimension = ref(); const derived = ref(); const on_change = ref(); const image_path = ref(''); const size = ref(0); const format = ref(''); const filename = ref(''); (async () => { try { if (props.componentArg !== undefined) { await props.componentArg.interpolateAttributes(); // const { table_name: _table_name, field_name: _field_name, record: _record, id: _id, width: _width, height: _height } = await parseParams(props.componentArg); const qimgParams: QimgParams = parseOptions(props.componentArg); table_name.value = qimgParams.table_name; field_name.value = qimgParams.field_name; image_format.value = qimgParams.image_format; key.value = qimgParams.key || ''; cropWidth.value = qimgParams.width; cropHeight.value = qimgParams.height; max_dimension.value = qimgParams.max_dimension; derived.value = qimgParams.derived; on_change.value = qimgParams.onchange; locked.value = qimgParams.locked || false; zoomable.value = qimgParams.zoomable === undefined ? true : qimgParams.zoomable; downloadable.value = qimgParams.downloadable === undefined ? true : qimgParams.downloadable; if (qimgParams.width) { width.value = `${qimgParams.width}px`; } if (qimgParams.height) { height.value = `${qimgParams.height}px`; } const flatKey = key.value.replace(/\t/g, "_") filename.value = `${table_name.value}_${encodeURIComponent(flatKey)}.${format.value}` // logger.debug('cykQimg.meta', meta, 'width.value', width.value, 'height.value', height.value, "props", props) image_path.value = `${dbRemote.serverURL}/api/image/${table_name.value}/${encodeURIComponent(key.value)}/${field_name.value}?size=${size.value}`; } else { AlertException("cykQimg props.componentArg undefined"); } } catch (err) { AlertException(err); } })().finally(() => { isLoading.value = false; }); const controlUploadable = (): boolean => { let result = false; if ( table_name.value && field_name.value && key.value !== null && key.value !== undefined && key.value !== "" ) result = true; return result; }; const uploadable = computed({ get() { let result = false; if (props.componentArg !== undefined) { result = controlUploadable(); } return result; }, set() { throw "setter not supported"; }, }); const zoom = ref(false); const clickImg = () => { if (image_path.value !== "" && zoomable.value === true) { zoom.value = true; } }; const removeImage = () => { if (confirm(t("confirm_delete_image"))) { (async () => { try { await dbRemote.apiServer.delete(image_path.value); image_path.value = ''; if ( props.componentArg === undefined || props.componentArg.model === undefined ) return; props.componentArg.model.setValue(null); props.componentArg.dirtyManager?.variableChanged( props.componentArg.model ); zoom.value = false; } catch (err) { alert(String(err)); } })(); } }; onUnmounted(() => { props.componentArg?.objectData?.destroy(); }); return { isLoading, visible, label, uploadable, zoom, clickImg, removeImage, cropWidth, cropHeight, max_dimension, key, width, height, derived, table_name, field_name, image_format, on_change, image_path, size, format, filename, locked, zoomable, downloadable, }; } /** * parse la définition de la colonne de tableau * @param dbColumn * @returns */ async function parseProperties(dbColumn: DBColumn, row: ObjectData): Promise { const result: any = {}; for (let ind = 0; ind < qimgOptions.length; ind++) { const { name, type } = qimgOptions[ind] const optionVar = dbColumn.params?.getVariable(name) if (optionVar) { if (name === 'key') { if (optionVar.dataType.name !== 'function') throw `qimg parameter key should be a function` const functionData = optionVar.data as FunctionData const callParams = new Variables() const varRow = new Variable(structure.objectDataType, row) callParams.push('row', varRow) const data = await functionData.callFunction(callParams, structure.scope) if (!data?.type.isPrimitive()) throw `qimg key() should return PrimitiveData mais returned ${data?.type.name}` result['key'] = (data as PrimitiveData).value as string } else if (optionVar.dataType.name !== type) { AlertException( new XmlError(`qimg parameter ${name}, type expected ${type} but received ${optionVar.dataType.name}`, new Tag("")) ); } else { if (optionVar.dataType.isPrimitive()) { result[name] = (optionVar.data as PrimitiveData)?.value; } else if (type === "function") { result[name] = optionVar.data as FunctionData; } else { // object result[name] = variable2json(optionVar); } } } else { // result[name] = undefined } } if (!result.key && result.id) { result.key = String(result.id); } // logger.debug('cykQimgCell.parseProperties -> ', result) return result; } /** * * @param bytes * @returns */ function formatFileSize(bytes: number) { if (!bytes) return '0 octets'; const k = 1024; const sizes = ['octets', 'ko', 'Mo', 'Go', 'To']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * * @param props */ export function useCykQimgCell(props: { row: RowObject | undefined, dbColumn: DBColumn | undefined, scopeRow: Scope | undefined }) { const { dbRemote } = useCykLang(); const isLoading = ref(true) const isImage = ref(false) const key = ref(''); const table_name = ref(); const field_name = ref(); const width = ref(); const height = ref(); const label = ref('') const image_path = ref(''); const format = ref(''); const filename = ref(''); const fileicon = ref('') const load = async () => { try { if (!props.row || !props.dbColumn) throw `row or dbColumn undefined` const qimgParams = await parseProperties(props.dbColumn, props.row.objectData); const model = props.row.objectData.variables.getVariable(props.dbColumn.name) const meta = model ? variable2json(model) : undefined; qimgParams.key = qimgParams.key || meta?.key qimgParams.table_name = qimgParams.table_name || meta?.table_name qimgParams.field_name = qimgParams.field_name || meta?.field_name qimgParams.height = qimgParams.height || 80 if (meta?.width && meta?.height) { if (qimgParams.width && qimgParams.height) { width.value = `${qimgParams.width}px`; height.value = `${qimgParams.height}px`; } else if (qimgParams.height) { height.value = `${qimgParams.height}px`; width.value = `${(qimgParams.height.valueOf() * meta.width) / meta.height }px`; } else if (qimgParams.width) { width.value = `${qimgParams.width}px`; height.value = `${(qimgParams.width.valueOf() * meta.height) / meta.width }px`; } } if (meta) { if (meta.filename && meta.filename !== 'blob') { filename.value = meta.filename; } label.value = `${meta.filename} ( ${formatFileSize(meta.size)} )` if (meta.mimetype.startsWith('image/')) { isImage.value = true } const mimetype: string = meta.mimetype || '' if (mimetype.startsWith('image/')) { fileicon.value = "image" isImage.value = true } else if (mimetype === 'application/pdf') { fileicon.value = "picture_as_pdf" } else if (mimetype.startsWith('text/')) { fileicon.value = "description" } else if (mimetype) { fileicon.value = "attach_file" } } if (filename.value !== '') { const flatKey = key.value.replace(/\t/g, "_") logger.debug(`flatKey = ${flatKey}`) filename.value = `${table_name.value}_${encodeURIComponent(flatKey)}.${format.value}` } image_path.value = `${dbRemote.serverURL}/api/image/${qimgParams.table_name}/${encodeURIComponent(String(qimgParams.key))}/${qimgParams.field_name}?size=${meta?.size}`; isLoading.value = false } catch (err) { logger.error(err) } }; load(); return { isLoading, isImage, label, filename, fileicon, image_path, width, height } }