feat: Upload Alert done.
This commit is contained in:
@@ -30,6 +30,30 @@
|
|||||||
transform: rotate(360deg)
|
transform: rotate(360deg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* loaderBar */
|
||||||
|
/* <span class="loaderBar"></span> */
|
||||||
|
.loaderBar {
|
||||||
|
width: 80%;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #0099FF;
|
||||||
|
border: 1px solid #0099FF;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.25) 50%, rgba(0, 0, 0, 0.25) 75%, transparent 75%, transparent);
|
||||||
|
font-size: 30px;
|
||||||
|
background-size: 1em 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: barStripe 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes barStripe {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 1em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* components */
|
/* components */
|
||||||
/* Scrollbar */
|
/* Scrollbar */
|
||||||
|
|||||||
@@ -6,58 +6,58 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="h-full flex items-start justify-start p-4">
|
<div class="h-full flex items-start justify-start p-4">
|
||||||
<!-- Trace List -->
|
<!-- Trace List -->
|
||||||
<section class="w-80 h-full pr-4">
|
<section class="w-80 h-full pr-4">
|
||||||
<p class="h2 px-2 mb-2">Trace List ({{ traceTotal }})</p>
|
<p class="h2 px-2 mb-2">Trace List ({{ traceTotal }})</p>
|
||||||
<p class="text-primary h2 px-2 mb-2">
|
<p class="text-primary h2 px-2 mb-2">
|
||||||
<span class="material-symbols-outlined text-sm align-[-10%] mr-2">info</span>Click trace number to see more.
|
<span class="material-symbols-outlined text-sm align-[-10%] mr-2">info</span>Click trace number to see more.
|
||||||
</p>
|
</p>
|
||||||
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
|
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
|
||||||
<table class="border-separate border-spacing-x-2 text-sm">
|
<table class="border-separate border-spacing-x-2 text-sm">
|
||||||
<thead class="sticky top-0 z-10 bg-neutral-100">
|
<thead class="sticky top-0 z-10 bg-neutral-100">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="h2 px-2 border-b border-neutral-500">Trace</th>
|
<th class="h2 px-2 border-b border-neutral-500">Trace</th>
|
||||||
<th class="h2 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
<th class="h2 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id)">
|
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id)">
|
||||||
<td class="p-2">#{{ trace.id }}</td>
|
<td class="p-2">#{{ trace.id }}</td>
|
||||||
<td class="p-2 w-24">
|
<td class="p-2 w-24">
|
||||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||||
<div class="h-full bg-primary" :style="progressWidth(trace.value)"></div>
|
<div class="h-full bg-primary" :style="progressWidth(trace.value)"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="py-2 text-right">{{ trace.count }}</td>
|
||||||
|
<td class="p-2 text-right">{{ trace.ratio }}%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- Trace item Table -->
|
||||||
|
<section class="px-4 py-2 h-full w-[calc(100%_-_320px)] bg-neutral-10 rounded-xl">
|
||||||
|
<p class="h2 mb-2 px-4">Trace #{{ showTraceId }}</p>
|
||||||
|
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
||||||
|
<div class="h-full w-full">
|
||||||
|
<div id="cfmTrace" ref="cfmTrace" class="h-full min-w-full relative"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable" @scroll="handleScroll">
|
||||||
|
<DataTable :value="caseData" showGridlines tableClass="text-sm " breakpoint="0">
|
||||||
|
<div v-for="(col, index) in columnData" :key="index">
|
||||||
|
<Column :field="col.field" :header="col.header">
|
||||||
|
<template #body="{ data }">
|
||||||
|
<div :class="data[col.field]?.length > 18 ? 'whitespace-normal' : 'whitespace-nowrap'">
|
||||||
|
{{ data[col.field] }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</template>
|
||||||
<td class="py-2 text-right">{{ trace.count }}</td>
|
</Column>
|
||||||
<td class="p-2 text-right">{{ trace.ratio }}%</td>
|
</div>
|
||||||
</tr>
|
</DataTable>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
<!-- Trace item Table -->
|
|
||||||
<section class="px-4 py-2 h-full w-[calc(100%_-_320px)] bg-neutral-10 rounded-xl">
|
|
||||||
<p class="h2 mb-2 px-4">Trace #{{ showTraceId }}</p>
|
|
||||||
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
|
||||||
<div class="h-full w-full">
|
|
||||||
<div id="cfmTrace" ref="cfmTrace" class="h-full min-w-full relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable" @scroll="handleScroll">
|
|
||||||
<DataTable :value="caseData" showGridlines tableClass="text-sm " breakpoint="0">
|
|
||||||
<div v-for="(col, index) in columnData" :key="index">
|
|
||||||
<Column :field="col.field" :header="col.header">
|
|
||||||
<template #body="{ data }">
|
|
||||||
<div :class="data[col.field]?.length > 18 ? 'whitespace-normal' : 'whitespace-nowrap'">
|
|
||||||
{{ data[col.field] }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Column>
|
|
||||||
</div>
|
|
||||||
</DataTable>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
51
src/components/File/UploadModal.vue
Normal file
51
src/components/File/UploadModal.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :visible="uploadModal" modal :style="{ width: '90vw', height: '90vh' }" :contentClass="contentClass" @update:visible="$emit('closeModal', $event)">
|
||||||
|
<template #header>
|
||||||
|
<div class="py-5">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class=" h-full flex flex-col justify-center items-center p-4 space-y-4">
|
||||||
|
<IconUploarding class="loader-arrow-upward"></IconUploarding>
|
||||||
|
<p class="text-neutral-900">Click or drag a file here.</p>
|
||||||
|
<p class="text-neutral-500">(Only <span class="text-primary">.csv</span> is supported)</p>
|
||||||
|
<p class="btn btn-lg btn-c-primary">下一頁</p>
|
||||||
|
<p class="btn btn-lg btn-c-primary" @click="uploadFailde">上傳失敗 Modal</p>
|
||||||
|
<p class="btn btn-lg btn-c-primary" @click="uploadSuccess">上傳成功 Modal</p>
|
||||||
|
<p class="btn btn-lg btn-c-primary" @click="uploadConfirm">是否上傳 Modal</p>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import IconUploarding from '../icons/IconUploarding.vue';
|
||||||
|
import { uploadFailde, uploadSuccess, uploadConfirm } from '@/module/alertModal.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['uploadModal'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contentClass: 'h-full',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
IconUploarding,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
uploadFailde,
|
||||||
|
uploadSuccess,
|
||||||
|
uploadConfirm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.loader-arrow-upward {
|
||||||
|
animation: bump 0.6s linear infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes bump {
|
||||||
|
0% {
|
||||||
|
transform: translate(0%, 5px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0%, -5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -27,11 +27,15 @@
|
|||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form> -->
|
||||||
<label class="btn btn-sm btn-neutral cursor-pointer">
|
<!-- <label class="btn btn-sm btn-neutral cursor-pointer">
|
||||||
<input id="uploadFiles" class="hidden" type="file">
|
<input id="uploadFiles" class="hidden" type="file">
|
||||||
Upload
|
Upload
|
||||||
</label> -->
|
</label> -->
|
||||||
|
<div class="btn btn-sm btn-neutral cursor-pointer" @click="uploadModal = true">
|
||||||
|
Upload
|
||||||
|
<UploadModal :visible="uploadModal" @closeModal="uploadModal = $event"></UploadModal>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Other Page: Save and Download -->
|
<!-- Other Page: Save and Download -->
|
||||||
<!-- Save 有 data 跳重新命名,沒有 data 跳要不要儲存,沒有動都不跳 -->
|
<!-- Save 有 data 跳重新命名,沒有 data 跳要不要儲存,沒有動都不跳 -->
|
||||||
@@ -52,6 +56,7 @@ import ConformanceStore from '@/stores/conformance.js';
|
|||||||
import IconSearch from '@/components/icons/IconSearch.vue';
|
import IconSearch from '@/components/icons/IconSearch.vue';
|
||||||
import IconSetting from '@/components/icons/IconSetting.vue';
|
import IconSetting from '@/components/icons/IconSetting.vue';
|
||||||
import { saveFilter, savedSuccessfully, saveConformance } from '@/module/alertModal.js';
|
import { saveFilter, savedSuccessfully, saveConformance } from '@/module/alertModal.js';
|
||||||
|
import UploadModal from './File/UploadModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
@@ -66,6 +71,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
IconSearch,
|
IconSearch,
|
||||||
IconSetting,
|
IconSetting,
|
||||||
|
UploadModal,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -79,6 +85,7 @@ export default {
|
|||||||
},
|
},
|
||||||
navViewName: 'FILES',
|
navViewName: 'FILES',
|
||||||
isActive: null,
|
isActive: null,
|
||||||
|
uploadModal: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
13
src/components/icons/IconUploarding.vue
Normal file
13
src/components/icons/IconUploarding.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none">
|
||||||
|
<g clip-path="url(#clip0_2395_2657)">
|
||||||
|
<circle cx="32" cy="32" r="30" fill="#0099FF"/>
|
||||||
|
<path d="M33.7678 22.2322C32.7915 21.2559 31.2085 21.2559 30.2322 22.2322L14.3223 38.1421C13.346 39.1185 13.346 40.7014 14.3223 41.6777C15.2986 42.654 16.8816 42.654 17.8579 41.6777L32 27.5355L46.1421 41.6777C47.1185 42.654 48.7014 42.654 49.6777 41.6777C50.654 40.7014 50.654 39.1184 49.6777 38.1421L33.7678 22.2322ZM29.5 64C29.5 65.3807 30.6193 66.5 32 66.5C33.3807 66.5 34.5 65.3807 34.5 64H29.5ZM29.5 24L29.5 64H34.5L34.5 24L29.5 24Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_2395_2657">
|
||||||
|
<rect width="64" height="64" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
@@ -4,6 +4,7 @@ import ConformanceStore from '@/stores/conformance.js';
|
|||||||
import LoginStore from '@/stores/login.js';
|
import LoginStore from '@/stores/login.js';
|
||||||
|
|
||||||
const customClass = {
|
const customClass = {
|
||||||
|
container: '!z-[99999]',
|
||||||
popup: '!w-[564px]',
|
popup: '!w-[564px]',
|
||||||
title: '!text-xl !font-semibold !mb-2',
|
title: '!text-xl !font-semibold !mb-2',
|
||||||
htmlContainer: '!text-sm !font-normal !h-full !mb-4 !leading-5',
|
htmlContainer: '!text-sm !font-normal !h-full !mb-4 !leading-5',
|
||||||
@@ -165,36 +166,70 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
|
|||||||
logOut ? null : next(false);
|
logOut ? null : next(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
// 登出 button 規則,暫時沒用到
|
* Upload failde
|
||||||
export async function logoutLeave(addFilterId) {
|
* @param { string } value
|
||||||
const allMapDataStore = AllMapDataStore();
|
*/
|
||||||
const loginStore = LoginStore();
|
export async function uploadFailde(value) {
|
||||||
|
value = value ? value : '';
|
||||||
|
await Swal.fire({
|
||||||
|
title: 'UPLOAD FAILED',
|
||||||
|
html: `(錯誤樣態由前後端自行確認,呈現於此處) e.g.您的資料格式錯誤,請檢查資料後再重新上傳。`,
|
||||||
|
timer: 5000, // 停留5秒後自動關閉
|
||||||
|
showConfirmButton: false,
|
||||||
|
icon: 'error',
|
||||||
|
iconColor: '#FF3366',
|
||||||
|
customClass: customClass
|
||||||
|
})
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Upload Success
|
||||||
|
* @param { string } value
|
||||||
|
*/
|
||||||
|
export async function uploadSuccess() {
|
||||||
|
await Swal.fire({
|
||||||
|
title: 'UPLOAD COMPLETED',
|
||||||
|
timer: 5000, // 停留5秒後自動關閉
|
||||||
|
showConfirmButton: false,
|
||||||
|
icon: 'success',
|
||||||
|
iconColor: '#0099FF',
|
||||||
|
customClass: customClass
|
||||||
|
})
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Confirm whether to upload the file */
|
||||||
|
export async function uploadConfirm() {
|
||||||
const result = await Swal.fire({
|
const result = await Swal.fire({
|
||||||
title: 'ARE YOU SURE TO LEAVE MAP?',
|
title: 'ARE YOU SURE?',
|
||||||
html: 'Filter settings have not been saved.</br>Click “Save as” to save filtered results, “OK” to leave map.',
|
html: 'After uploading, you won’t be able to modify labels.',
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
iconColor: '#FF3366',
|
iconColor: '#FF3366',
|
||||||
reverseButtons:true,
|
reverseButtons:true,
|
||||||
confirmButtonText: 'Save as',
|
confirmButtonText: 'Yes',
|
||||||
confirmButtonColor: '#FF3366',
|
confirmButtonColor: '#FF3366',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
cancelButtonText: 'OK',
|
cancelButtonText: 'No',
|
||||||
cancelButtonColor: '#94a3b8',
|
cancelButtonColor: '#94a3b8',
|
||||||
customClass: customClass
|
customClass: customClass
|
||||||
})
|
})
|
||||||
if(result.isConfirmed) {
|
if(result.isConfirmed) {
|
||||||
await saveFilter(addFilterId);
|
uploadloader(); // 跑馬燈
|
||||||
// allMapDataStore.tempFilterId = await null;
|
|
||||||
// allMapDataStore.temporaryData = await [];
|
|
||||||
// allMapDataStore.postRuleData = await [];
|
|
||||||
// allMapDataStore.ruleData = await [];
|
|
||||||
// await loginStore.logOut()
|
|
||||||
} else if(result.dismiss === 'cancel') {
|
} else if(result.dismiss === 'cancel') {
|
||||||
allMapDataStore.tempFilterId = await null;
|
// 什麼都不做
|
||||||
// allMapDataStore.temporaryData = await [];
|
} else if(result.dismiss === 'backdrop') {
|
||||||
// allMapDataStore.postRuleData = await [];
|
// 什麼都不做
|
||||||
// allMapDataStore.ruleData = await [];
|
|
||||||
await loginStore.logOut()
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
/**
|
||||||
|
* Upload loader
|
||||||
|
*/
|
||||||
|
export async function uploadloader() {
|
||||||
|
await Swal.fire({
|
||||||
|
html: '<span class="loaderBar mt-7"></span>',
|
||||||
|
timer: 5000, // 停留5秒後自動關閉
|
||||||
|
showConfirmButton: false,
|
||||||
|
allowOutsideClick: false,
|
||||||
|
// allowOutsideClick: () => !Swal.isLoading()
|
||||||
|
customClass: customClass
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user