refactor: view image dialog
This commit is contained in:
parent
bfe5da2565
commit
718a28642d
3 changed files with 126 additions and 1 deletions
|
|
@ -6,7 +6,11 @@ import { storeToRefs } from 'pinia';
|
|||
import WorkerItem from './WorkerItem.vue';
|
||||
import DeleteButton from '../button/DeleteButton.vue';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
import { ProductServiceList, QuotationPayload } from 'stores/quotations/types';
|
||||
import {
|
||||
ProductRelation,
|
||||
ProductServiceList,
|
||||
QuotationPayload,
|
||||
} from 'stores/quotations/types';
|
||||
import { formatNumberDecimal, commaInput } from 'stores/utils';
|
||||
import { useConfigStore } from 'stores/config';
|
||||
|
||||
|
|
@ -29,6 +33,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'viewFile', data: ProductRelation): void;
|
||||
(e: 'delete', index: number): void;
|
||||
(
|
||||
e: 'updateTable',
|
||||
|
|
@ -449,6 +454,7 @@ watch(
|
|||
background-color: hsla(var(--positive-bg) / 0.1);
|
||||
color: hsl(var(--positive-bg));
|
||||
"
|
||||
@click="$emit('viewFile', props.row.product)"
|
||||
/>
|
||||
<DeleteButton
|
||||
v-if="!readonly"
|
||||
|
|
|
|||
99
src/components/dialog/DialogViewFile.vue
Normal file
99
src/components/dialog/DialogViewFile.vue
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import DialogFormContainer from './DialogFormContainer.vue';
|
||||
import DialogHeader from './DialogHeader.vue';
|
||||
import MainButton from '../button/MainButton.vue';
|
||||
import NoData from '../NoData.vue';
|
||||
|
||||
defineProps<{
|
||||
title: string;
|
||||
url?: string;
|
||||
}>();
|
||||
|
||||
const open = defineModel<boolean>({ default: false });
|
||||
|
||||
const state = reactive({
|
||||
imageZoom: 100,
|
||||
});
|
||||
|
||||
function openDialog() {
|
||||
state.imageZoom = 100;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<DialogFormContainer v-model="open" v-on:open="openDialog">
|
||||
<template #header>
|
||||
<DialogHeader :title="title" />
|
||||
</template>
|
||||
|
||||
<main class="column full-height">
|
||||
<section
|
||||
style="background: var(--gray-3)"
|
||||
class="q-py-sm row justify-center"
|
||||
>
|
||||
<div class="surface-2 q-px-md q-py-sm rounded row no-wrap items-center">
|
||||
<MainButton
|
||||
icon="mdi-minus"
|
||||
color="0 0% 0%"
|
||||
icon-only
|
||||
@click="
|
||||
() => {
|
||||
if (state.imageZoom > 0) state.imageZoom -= 10;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<q-input
|
||||
dense
|
||||
outlined
|
||||
class="q-px-sm"
|
||||
input-class="text-center text-caption"
|
||||
:model-value="state.imageZoom"
|
||||
@update:model-value="
|
||||
(val) => {
|
||||
const numVal = Number(val);
|
||||
if (numVal > 500 || numVal < 0) {
|
||||
state.imageZoom = 100;
|
||||
} else {
|
||||
state.imageZoom = numVal || 100;
|
||||
}
|
||||
}
|
||||
"
|
||||
></q-input>
|
||||
<MainButton
|
||||
icon="mdi-plus"
|
||||
color="0 0% 0%"
|
||||
icon-only
|
||||
@click="
|
||||
() => {
|
||||
if (state.imageZoom < 500) state.imageZoom += 10;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div
|
||||
:class="{ 'cursor-pointer': state.imageZoom > 100 }"
|
||||
class="full-height full-width flex justify-center items-center col scroll q-pa-md"
|
||||
v-dragscroll
|
||||
>
|
||||
<q-img
|
||||
v-if="url"
|
||||
class="full-height"
|
||||
:src="url"
|
||||
fit="contain"
|
||||
:style="{ transform: `scale(${state.imageZoom / 100})` }"
|
||||
style="transform-origin: 0 0"
|
||||
/>
|
||||
<NoData v-else />
|
||||
</div>
|
||||
</main>
|
||||
</DialogFormContainer>
|
||||
</template>
|
||||
<style scoped>
|
||||
:deep(.q-field__control.relative-position.row.no-wrap) {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -29,6 +29,7 @@ import { View } from './types.ts';
|
|||
import {
|
||||
EmployeeWorker,
|
||||
PayCondition,
|
||||
ProductRelation,
|
||||
ProductServiceList,
|
||||
QuotationPayload,
|
||||
} from 'src/stores/quotations/types';
|
||||
|
|
@ -64,6 +65,7 @@ import QuotationFormInfo from './QuotationFormInfo.vue';
|
|||
import QuotationFormWorkerSelect from './QuotationFormWorkerSelect.vue';
|
||||
import QuotationFormWorkerAddDialog from './QuotationFormWorkerAddDialog.vue';
|
||||
import UploadFileSection from 'src/components/upload-file/UploadFileSection.vue';
|
||||
import DialogViewFile from 'src/components/dialog/DialogViewFile.vue';
|
||||
|
||||
import { columnPaySplit } from './constants';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
|
|
@ -291,6 +293,9 @@ const pageState = reactive({
|
|||
employeeModal: false,
|
||||
productServiceModal: false,
|
||||
remarkWrite: true,
|
||||
imageDialog: false,
|
||||
imageDialogTitle: '',
|
||||
imageDialogUrl: '',
|
||||
});
|
||||
|
||||
const productList = ref<Partial<Record<ProductGroupId, Product[]>>>({});
|
||||
|
|
@ -956,6 +961,14 @@ async function uploadAttachment(file?: File) {
|
|||
if (ret) await getAttachment();
|
||||
}
|
||||
|
||||
function viewProductFile(data: ProductRelation) {
|
||||
const base64 = data.detail.match(/src="([^"]+)"/);
|
||||
|
||||
pageState.imageDialog = true;
|
||||
pageState.imageDialogTitle = data.name;
|
||||
pageState.imageDialogUrl = base64 ? base64[1] : '';
|
||||
}
|
||||
|
||||
const sessionData = ref<Record<string, any>>();
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -1539,6 +1552,7 @@ watch(
|
|||
}
|
||||
"
|
||||
@update-table="handleUpdateProductTable"
|
||||
@view-file="viewProductFile"
|
||||
/>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
|
|
@ -2262,6 +2276,12 @@ watch(
|
|||
fetchQuotation();
|
||||
"
|
||||
/>
|
||||
|
||||
<DialogViewFile
|
||||
:title="pageState.imageDialogTitle"
|
||||
:url="pageState.imageDialogUrl"
|
||||
v-model="pageState.imageDialog"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue