feat: upload done.

This commit is contained in:
chiayin
2023-12-29 16:05:27 +08:00
parent cd2ab42125
commit 9ef441ee83
8 changed files with 384 additions and 207 deletions

View File

@@ -18,6 +18,9 @@
} }
/* vue-toast-notification */ /* vue-toast-notification */
.v-toast {
@apply z-[99999]
}
.v-toast__item { .v-toast__item {
@apply min-h-[48px] rounded-full @apply min-h-[48px] rounded-full
} }

View File

@@ -4,29 +4,30 @@
<div class="py-5"> <div class="py-5">
</div> </div>
</template> </template>
<div class=" h-full flex flex-col justify-center items-center p-4 space-y-4"> <label for="uploadFiles">
<div class=" h-full flex flex-col justify-center items-center p-4 space-y-4 relative">
<input id="uploadFiles" class=" absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" type="file" accept="text/csv" @change="upload($event)">
<IconUploarding class="loader-arrow-upward"></IconUploarding> <IconUploarding class="loader-arrow-upward"></IconUploarding>
<p class="text-neutral-900">Click or drag a file here.</p> <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="text-neutral-500">(Only <span class="text-primary">.csv</span> is supported)</p>
<router-link class="btn btn-lg btn-c-primary" :to="{name: 'Upload', replace: true}">下一頁路徑不添進歷史紀錄</router-link>
<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>
<label class="btn btn-sm btn-neutral cursor-pointer">
hidden
<input id="uploadFiles" class="" type="file" @change="upload($event)">
Upload
</label>
</div> </div>
</label>
</Dialog> </Dialog>
</template> </template>
<script> <script>
import files from '../../stores/files';
import IconUploarding from '../icons/IconUploarding.vue'; import IconUploarding from '../icons/IconUploarding.vue';
import { uploadFailde, uploadSuccess, uploadConfirm } from '@/module/alertModal.js' import { uploadFailed } from '@/module/alertModal.js'
import { storeToRefs } from 'pinia';
import FilesStore from '@/stores/files.js';
export default { export default {
props: ['uploadModal'], props: ['uploadModal'],
setup() {
const filesStore = FilesStore();
const { uploadFileName } = storeToRefs(filesStore);
return { filesStore, uploadFileName }
},
data() { data() {
return { return {
contentClass: 'h-full', contentClass: 'h-full',
@@ -36,27 +37,28 @@ export default {
IconUploarding, IconUploarding,
}, },
methods: { methods: {
uploadFailde, async upload(event) {
uploadSuccess, const fileInput = document.getElementById('uploadFiles');
uploadConfirm, const target = event.target;
upload(event) { const formData = new FormData();
const target = event.target let uploadFile;
console.log(target.files);
let file;
let form;
if(target && target.files) files.value = target.files[0]; // 判斷是否有檔案
if(file.value) { if(target && target.files) uploadFile = target.files[0];
try { // 判斷檔案大小不可超過 90MB (90(MB)*1024(KB)*1024(Bytes)=94,371,840)
if(uploadFile.size >= 94371840) return uploadFailed('size')
} catch(error) { // 將檔案加進 formData欄位一定要「csv」
console.log('error', error); formData.append('csv', uploadFile);
form.value?.reset(); // 呼叫第一階段上傳 API
file.value = null; if(uploadFile) await this.filesStore.upload(formData);
} this.uploadFileName = (uploadFile.name).match(/(.*)\.csv/)[1];
} // 清除選擇文件
if(fileInput) fileInput.value = '';
} }
}, },
beforeUnmount() {
this.$emit('closeModal', false);
}
} }
</script> </script>
<style scoped> <style scoped>

View File

@@ -73,7 +73,7 @@ app.use(VueAxios, axios);
app.use(VueSweetalert2); app.use(VueSweetalert2);
app.use(ToastPlugin, { // use `this.$toast` in Vue.js app.use(ToastPlugin, { // use `this.$toast` in Vue.js
position: 'bottom', position: 'bottom',
duration: 3000, duration: 99999999,
}); });
app.use(PrimeVue); app.use(PrimeVue);
app.component('Sidebar', Sidebar); app.component('Sidebar', Sidebar);

View File

@@ -1,7 +1,7 @@
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import AllMapDataStore from '@/stores/allMapData.js'; import AllMapDataStore from '@/stores/allMapData.js';
import ConformanceStore from '@/stores/conformance.js'; import ConformanceStore from '@/stores/conformance.js';
import LoginStore from '@/stores/login.js'; import FilesStore from '@/stores/files.js';
const customClass = { const customClass = {
container: '!z-[99999]', container: '!z-[99999]',
@@ -168,13 +168,36 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
}; };
/** /**
* Upload failde * Upload failde
* @param { string } value * @param { string } failureType
* @param { string } failureMsg
*/ */
export async function uploadFailde(value) { export async function uploadFailed(failureType, failureMsg) {
value = value ? value : ''; // msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv'
// type: 'encoding' | 'insufficient_columns' | 'empty' | 'name_suffix'
let value = '';
switch (failureType) {
case 'size':
value = '檔案大小不可超過 90MB';
break;
case 'encoding':
value = 'not in UTF-8';
break;
case 'insufficient_columns':
value = 'insufficient columns';
break;
case 'empty':
value = 'the csv file is empty';
break;
case 'name_suffix':
value = 'the filename does not ends with .csv';
break;
default:
value = failureMsg;
break;
}
await Swal.fire({ await Swal.fire({
title: 'UPLOAD FAILED', title: 'UPLOAD FAILED',
html: `(錯誤樣態由前後端自行確認,呈現於此處) e.g.您的資料格式錯誤,請檢查資料後再重新上傳。`, html: value,
timer: 5000, // 停留5秒後自動關閉 timer: 5000, // 停留5秒後自動關閉
showConfirmButton: false, showConfirmButton: false,
icon: 'error', icon: 'error',
@@ -197,8 +220,12 @@ export async function uploadSuccess() {
}) })
}; };
/** /**
* Confirm whether to upload the file */ * Confirm whether to upload the file
export async function uploadConfirm() { * @param { object } fetchData
*/
export async function uploadConfirm(fetchData) {
const filesStore = FilesStore();
const result = await Swal.fire({ const result = await Swal.fire({
title: 'ARE YOU SURE?', title: 'ARE YOU SURE?',
html: 'After uploading, you wont be able to modify labels.', html: 'After uploading, you wont be able to modify labels.',
@@ -210,10 +237,10 @@ export async function uploadConfirm() {
showCancelButton: true, showCancelButton: true,
cancelButtonText: 'No', cancelButtonText: 'No',
cancelButtonColor: '#94a3b8', cancelButtonColor: '#94a3b8',
customClass: customClass customClass: customClass,
}) })
if(result.isConfirmed) { if(result.isConfirmed) {
uploadloader(); // 跑馬燈 filesStore.uploadLog(fetchData);
} else if(result.dismiss === 'cancel') { } else if(result.dismiss === 'cancel') {
// 什麼都不做 // 什麼都不做
} else if(result.dismiss === 'backdrop') { } else if(result.dismiss === 'backdrop') {
@@ -226,10 +253,9 @@ export async function uploadConfirm() {
export async function uploadloader() { export async function uploadloader() {
await Swal.fire({ await Swal.fire({
html: '<span class="loaderBar mt-7"></span>', html: '<span class="loaderBar mt-7"></span>',
timer: 5000, // 停留5秒後自動關閉 // timer: 5000, // 停留5秒後自動關閉
showConfirmButton: false, showConfirmButton: false,
allowOutsideClick: false, allowOutsideClick: false,
// allowOutsideClick: () => !Swal.isLoading() customClass: customClass,
customClass: customClass
}) })
}; };

View File

@@ -2,37 +2,11 @@ import { defineStore } from "pinia";
import axios from "axios"; import axios from "axios";
import moment from 'moment'; import moment from 'moment';
import apiError from '@/module/apiError.js'; import apiError from '@/module/apiError.js';
import Swal from 'sweetalert2';
import { uploadFailed, uploadloader, uploadSuccess } from '@/module/alertModal.js'
export default defineStore('filesStore', { export default defineStore('filesStore', {
state: () => ({ state: () => ({
allFilter: [
{
log: {},
fileType: '',
ownerName: '',
}
],
allConformanceLog: [
{
log: {},
fileType: '',
ownerName: '',
}
],
allConformanceFilter: [
{
filter: {},
fileType: '',
ownerName: '',
}
],
allEventLog: [
{
parentLog: '',
fileType: '',
ownerName: '',
}
],
allEventFiles: [ allEventFiles: [
{ {
parentLog: '', parentLog: '',
@@ -48,6 +22,10 @@ export default defineStore('filesStore', {
}, },
filesTag: 'ALL', filesTag: 'ALL',
httpStatus: 200, httpStatus: 200,
uploadId: null,
allUploadDetail: null,
uploadLogId: null,
uploadFileName: null,
}), }),
getters: { getters: {
/** /**
@@ -62,105 +40,14 @@ export default defineStore('filesStore', {
return result; return result;
}, },
/**
* Get upload preview
*/
uploadDetail: state => {
return state.allUploadDetail;
}
}, },
actions: { actions: {
/**
* Fetch event logs api
*/
async fetchEventLog() {
const api = '/api/logs';
try {
const response = await axios.get(api);
this.allEventLog = response.data;
this.allEventLog.map(o => {
o.icon = 'work_history';
o.parentLog = o.name;
o.fileType = "Log";
o.ownerName = o.owner.name;
o.updated_base = o.updated_at;
o.accessed_base = o.accessed_at;
o.updated_at = moment(o.updated_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm');
o.accessed_at = o.accessed_at ? moment(o.accessed_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm') : null;
return this.allEventLog
})
} catch(error) {
apiError(error, 'Failed to load the logs.');
};
},
/**
* Fetch filters api
*/
async fetchFilter() {
const api = '/api/filters';
try {
const response = await axios.get(api);
this.allFilter = response.data;
this.allFilter.map(o => {
o.icon = 'tornado';
o.parentLog = o.log.name;
o.fileType = "Filter";
o.ownerName = o.owner.name;
o.updated_base = o.updated_at;
o.accessed_base = o.accessed_at;
o.updated_at = moment(o.updated_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm');
o.accessed_at = o.accessed_at ? moment(o.accessed_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm') : null;
});
} catch(error) {
apiError(error, 'Failed to load the filters.');
};
},
/**
* Fetch Conformance Log api
*/
async fetchConformanceLog() {
const api = '/api/log-checks';
try {
const response = await axios.get(api);
this.allConformanceLog = response.data;
this.allConformanceLog.map(o => {
o.icon = 'local_police';
o.parentLog = o.log.name;
o.fileType = "Rule";
o.ownerName = o.owner.name;
o.updated_base = o.updated_at;
o.accessed_base = o.accessed_at;
o.updated_at = moment(o.updated_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm');
o.accessed_at = o.accessed_at ? moment(o.accessed_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm') : null;
});
} catch(error) {
apiError(error, 'Failed to load the filters.');
};
},
/**
* Fetch Conformance Filter api
*/
async fetchConformanceFilter() {
const api = '/api/filter-checks';
try {
const response = await axios.get(api);
this.allConformanceFilter = response.data;
this.allConformanceFilter.map(o => {
o.icon = 'local_police';
o.parentLog = o.filter.name;
o.fileType = "Rule";
o.ownerName = o.owner.name;
o.updated_base = o.updated_at;
o.accessed_base = o.accessed_at;
o.updated_at = moment(o.updated_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm');
o.accessed_at = o.accessed_at ? moment(o.accessed_at).utcOffset('+08:00').format('YYYY-MM-DD HH:mm') : null;
});
} catch(error) {
apiError(error, 'Failed to load the filters.');
};
},
/** /**
* Fetch All Files api * Fetch All Files api
*/ */
@@ -192,6 +79,9 @@ export default defineStore('filesStore', {
fileType = 'Rule'; fileType = 'Rule';
parentLog = o.parent.name; parentLog = o.parent.name;
break; break;
case 'design':
icon = 'shape_line';
break;
} }
o.icon = icon; o.icon = icon;
o.parentLog = parentLog; o.parentLog = parentLog;
@@ -206,7 +96,92 @@ export default defineStore('filesStore', {
apiError(error, 'Failed to load the files.'); apiError(error, 'Failed to load the files.');
}; };
}, },
// fetchRule(){o.icon = local_police} /**
// fetchDesign(){o.icon = shape_line} * Uploads a CSV log file. 第一階段上傳
* @param {Object} fromData
*/
async upload(fromData) {
const api = '/api/logs/csv-uploads';
const config = {
data: true,
headers: {
'Content-Type': 'multipart/form-data',
},
};
uploadloader(); // 進度條
try {
const response = await axios.post(api, fromData, config);
this.uploadId = response.data.id;
this.$router.push({name: 'Upload'});
Swal.close(); // 關閉進度條
} catch(error) {
if(error.response.status === 422) {
// msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv'
// type: 'encoding' | 'insufficient_columns' | 'empty' | 'name_suffix'
uploadFailed(error.response.data.detail[0].type, error.response.data.detail[0].msg);
} else {
Swal.close(); // 關閉進度條
apiError(error, 'Failed to upload the files.');
}
}
},
/**
* Fetch upload detail
*/
async getUploadDetail() {
const uploadId = this.uploadId;
const api = `/api/logs/csv-uploads/${uploadId}`;
try {
const response = await axios.get(api);
this.allUploadDetail = response.data.preview;
} catch(error) {
apiError(error, 'Failed to get upload detail.');
}
},
/**
* Add a Log from an Uploaded CSV Log File. 第二階段上傳
* @param {Object} data
*/
async uploadLog(data) {
const uploadId = this.uploadId;
const api = `/api/logs/csv-uploads/${uploadId}`;
uploadloader(); // 進度條
try {
const response = await axios.post(api, data);
this.uploadLogId = response.data.id;
Swal.close(); // 關閉進度條
await this.rename(); // 改檔名
await uploadSuccess();
this.$router.push({name: 'Files'});
} catch(error) {
console.log('error:', error);
if(error.response.status === 422) {
uploadFailed(error.response.data.detail[0].type, error.response.data.detail[0].msg);
} else {
Swal.close(); // 關閉進度條
apiError(error, 'Failed to upload the log files.');
}
}
},
/**
* Rename a Log
*/
async rename() {
const id = this.uploadLogId;
const api = `/api/logs/${id}/name`;
const data = {"name": this.uploadFileName};
try {
const response = await axios.put(api, data);
console.log('response:', response);
this.uploadFileName = null;
} catch(error) {
console.log('error:', error);
apiError(error, 'Failed to rename.');
}
}
}, },
}) })

View File

@@ -176,6 +176,8 @@
this.isLoading = true; this.isLoading = true;
this.store.fetchAllFiles(); this.store.fetchAllFiles();
this.isLoading = false; this.isLoading = false;
},
beforeUnmount() {
} }
} }
</script> </script>

View File

@@ -1,40 +1,79 @@
<template> <template>
<section class="h-screen-main w-full px-4 flex flex-col justify-between items-start"> <section class="h-screen-main w-full px-4 flex flex-col justify-between items-start">
<!-- Upload Content --> <!-- Upload Content -->
<div class="w-full"> <div class="w-full h-[calc(100%_-_64px)]">
<!-- File name --> <!-- File name -->
<p class="font-bold text-base leading-[48px] border-b border-neutral-300">File Name(點擊可以改名字)</p> <p id="uploadFileName" class="font-bold text-base leading-[48px] border-b border-neutral-300 cursor-pointer focus-visible:outline-primary/50 focus-visible:outline-0 " contentEditable="true">{{ uploadFileName }}<span class="material-symbols-outlined align-text-bottom text-lg" contentEditable="false">stylus</span></p>
<!-- Upload notification --> <!-- Upload notification -->
<div class="flex justify-start items-center space-x-2 ml-2 py-2"> <div class="flex justify-start items-center space-x-2 ml-2 py-2">
<span class="material-symbols-outlined text-neutral-700" v-tooltip.right="tooltipUpload">info</span> <span class="material-symbols-outlined text-neutral-700 cursor-pointer" v-tooltip.right="tooltipUpload">info</span>
<span class="w-px h-7 bg-neutral-300"></span> <span class="w-px h-7 bg-neutral-300"></span>
<div class="flex items-center">
<span class="material-symbols-outlined text-primary">notifications</span>
<span class="material-symbols-outlined material-fill text-danger">warning</span>
</div>
<!-- Upload text --> <!-- Upload text -->
<div class="flex justify-start items-center"> <div>
<div v-if="isInform" class="flex justify-start items-center space-x-2 duration-700">
<span class="material-symbols-outlined text-primary">notifications</span>
<p class="text-primary text-sm"> <p class="text-primary text-sm">
Please verify the label for each column before uploading. Please verify the label for each column before uploading.
</p> </p>
</div>
<div v-else class="flex justify-start items-center space-x-2 duration-700">
<span class="material-symbols-outlined material-fill text-danger">warning</span>
<p class="text-danger text-sm"> <p class="text-danger text-sm">
Need to select [A], [B], [C]. Need to select
<span v-for="(item, index) in informData" :key="index">[ {{ item }} ]<span v-if="index !== informData.length - 1">, </span></span>.
</p> </p>
<div class=" space-y-1"> <div v-if="repeatedData.length !== 0" class="duration-700">
<p class="text-danger text-sm"> <p v-if="repeatedData.length === 1" class="text-danger text-sm">
[Case ID] has been assigned. [ {{repeatedData[0]}} ] has been assigned.
</p> </p>
<p class="text-danger text-sm"> <p v-else class="text-danger text-sm">
[Case ID], [Activity] have been assigned. <span v-for="(item, index) in repeatedData" :key="index">[ {{ item }} ]<span v-if="index !== repeatedData.length - 1">, </span></span>
have been assigned.
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Upload table --> <!-- Upload table -->
<div class="overflow-y-auto overflow-x-auto scrollbar max-h-[calc(100%_-_97px)]">
<table class="text-sm w-full table-fixed border-separate border-spacing-0">
<thead class="sticky top-0 bg-neutral-10">
<tr>
<td v-for="(item, index) in uploadDetail?.columns" :key="index" class="border border-neutral-500 p-2">{{ item }}</td>
</tr>
<tr>
<td v-for="(item, index) in uploadDetail?.columns" :key="index" class="px-2 py-1 bg-neutral-300 border border-neutral-500">
<Dropdown
v-model="selectedColumns[index]"
:options="columnType"
optionLabel="name"
placeholder="Not Assigned"
class="w-full !border-neutral-500"
:data-type="item"
:inputId="index.toString()"
:inputClass="selectedColumns[index]?.color"
inputClass="!text-sm">
<template #option="slotProps">
<div :class="slotProps.option.color" class="text-sm">
<span>{{ slotProps.option.name }}</span>
</div>
</template>
</Dropdown>
</td>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in uploadDetail?.data" :key="index">
<td v-for="(itemDetail, key) in item" :key="key" class="border border-neutral-500 p-2 truncate break-keep">{{ itemDetail }}</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<!-- Upload button --> <!-- Upload button -->
<div class="w-full text-right space-x-4 px-8 py-4"> <div class="w-full text-right space-x-4 px-8 py-4">
<button type="button" class="btn btn-sm btn-neutral" @click="reset">Cancel</button> <button type="button" class="btn btn-sm btn-neutral" @click="cancel">Cancel</button>
<button type="button" class="btn btn-sm btn-neutral" @click="reset">Reset</button>
<button type="button" class="btn btn-sm" @click="submit" :disabled="isDisabled" :class="isDisabled ? 'btn-disable' : 'btn-neutral'">Upload</button> <button type="button" class="btn btn-sm" @click="submit" :disabled="isDisabled" :class="isDisabled ? 'btn-disable' : 'btn-neutral'">Upload</button>
</div> </div>
</section> </section>
@@ -42,45 +81,175 @@
<script> <script>
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import LoadingStore from '@/stores/loading.js'; import LoadingStore from '@/stores/loading.js';
import FilesStore from '@/stores/files.js';
import { uploadFailed, uploadSuccess, uploadConfirm } from '@/module/alertModal.js'
export default { export default {
setup() { setup() {
const loadingStore = LoadingStore(); const loadingStore = LoadingStore();
const filesStore = FilesStore();
const { isLoading } = storeToRefs(loadingStore); const { isLoading } = storeToRefs(loadingStore);
const { uploadDetail, uploadId, uploadFileName } = storeToRefs(filesStore);
return { isLoading } return { isLoading, filesStore, uploadDetail, uploadId, uploadFileName }
}, },
data() { data() {
return { return {
isDisabled: true,
tooltipUpload: { tooltipUpload: {
value: `1. Case ID: A unique identifier for each case. value: `1. Case ID: A unique identifier for each case.
2. Activity: A process step executed by either a system (automated) or humans (manual). 2. Activity: A process step executed by either a system (automated) or humans (manual).
3. Activity Instance ID: A unique identifier for a single occurrence of an activity. 3. Activity Instance ID: A unique identifier for a single occurrence of an activity.
4. Timestamp: The time of occurrence of a particular event, such as the start or end of an activity. 4. Timestamp: The time of occurrence of a particular event, such as the start or end of an activity.
5. Status: Activity status, such as Start or Complete. 5. Status: Activity status, such as Start or Complete.
6. Attribute: A property that can be associated with a case to provide additional information about that case. 6. Attribute: A property that can be associated with a case to provide additional information about that case.`,
7. Resource: A resource refers to any entity that is required to carry out a business process. This can include people, equipment, software, or any other type of asset.`, // 7. Resource: A resource refers to any entity that is required to carry out a business process. This can include people, equipment, software, or any other type of asset.
class: '!max-w-[400px] !text-[10px]', class: '!max-w-[400px] !text-[10px]',
autoHide: false, autoHide: false,
} },
columnType: [
{ name: 'Case ID*', code: 'case_id', color: '!text-secondary', value: '' },
{ name: 'Timestamp*', code: 'timestamp', color: '!text-secondary', value: '' },
{ name: 'Status*', code: 'status', color: '!text-secondary', value: '' },
{ name: 'Activity*', code: 'name', color: '!text-secondary', value: '' },
{ name: 'Activity Instance ID*', code: 'instance', color: '!text-secondary', value: '' },
{ name: 'Case Attribute', code: 'case_attributes', color: '!text-primary', value: '' },
// { name: 'Resource', code: '', color: '', value: '' }, // 現階段沒有,未來可能有
{ name: 'Not Assigned', code: '', color: '!text-neutral-700', value: '' },
],
selectedColumns: [],
isInform: true, // true: 藍字提示; false: 紅字提示
informData: [], // 紅字提示,尚未選擇的 type
repeatedData: [], // 紅字提示,重複選擇的 type
baseTypeData: ['Case ID*', 'Timestamp*', 'Status*', 'Activity*', 'Activity Instance ID*'],
};
},
computed: {
isDisabled: function() {
// 1. 長度一樣,強制每一個都要選
// 2. 不為 null undefind
let hasValue = !this.selectedColumns.includes(undefined);
let result = !(this.selectedColumns.length === this.uploadDetail?.columns.length && this.informData.length === 0 && this.repeatedData.length === 0 && hasValue) ? true : false;
return result
},
},
watch: {
selectedColumns: {
deep: true, // 監聽陣列內部的變化
handler(newVal, oldVal) {
this.updateValidationData(newVal);
},
} }
}, },
methods: { methods: {
uploadFailed,
uploadSuccess,
uploadConfirm,
/**
* 驗證,根據新的 selectedColumns 更新 isInform、informData 和 repeatedData
* @param {Array} data
*/
updateValidationData(data) {
const nameOccurrences = {};
let selectedData = [] // 已經選擇的 data
this.informData = []; // 尚未選擇的 data
this.repeatedData = []; // 重複選擇的 data
data.forEach(item => {
const { name, code } = item;
if(nameOccurrences[name]) {
// 'Not Assigned'、'Case Attribute' 不列入驗證
if(!code || code === 'case_attributes') return;
nameOccurrences[name]++;
this.repeatedData.push(name);
}else {
nameOccurrences[name] = 1;
selectedData.push(name);
this.informData = this.baseTypeData.filter(item => !selectedData.includes(item));
}
});
this.isInform = (this.informData.length === 0 && this.repeatedData.length === 0) ? true : false;
},
/**
* Reset Button
*/
reset() {
// 路徑不列入歷史紀錄
this.selectedColumns = [];
},
/** /**
* Cancel Button * Cancel Button
*/ */
reset() {}, cancel() {
// 路徑不列入歷史紀錄
this.$router.push({name: 'Files', replace: true});
},
/** /**
* Upload Button * Upload Button
*/ */
submit() {}, async submit() {
// Post API Data
let fetchData = {
timestamp: '',
case_id: '',
name: '',
instance: '',
status: '',
case_attributes: []
};
// 給值
let haveValueData = this.selectedColumns.map((column, i) => {
if (column && this.uploadDetail.columns[i]) {
return {
name: column.name,
code: column.code,
color: column.color,
value: this.uploadDetail.columns[i]
}
}
});
// 取得欲更改的檔名
this.uploadFileName = document.querySelector('#uploadFileName').firstChild.textContent;
// 設定第二階段上傳的 data
haveValueData.forEach(column => {
switch (column.code) {
case 'timestamp':
fetchData.timestamp = column.value;
break;
case 'case_id':
fetchData.case_id = column.value;
break;
case 'name':
fetchData.name = column.value;
break;
case 'instance':
fetchData.instance = column.value;
break;
case 'status':
fetchData.status = column.value;
break;
case 'case_attributes':
fetchData.case_attributes.push(column.value);
break;
default:
break;
}
});
this.uploadConfirm(fetchData);
},
}, },
mounted() { mounted() {
// 要有 uploadID 才能進來
this.isInform = true; // 初始為藍字提示
this.filesStore.getUploadDetail();
this.isLoading = false; this.isLoading = false;
}, },
beforeUnmount() {
// 離開頁面要刪 uploadID
this.uploadId = null;
this.uploadFileName = null;
}
} }
// beforeRouteUpdate(){} // beforeRouteUpdate(){}
</script> </script>
<style scoped>
</style>

View File

@@ -11,7 +11,7 @@ module.exports = {
transparent: 'transparent', transparent: 'transparent',
current: 'currentColor', current: 'currentColor',
'primary': '#0099FF', 'primary': '#0099FF',
// 'secondary': '#FFAA44', 'secondary': '#FFAA44',
'cfm-primary': '#0099FF', 'cfm-primary': '#0099FF',
'cfm-secondary': '#FFAA44', 'cfm-secondary': '#FFAA44',
'neutral': { 'neutral': {