Initial commit
This commit is contained in:
223
app/components/product/ProductHandover.vue
Normal file
223
app/components/product/ProductHandover.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Header với nút thêm và upload -->
|
||||
<div class="columns">
|
||||
<div class="column is-10">
|
||||
<!-- Stage selection list -->
|
||||
<div>
|
||||
<div class="tags">
|
||||
<div
|
||||
v-for="(stage, index) in stages"
|
||||
:key="stage.id"
|
||||
class="tag is-hoverable is-medium has-addons"
|
||||
:class="{ 'is-primary': selectedStages.includes(stage.id) }"
|
||||
style="user-select: none;"
|
||||
@click="handleStageClick(stage.id, index, $event)"
|
||||
>
|
||||
<span>{{ stage.name }}</span>
|
||||
<span
|
||||
v-if="selectedStages.includes(stage.id) && $getEditRights()"
|
||||
class="tag ml-2 px-1"
|
||||
style="width: 1.25rem; height: 1.25rem; cursor: pointer"
|
||||
@click.stop="editStage(stage.id)"
|
||||
title="Chỉnh sửa giai đoạn">
|
||||
<SvgIcon v-bind="{ name: 'pen1.svg', type: 'dark', size: 16 }" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="$getEditRights()" class="column is-2">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<a class="mr-3" @click="upload()">
|
||||
<span class="icon-text">
|
||||
<SvgIcon v-bind="{ name: 'upload.svg', type: 'primary', size: 25 }" />
|
||||
<span class="ml-1 fsb-17">Phân bổ</span>
|
||||
</span>
|
||||
</a>
|
||||
<a @click="addNew()">
|
||||
<span class="icon-text">
|
||||
<SvgIcon v-bind="{ name: 'add1.png', type: 'primary', size: 23 }" />
|
||||
<span class="ml-1 fsb-17">Thêm đợt</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data View với product được filter -->
|
||||
<div class="m-0" v-if="selectedStages.length > 0">
|
||||
<div class="level mb-3">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<h5 class="fsb-18">Sản phẩm bàn giao</h5>
|
||||
<button class="button is-small is-text" @click="clearSelection">
|
||||
Bỏ chọn tất cả
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DataView với key động để force reload khi filter thay đổi -->
|
||||
<DataView v-bind="dataViewConfig" :key="`${selectedStagesKey}-${uploadedKey}`" />
|
||||
</div>
|
||||
<p v-else class="has-text-centered py-6 fs-20 has-text-grey-light">Chọn đợt bàn giao để xem các sản phẩm.</p>
|
||||
|
||||
<!-- Modal Components -->
|
||||
<Modal v-if="showmodal" @close="showmodal = undefined" @dataevent="dataevent" v-bind="showmodal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { isEqual, pull } from 'es-toolkit'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import DataView from '~/components/datatable/DataView.vue'
|
||||
import Modal from '~/components/Modal.vue'
|
||||
import SvgIcon from '~/components/SvgIcon.vue'
|
||||
|
||||
const { $getdata } = useNuxtApp()
|
||||
|
||||
// State
|
||||
const showmodal = ref()
|
||||
const showStageList = ref(true)
|
||||
const selectedStages = ref([])
|
||||
const stages = ref([])
|
||||
const lastSelectedIndex = ref(null)
|
||||
const uploadedKey = ref(0);
|
||||
|
||||
// Key động để force reload DataView khi selectedStages thay đổi
|
||||
const selectedStagesKey = computed(() => {
|
||||
const sorted = [...selectedStages.value].sort((a, b) => a - b)
|
||||
return 'dataview-stage-' + sorted.join('-')
|
||||
})
|
||||
|
||||
// DataView config
|
||||
const dataViewConfig = computed(() => {
|
||||
const config = {
|
||||
api: 'product',
|
||||
setting: 'product-stage',
|
||||
pagename: 'pagedata-product-stage',
|
||||
modal: { component: 'parameter/ProductForm', title: 'Sản phẩm' },
|
||||
timeopt: { time: 36000, disable: "add" },
|
||||
realtime: { time: 2, update: "false" }
|
||||
}
|
||||
|
||||
if (selectedStages.value.length > 0) {
|
||||
config.filter = { elpro__stage__in: selectedStages.value }
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
// Methods
|
||||
function handleStageClick(stageId, index, event) {
|
||||
if (event.ctrlKey) {
|
||||
// Ctrl + Click: Chọn/bỏ chọn từng cái
|
||||
toggleStage(stageId)
|
||||
lastSelectedIndex.value = index
|
||||
} else if (event.shiftKey) {
|
||||
// Shift + Click: Chọn range từ last đến hiện tại
|
||||
if (lastSelectedIndex.value !== null) {
|
||||
selectRange(lastSelectedIndex.value, index)
|
||||
} else {
|
||||
toggleStage(stageId)
|
||||
lastSelectedIndex.value = index
|
||||
}
|
||||
} else {
|
||||
// Click bình thường: Chọn chỉ cái này
|
||||
if (isEqual(selectedStages.value, [stageId])) {
|
||||
pull(selectedStages.value, [stageId]);
|
||||
}
|
||||
else selectedStages.value = [stageId];
|
||||
|
||||
lastSelectedIndex.value = index
|
||||
}
|
||||
}
|
||||
|
||||
function selectRange(startIndex, endIndex) {
|
||||
const start = Math.min(startIndex, endIndex)
|
||||
const end = Math.max(startIndex, endIndex)
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
const stageId = stages.value[i].id
|
||||
if (!selectedStages.value.includes(stageId)) {
|
||||
selectedStages.value.push(stageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleStage(stageId) {
|
||||
const index = selectedStages.value.indexOf(stageId)
|
||||
if (index > -1) {
|
||||
selectedStages.value.splice(index, 1)
|
||||
} else {
|
||||
selectedStages.value.push(stageId)
|
||||
}
|
||||
}
|
||||
|
||||
function removeStage(stageId) {
|
||||
const index = selectedStages.value.indexOf(stageId)
|
||||
if (index > -1) {
|
||||
selectedStages.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedStages.value = []
|
||||
showStageList.value = true
|
||||
}
|
||||
|
||||
function upload() {
|
||||
showmodal.value = {
|
||||
component: 'parameter/ImportData',
|
||||
title: 'Tải lên sản phẩm bàn giao',
|
||||
width: '80%',
|
||||
height: '400px',
|
||||
vbind: {
|
||||
code: 'eligibility-import',
|
||||
onClose: () => uploadedKey.value++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addNew() {
|
||||
showmodal.value = {
|
||||
component: 'parameter/StageForm',
|
||||
title: 'Thêm giai đoạn mới',
|
||||
width: '900px',
|
||||
height: '500px'
|
||||
}
|
||||
}
|
||||
|
||||
function editStage(stageId) {
|
||||
showmodal.value = {
|
||||
component: 'parameter/StageForm',
|
||||
title: 'Chỉnh sửa giai đoạn',
|
||||
width: '900px',
|
||||
height: '500px',
|
||||
vbind: {
|
||||
id: stageId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dataevent() {
|
||||
loadStages()
|
||||
}
|
||||
|
||||
async function loadStages() {
|
||||
try {
|
||||
const stagesData = await $getdata('stage');
|
||||
stages.value = stagesData;
|
||||
} catch (error) {
|
||||
console.error('Lỗi khi fetch stage:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadStages()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user