Add JSDoc documentation and file headers to all source files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/AccountManagement/AccountAdmin Account
|
||||
* administration page with user list, search, create/edit/
|
||||
* delete actions, admin toggle, and infinite scroll.
|
||||
*/
|
||||
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import { useModalStore } from '@/stores/modal';
|
||||
@@ -287,8 +297,8 @@ const onAdminInputClick = async(userData, inputIsAdminOn) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 無限滾動: 監聯 scroll 有沒有滾到底部
|
||||
* @param {element} event 滾動傳入的事件
|
||||
* Loads more account data when the user scrolls near the bottom.
|
||||
* @param {Event} event - The scroll event from the data grid.
|
||||
*/
|
||||
const handleScroll = (event) => {
|
||||
const container = event.target;
|
||||
|
||||
@@ -182,6 +182,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/AccountManagement/ModalAccountEditCreate Modal
|
||||
* for creating new accounts or editing existing accounts with
|
||||
* username, name, password fields and validation.
|
||||
*/
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import i18next from "@/i18n/i18n.js";
|
||||
import { useModalStore } from '@/stores/modal';
|
||||
|
||||
@@ -23,6 +23,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AccountManagement/ModalAccountInfo Account
|
||||
* information modal displaying user details, admin/suspended
|
||||
* status badges, and visit count.
|
||||
*/
|
||||
|
||||
import { onBeforeMount, computed, ref } from 'vue';
|
||||
import i18next from '@/i18n/i18n.js';
|
||||
import { useAcctMgmtStore } from '@/stores/acctMgmt';
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AccountManagement/ModalContainer Container
|
||||
* that renders the appropriate account management modal
|
||||
* based on the current modal type.
|
||||
*/
|
||||
|
||||
import { computed } from 'vue';
|
||||
import { useModalStore } from '@/stores/modal';
|
||||
import ModalAccountEditCreate from './ModalAccountEditCreate.vue';
|
||||
|
||||
@@ -28,6 +28,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AccountManagement/ModalDeleteAlert Confirmation
|
||||
* modal for account deletion with yes/no buttons.
|
||||
*/
|
||||
|
||||
import { useModalStore } from '@/stores/modal';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAcctMgmtStore } from '@/stores/acctMgmt';
|
||||
@@ -39,6 +49,7 @@ const modalStore = useModalStore();
|
||||
const toast = useToast();
|
||||
const router = useRouter();
|
||||
|
||||
/** Confirms account deletion, shows success toast, and navigates to account admin page. */
|
||||
const onDeleteConfirmBtnClick = async() => {
|
||||
if(await acctMgmtStore.deleteAccount(acctMgmtStore.currentViewingUser.username)){
|
||||
toast.success(i18next.t("AcctMgmt.MsgAccountDeleteSuccess"));
|
||||
@@ -48,6 +59,7 @@ const onDeleteConfirmBtnClick = async() => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Cancels deletion and closes the modal. */
|
||||
const onNoBtnClick = () => {
|
||||
modalStore.closeModal();
|
||||
};
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AccountManagement/ModalHeader Reusable modal
|
||||
* header with title text and close button.
|
||||
*/
|
||||
|
||||
import { useModalStore } from '@/stores/modal';
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AccountManagement/MyAccount My Account page for
|
||||
* viewing and editing the current user's name and password.
|
||||
*/
|
||||
|
||||
import { onMounted, computed, ref } from 'vue';
|
||||
import i18next from '@/i18n/i18n.js';
|
||||
import { useLoginStore } from '@/stores/login';
|
||||
@@ -147,18 +157,22 @@ const isPwdEditable = ref(false);
|
||||
const isPwdEyeOn = ref(false);
|
||||
const isPwdLengthValid = ref(true);
|
||||
|
||||
/** Enables the name editing input. */
|
||||
const onEditNameClick = () => {
|
||||
isNameEditable.value = true;
|
||||
};
|
||||
|
||||
/** Enables the password editing input. */
|
||||
const onResetPwdClick = () => {
|
||||
isPwdEditable.value = true;
|
||||
};
|
||||
|
||||
/** Validates that the password meets the minimum length requirement. */
|
||||
const validatePwdLength = () => {
|
||||
isPwdLengthValid.value = inputPwd.value.length >= PWD_VALID_LENGTH;
|
||||
};
|
||||
|
||||
/** Saves the edited name to the server and refreshes user data. */
|
||||
const onSaveNameClick = async() => {
|
||||
if(inputName.value.length > 0) {
|
||||
await acctMgmtStore.editAccountName(username, inputName.value);
|
||||
@@ -169,6 +183,7 @@ const onSaveNameClick = async() => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Validates and saves the new password. */
|
||||
const onSavePwdClick = async() => {
|
||||
validatePwdLength();
|
||||
if (isPwdLengthValid.value) {
|
||||
@@ -180,17 +195,23 @@ const onSavePwdClick = async() => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Cancels name editing and restores the original value. */
|
||||
const onCancelNameClick = () => {
|
||||
isNameEditable.value = false;
|
||||
inputName.value = name.value;
|
||||
};
|
||||
|
||||
/** Cancels password editing and clears the input. */
|
||||
const onCancelPwdClick = () => {
|
||||
isPwdEditable.value = false;
|
||||
inputPwd.value = '';
|
||||
isPwdLengthValid.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the password visibility eye button.
|
||||
* @param {boolean} toBeOpen - Whether to show the password.
|
||||
*/
|
||||
const togglePwdEyeBtn = (toBeOpen) => {
|
||||
isPwdEyeOn.value = toBeOpen;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/AuthContainer Layout container for
|
||||
* authentication-related pages with header and navbar.
|
||||
*/
|
||||
|
||||
import Header from "@/components/Header.vue";
|
||||
import Navbar from "@/components/Navbar.vue";
|
||||
</script>
|
||||
|
||||
@@ -150,6 +150,16 @@
|
||||
</main>
|
||||
</template>
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/Compare/Dashboard/Compare
|
||||
* Performance comparison dashboard with side-by-side
|
||||
* charts comparing two datasets.
|
||||
*/
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -253,6 +263,10 @@ const colorSecondary = '#FFAA44';
|
||||
const sidebarState = ref(false);
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Scrolls to the selected chart section.
|
||||
* @param {string} tagId - The anchor tag ID to navigate to.
|
||||
*/
|
||||
function handleClick(tagId) {
|
||||
isActive.value = tagId;
|
||||
|
||||
@@ -263,11 +277,21 @@ function handleClick(tagId) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a tag ID is safe for navigation.
|
||||
* @param {string} tagId - The tag ID to validate.
|
||||
* @returns {boolean} True if the tag ID is safe.
|
||||
*/
|
||||
function isSafeTagId(tagId) {
|
||||
const pattern = /^#?[a-zA-Z0-9]*$/;
|
||||
return pattern.test(tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates evenly-spaced X-axis label timestamps.
|
||||
* @param {object} valueData - Object with min and max date strings.
|
||||
* @returns {Array<number>} Array of timestamp values.
|
||||
*/
|
||||
function setXLabelsData(valueData) {
|
||||
const min = new Date(valueData.min).getTime();
|
||||
const max = new Date(valueData.max).getTime();
|
||||
@@ -281,6 +305,11 @@ function setXLabelsData(valueData) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the height for a horizontal bar chart based on bar count.
|
||||
* @param {object} chartData - The chart data with x_axis labels.
|
||||
* @returns {string} The CSS height string.
|
||||
*/
|
||||
function getHorizontalBarHeight(chartData) {
|
||||
const totalBars = chartData.x_axis.labels.length;
|
||||
let hBarHeight = horizontalBarHeight;
|
||||
@@ -292,6 +321,13 @@ function getHorizontalBarHeight(chartData) {
|
||||
return hBarHeight + 'px'
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a line chart configuration for Chart.js.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} yUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getLineChart(chartData, content, yUnit) {
|
||||
let datasetsPrimary;
|
||||
let datasetsSecondary;
|
||||
@@ -415,6 +451,13 @@ function getLineChart(chartData, content, yUnit) {
|
||||
return [primeVueSetData, primeVueSetOption];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a bar chart configuration for Chart.js.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} caller - Identifier for chart-specific options.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getBarChart(chartData, content, caller) {
|
||||
const getMoment = (time)=> moment(time).format('YYYY/MM/DD');
|
||||
const labelPrimary = chartData.data[0].label;
|
||||
@@ -514,6 +557,14 @@ function getBarChart(chartData, content, caller) {
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a horizontal bar chart configuration for Chart.js.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {boolean} isSingle - Whether labels are single values.
|
||||
* @param {string} xUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getHorizontalBarChart(chartData, content, isSingle, xUnit) {
|
||||
const maxY = chartData.y_axis.max;
|
||||
const getSimpleTimeLabel = simpleTimeLabel;
|
||||
@@ -633,6 +684,12 @@ function getHorizontalBarChart(chartData, content, isSingle, xUnit) {
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes Chart.js scale options with content and tick data.
|
||||
* @param {object} whichScaleObj - The base scale options object.
|
||||
* @param {object} options - Options containing content and ticksOfXAxis.
|
||||
* @returns {object} The customized scale options.
|
||||
*/
|
||||
function getCustomizedScaleOption(whichScaleObj, {customizeOptions: {
|
||||
content,
|
||||
ticksOfXAxis,
|
||||
@@ -644,6 +701,12 @@ function getCustomizedScaleOption(whichScaleObj, {customizeOptions: {
|
||||
return resultScaleObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets axis titles on a scale options object.
|
||||
* @param {object} whichScaleObj - The base scale options.
|
||||
* @param {object} content - Object with x and y axis title text.
|
||||
* @returns {object} The scale options with updated titles.
|
||||
*/
|
||||
function customizeScaleChartOptionTitleByContent(whichScaleObj, content){
|
||||
if (!content) {
|
||||
return whichScaleObj;
|
||||
@@ -668,6 +731,12 @@ function customizeScaleChartOptionTitleByContent(whichScaleObj, content){
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom tick callbacks on a scale options object.
|
||||
* @param {object} scaleObjectToAlter - The scale options to modify.
|
||||
* @param {Array} ticksOfXAxis - The formatted tick labels.
|
||||
* @returns {object} The scale options with custom ticks.
|
||||
*/
|
||||
function customizeScaleChartOptionTicks(scaleObjectToAlter, ticksOfXAxis) {
|
||||
return {
|
||||
...scaleObjectToAlter,
|
||||
@@ -683,6 +752,13 @@ function customizeScaleChartOptionTicks(scaleObjectToAlter, ticksOfXAxis) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an alternative line chart configuration with inline scales.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} yUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getLineChart0(chartData, content, yUnit) {
|
||||
let datasetsPrimary;
|
||||
let datasetsSecondary;
|
||||
@@ -836,6 +912,14 @@ function getLineChart0(chartData, content, yUnit) {
|
||||
return [primeVueSetData, primeVueSetOption];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a horizontal bar chart for cases-by-task data.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {boolean} isSingle - Whether labels are single values.
|
||||
* @param {string} [xUnit='count'] - The x-axis unit.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getCaseByTaskHorizontalBarChart(chartData, content, isSingle, xUnit = 'count') {
|
||||
const labelPrimary = chartData.data[0].label;
|
||||
const labelSecondary = chartData.data[1].label;
|
||||
@@ -990,6 +1074,14 @@ function getCaseByTaskHorizontalBarChart(chartData, content, isSingle, xUnit = '
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a horizontal bar chart for average process time data.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {boolean} isSingle - Whether labels are single values.
|
||||
* @param {string} [xUnit='date'] - The x-axis unit.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getAvgProcessTimeHorizontalBarChart(chartData, content, isSingle, xUnit="date") {
|
||||
const maxY = chartData.y_axis.max;
|
||||
const getSimpleTimeLabel = simpleTimeLabel;
|
||||
|
||||
@@ -61,6 +61,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/Compare/MapCompare
|
||||
* Side-by-side process map comparison view with
|
||||
* dual Cytoscape graphs.
|
||||
*/
|
||||
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
|
||||
export default {
|
||||
@@ -241,27 +252,48 @@ watch(sidebarState, (newValue) => {
|
||||
});
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Switches the map type and re-renders the Cytoscape graph.
|
||||
* @param {string} type - 'processMap' or 'bpmn'.
|
||||
*/
|
||||
async function switchMapType(type) {
|
||||
mapType.value = type;
|
||||
createCy(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the edge curve style and re-renders the graph.
|
||||
* @param {string} style - The curve style.
|
||||
*/
|
||||
async function switchCurveStyles(style) {
|
||||
curveStyle.value = style;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the layout direction and re-renders the graph.
|
||||
* @param {string} rankValue - 'LR' (horizontal) or 'TB' (vertical).
|
||||
*/
|
||||
async function switchRank(rankValue) {
|
||||
rank.value = rankValue;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the data layer type/option and re-renders the graph.
|
||||
* @param {string} type - 'freq' or 'duration'.
|
||||
* @param {string} option - The data option (e.g., 'total', 'average').
|
||||
*/
|
||||
async function switchDataLayerType(type, option) {
|
||||
dataLayerType.value = type;
|
||||
dataLayerOption.value = option;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the displayed trace and reloads its detail.
|
||||
* @param {object} e - Object containing the trace id.
|
||||
*/
|
||||
async function switchTraceId(e) {
|
||||
if (e.id == traceId.value) return;
|
||||
isLoading.value = true;
|
||||
@@ -271,6 +303,10 @@ async function switchTraceId(e) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates node data from the process map or BPMN model.
|
||||
* @param {object} mapData - The map data object to populate.
|
||||
*/
|
||||
function setNodesData(mapData) {
|
||||
const mapTypeVal = mapType.value;
|
||||
const logFreq = {
|
||||
@@ -365,6 +401,10 @@ function setNodesData(mapData) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates edge data from the process map or BPMN model.
|
||||
* @param {object} mapData - The map data object to populate.
|
||||
*/
|
||||
function setEdgesData(mapData) {
|
||||
const mapTypeVal = mapType.value;
|
||||
const logDuration = {
|
||||
@@ -393,6 +433,10 @@ function setEdgesData(mapData) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and renders the Cytoscape graph.
|
||||
* @param {string} type - 'processMap' or 'bpmn'.
|
||||
*/
|
||||
async function createCy(type) {
|
||||
const graphId = document.getElementById('cy');
|
||||
const mapData = type === 'processMap' ? processMapData.value : bpmnData.value;
|
||||
@@ -410,6 +454,10 @@ async function createCy(type) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns background images to activity nodes based on data level grouping.
|
||||
* @param {object} mapData - The map data containing nodes.
|
||||
*/
|
||||
function setActivityBgImage(mapData) {
|
||||
const nodes = mapData.nodes;
|
||||
const groupSize = Math.floor(nodes.length / ImgCapsules.length);
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/Discover/Conformance Conformance checking page
|
||||
* with sidebar rule configuration and results display.
|
||||
*/
|
||||
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -53,6 +53,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/Discover/Map/Map
|
||||
* Process map view with Cytoscape graph rendering,
|
||||
* sidebars for view settings, filters, traces, and
|
||||
* statistics.
|
||||
*/
|
||||
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
|
||||
export default {
|
||||
@@ -230,27 +241,48 @@ watch(sidebarState, (newValue) => {
|
||||
});
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Switches the map type and re-renders the Cytoscape graph.
|
||||
* @param {string} type - 'processMap' or 'bpmn'.
|
||||
*/
|
||||
async function switchMapType(type) {
|
||||
mapType.value = type;
|
||||
createCy(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the edge curve style and re-renders the graph.
|
||||
* @param {string} style - The curve style ('unbundled-bezier' or 'taxi').
|
||||
*/
|
||||
async function switchCurveStyles(style) {
|
||||
curveStyle.value = style;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the layout direction and re-renders the graph.
|
||||
* @param {string} rankValue - 'LR' (horizontal) or 'TB' (vertical).
|
||||
*/
|
||||
async function switchRank(rankValue) {
|
||||
rank.value = rankValue;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the data layer type/option and re-renders the graph.
|
||||
* @param {string} type - 'freq' or 'duration'.
|
||||
* @param {string} option - The data option (e.g., 'total', 'average').
|
||||
*/
|
||||
async function switchDataLayerType(type, option){
|
||||
dataLayerType.value = type;
|
||||
dataLayerOption.value = option;
|
||||
createCy(mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the displayed trace and reloads its detail.
|
||||
* @param {object} e - Object containing the trace id.
|
||||
*/
|
||||
async function switchTraceId(e) {
|
||||
if(e.id == traceId.value) return;
|
||||
isLoading.value = true;
|
||||
@@ -260,6 +292,10 @@ async function switchTraceId(e) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates node data from the process map or BPMN model.
|
||||
* @param {object} mapData - The map data object to populate.
|
||||
*/
|
||||
function setNodesData(mapData) {
|
||||
const mapTypeVal = mapType.value;
|
||||
const logFreq = {
|
||||
@@ -348,6 +384,10 @@ function setNodesData(mapData) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates edge data from the process map or BPMN model.
|
||||
* @param {object} mapData - The map data object to populate.
|
||||
*/
|
||||
function setEdgesData(mapData) {
|
||||
const mapTypeVal = mapType.value;
|
||||
const logDuration = {
|
||||
@@ -376,6 +416,10 @@ function setEdgesData(mapData) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and renders the Cytoscape graph.
|
||||
* @param {string} type - 'processMap' or 'bpmn'.
|
||||
*/
|
||||
async function createCy(type) {
|
||||
const graphId = document.getElementById('cy');
|
||||
const mapData = type === 'processMap'? processMapData.value: bpmnData.value;
|
||||
@@ -393,6 +437,10 @@ async function createCy(type) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns background images to activity nodes based on data level grouping.
|
||||
* @param {object} mapData - The map data containing nodes.
|
||||
*/
|
||||
function setActivityBgImage(mapData) {
|
||||
const nodes = mapData.nodes;
|
||||
const groupSize = Math.floor(nodes.length / ImgCapsules.length);
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/Discover/Performance/FreqChart
|
||||
* Frequency chart component displaying activity
|
||||
* occurrence data with Chart.js bar charts.
|
||||
*/
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {
|
||||
setTimeStringFormatBaseOnTimeDifference,
|
||||
@@ -126,8 +137,8 @@ const customizeScaleChartOptionTicks = (scaleObjectToAlter, ticksOfXAxis) => {
|
||||
};
|
||||
|
||||
/** Compare page and Performance have this same function.
|
||||
* 在一個基本的物件上加以客製化這個物件,客製化的參照來源是 content 的內容
|
||||
* 之所以有辦法這樣撰寫,是因為我們知道物件的順序是先 x 再 title 再 text
|
||||
* Customizes a base tooltip object using the content data.
|
||||
* The object order is known to be: x, then title, then text.
|
||||
* This function alters the title property of known scales object of Chart option
|
||||
* This is based on the fact that we know the order must be x -> title -> text.
|
||||
* @param {object} whichScaleObj PrimeVue scale option object to reference to
|
||||
@@ -160,6 +171,12 @@ const customizeScaleChartOptionTitleByContent = (whichScaleObj, content) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the PrimeVue line chart data and options configuration.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} pageName - 'Compare' or page identifier.
|
||||
*/
|
||||
const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
|
||||
let datasetsArr;
|
||||
let datasets;
|
||||
|
||||
@@ -136,6 +136,18 @@
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/Discover/Performance
|
||||
* Performance analysis page with activity duration
|
||||
* charts, frequency data, and statistical summaries.
|
||||
*/
|
||||
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
|
||||
export default {
|
||||
@@ -247,6 +259,10 @@ const avgWaitingTimeByEdgeHeight = ref(500);
|
||||
const casesByTaskHeight = ref(500);
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Scrolls to the selected chart section.
|
||||
* @param {string} tagId - The anchor tag ID to navigate to.
|
||||
*/
|
||||
function handleClick(tagId) {
|
||||
isActive.value = tagId;
|
||||
|
||||
@@ -257,11 +273,21 @@ function handleClick(tagId) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a tag ID is safe for navigation.
|
||||
* @param {string} tagId - The tag ID to validate.
|
||||
* @returns {boolean} True if the tag ID is safe.
|
||||
*/
|
||||
function isSafeTagId(tagId) {
|
||||
const pattern = /^#?[a-zA-Z0-9-]*$/;
|
||||
return pattern.test(tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates evenly-spaced X-axis label timestamps.
|
||||
* @param {object} valueData - Object with min and max date strings.
|
||||
* @returns {Array<number>} Array of timestamp values.
|
||||
*/
|
||||
function setXLabelsData(valueData) {
|
||||
const min = new Date(valueData.min).getTime();
|
||||
const max = new Date(valueData.max).getTime();
|
||||
@@ -275,6 +301,11 @@ function setXLabelsData(valueData) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the height for a horizontal bar chart based on bar count.
|
||||
* @param {object} chartData - The chart data.
|
||||
* @returns {string} The CSS height string.
|
||||
*/
|
||||
function getHorizontalBarHeight(chartData) {
|
||||
const totalBars = chartData.data.length;
|
||||
let hBarHeight = horizontalBarHeight;
|
||||
@@ -284,6 +315,12 @@ function getHorizontalBarHeight(chartData) {
|
||||
return hBarHeight + 'px'
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a bar chart configuration for Chart.js.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getBarChart(chartData, content) {
|
||||
const getMoment = (time)=> moment(time).format('YYYY/M/D hh:mm:ss');
|
||||
let primeVueSetData = {};
|
||||
@@ -390,6 +427,14 @@ function getBarChart(chartData, content) {
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a horizontal bar chart configuration for Chart.js.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {boolean} isSingle - Whether labels are single values.
|
||||
* @param {string} xUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getHorizontalBarChart(chartData, content, isSingle, xUnit) {
|
||||
const maxY = chartData.y_axis.max;
|
||||
const getSimpleTimeLabel = simpleTimeLabel;
|
||||
@@ -526,6 +571,12 @@ function getHorizontalBarChart(chartData, content, isSingle, xUnit) {
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes Chart.js scale options with content and tick data.
|
||||
* @param {object} whichScaleObj - The base scale options object.
|
||||
* @param {object} options - Options containing content and ticksOfXAxis.
|
||||
* @returns {object} The customized scale options.
|
||||
*/
|
||||
function getCustomizedScaleOption(whichScaleObj, {customizeOptions: {
|
||||
content,
|
||||
ticksOfXAxis,
|
||||
@@ -537,6 +588,12 @@ function getCustomizedScaleOption(whichScaleObj, {customizeOptions: {
|
||||
return resultScaleObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets axis titles on a scale options object.
|
||||
* @param {object} whichScaleObj - The base scale options.
|
||||
* @param {object} content - Object with x and y axis title text.
|
||||
* @returns {object} The scale options with updated titles.
|
||||
*/
|
||||
function customizeScaleChartOptionTitleByContent(whichScaleObj, content){
|
||||
if (!content) {
|
||||
return whichScaleObj;
|
||||
@@ -561,6 +618,12 @@ function customizeScaleChartOptionTitleByContent(whichScaleObj, content){
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom tick callbacks on a scale options object.
|
||||
* @param {object} scaleObjectToAlter - The scale options to modify.
|
||||
* @param {Array} ticksOfXAxis - The formatted tick labels.
|
||||
* @returns {object} The scale options with custom ticks.
|
||||
*/
|
||||
function customizeScaleChartOptionTicks(scaleObjectToAlter, ticksOfXAxis) {
|
||||
return {
|
||||
...scaleObjectToAlter,
|
||||
@@ -576,6 +639,13 @@ function customizeScaleChartOptionTicks(scaleObjectToAlter, ticksOfXAxis) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a line chart with explicit scale declarations.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} yUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getExplicitDeclaredLineChart(chartData, content, yUnit) {
|
||||
const minX = chartData.x_axis.min;
|
||||
const maxX = chartData.x_axis.max;
|
||||
@@ -699,6 +769,13 @@ function getExplicitDeclaredLineChart(chartData, content, yUnit) {
|
||||
return [primeVueSetData, primeVueSetOption]
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a line chart for average waiting time data.
|
||||
* @param {object} chartData - The chart data from the API.
|
||||
* @param {object} content - The axis label content.
|
||||
* @param {string} yUnit - 'date' or 'count'.
|
||||
* @returns {Array} [chartData, chartOptions] tuple.
|
||||
*/
|
||||
function getAvgWaitingTimeLineChart(chartData, content, yUnit) {
|
||||
const getMoment = (time)=> moment(time).format('YYYY/M/D hh:mm:ss');
|
||||
const minX = chartData.x_axis.min;
|
||||
|
||||
@@ -221,6 +221,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/Files/Files File management page with data
|
||||
* table listing uploaded files, discover/compare navigation,
|
||||
* and file operations (rename, delete).
|
||||
*/
|
||||
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -313,7 +323,7 @@
|
||||
});
|
||||
|
||||
/**
|
||||
* 時間排序,如果沒有 accessed_at 就不加入 data
|
||||
* Sorts by time; entries without accessed_at are excluded.
|
||||
*/
|
||||
const recentlyUsedFiles = computed(() => {
|
||||
let recentlyUsed = Array.from(store.allFiles);
|
||||
@@ -375,8 +385,8 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* 選擇該 files 進入 Discover/Compare/Design 頁面
|
||||
* @param {object} file 該 file 的詳細資料
|
||||
* Selects a file and navigates to the Discover/Compare/Design page.
|
||||
* @param {object} file - The file details.
|
||||
*/
|
||||
function enterDiscover(file){
|
||||
let type;
|
||||
@@ -416,7 +426,7 @@
|
||||
|
||||
/**
|
||||
* Right Click DOM Event
|
||||
* @param {event} event 該 file 的詳細資料
|
||||
* @param {Event} event - The mouse event.
|
||||
* @param {string} file file's name
|
||||
*/
|
||||
function onRightClick(event, file) {
|
||||
@@ -428,7 +438,7 @@
|
||||
|
||||
/**
|
||||
* Right Click Table DOM Event
|
||||
* @param {event} event 該 file 的詳細資料
|
||||
* @param {Event} event - The right-click event with row data.
|
||||
*/
|
||||
function onRightClickTable(event) {
|
||||
selectedType.value = event.data.type;
|
||||
@@ -438,9 +448,9 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Right Click Gride Card DOM Event
|
||||
* @param {event} event 該 file 的詳細資料
|
||||
* @param {number} index 該 file 的 index
|
||||
* Right Click Grid Card DOM Event
|
||||
* @param {object} file - The file object.
|
||||
* @param {number} index - The file index.
|
||||
*/
|
||||
function onGridCardClick(file, index) {
|
||||
selectedType.value = file.type;
|
||||
@@ -451,9 +461,9 @@
|
||||
|
||||
/**
|
||||
* File's Rename
|
||||
* @param {string} type 該檔案的 type
|
||||
* @param {number} id 該檔案的 id
|
||||
* @param {string} source hover icon 該檔案的 icon
|
||||
* @param {string} type - The file type.
|
||||
* @param {number} id - The file ID.
|
||||
* @param {string} source - The hover icon source.
|
||||
* @param {string} fileName file's name
|
||||
*/
|
||||
function rename(type, id, source, fileName) {
|
||||
@@ -467,9 +477,10 @@
|
||||
|
||||
/**
|
||||
* Delete file
|
||||
* @param {string} type 該檔案的 type
|
||||
* @param {number} id 該檔案的 id
|
||||
* @param {string} source hover icon 該檔案的 icon
|
||||
* @param {string} type - The file type.
|
||||
* @param {number} id - The file ID.
|
||||
* @param {string} name - The file name.
|
||||
* @param {string} source - The hover icon source.
|
||||
*/
|
||||
async function deleteFile(type, id, name, source) {
|
||||
let srt = '';
|
||||
@@ -504,7 +515,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* 顯示被 Admin 或被其他帳號刪除的檔案
|
||||
* Shows files deleted by admin or by other accounts.
|
||||
*/
|
||||
function showReallyDelete(){
|
||||
let srt = '';
|
||||
@@ -529,9 +540,10 @@
|
||||
|
||||
/**
|
||||
* Download file as CSV
|
||||
* @param {string} type 該檔案的 type
|
||||
* @param {number} id 該檔案的 id
|
||||
* @param {string} source hover icon 該檔案的 icon
|
||||
* @param {string} type - The file type.
|
||||
* @param {number} id - The file ID.
|
||||
* @param {string} source - The hover icon source.
|
||||
* @param {string} name - The file name.
|
||||
*/
|
||||
function download(type, id, source, name) {
|
||||
if(type && id && source === 'list-hover' && name) {
|
||||
@@ -573,7 +585,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Grid 模板時的篩選器
|
||||
* Filter/sort handler for the grid view template.
|
||||
* @param {event} event choose columnType item
|
||||
*/
|
||||
function getGridSortData(event) {
|
||||
|
||||
@@ -47,6 +47,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
/**
|
||||
* @module views/Login/Login Login page with account/password
|
||||
* form, password visibility toggle, and return-to URL support.
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -76,8 +85,8 @@ const isDisabledButton = computed(() => {
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* when input onChange value , isInvalid === false.
|
||||
* @param {event} event input 傳入的事件
|
||||
* Clears the invalid state when the user modifies input.
|
||||
* @param {Event} event - The input change event.
|
||||
*/
|
||||
function changeHandler(event) {
|
||||
const inputValue = event.target.value;
|
||||
|
||||
@@ -11,6 +11,19 @@
|
||||
</template>
|
||||
|
||||
<script lang='ts'>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/MainContainer Main application layout with
|
||||
* header, navbar, and router view. Handles authentication
|
||||
* checks via beforeRouteEnter and unsaved-changes confirmation
|
||||
* via beforeRouteUpdate.
|
||||
*/
|
||||
|
||||
import { useLoginStore } from "@/stores/login";
|
||||
import { usePageAdminStore } from "@/stores/pageAdmin";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
@@ -116,6 +129,7 @@ const router = useRouter();
|
||||
const { tempFilterId, createFilterId, temporaryData, postRuleData, ruleData } = storeToRefs(allMapDataStore);
|
||||
const { conformanceLogTempCheckId, conformanceFilterTempCheckId } = storeToRefs(conformanceStore);
|
||||
|
||||
/** Sets the highlighted navbar item based on the current URL path on page load. */
|
||||
const setHighlightedNavItemOnLanding = () => {
|
||||
const currentPath = router.currentRoute.value.path;
|
||||
const pathSegments: string[] = currentPath.split('/').filter(segment => segment !== '');
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/MemberArea Member area page displaying the
|
||||
* logged-in user's profile information.
|
||||
*/
|
||||
|
||||
import { onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useLoginStore } from '@/stores/login';
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/NotFound404 404 page displayed when a route is not
|
||||
* found, with a link back to the Files page.
|
||||
*/
|
||||
|
||||
import Header from "@/components/Header.vue";
|
||||
import Navbar from "@/components/Navbar.vue";
|
||||
</script>
|
||||
|
||||
@@ -78,6 +78,18 @@
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
// The Lucia project.
|
||||
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
||||
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
||||
// imacat.yang@dsp.im (imacat), 2023/9/23
|
||||
/**
|
||||
* @module views/Upload File upload page with column mapping
|
||||
* dropdowns, file rename input, and validation before final
|
||||
* upload submission.
|
||||
*/
|
||||
|
||||
import { useFilesStore } from '@/stores/files';
|
||||
|
||||
export default {
|
||||
@@ -156,8 +168,8 @@ watch(selectedColumns, (newVal) => {
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Rename 離開 input 的行為
|
||||
* @param {Event} e input 傳入的事件
|
||||
* Adjusts input width and trims whitespace on blur.
|
||||
* @param {Event} e - The blur event from the file name input.
|
||||
*/
|
||||
function onBlur(e) {
|
||||
const baseWidth = 20;
|
||||
@@ -174,8 +186,8 @@ function onBlur(e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename 輸入 input 的行為
|
||||
* @param {Event} e input 傳入的事件
|
||||
* Dynamically resizes the input width as the user types.
|
||||
* @param {Event} e - The input event from the file name input.
|
||||
*/
|
||||
function onInput(e) {
|
||||
const baseWidth = 20;
|
||||
@@ -185,9 +197,10 @@ function onInput(e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* input 寬度隨著 value 響應式改變
|
||||
* @param {String} text file's name
|
||||
* @param {Event} e input 傳入的事件
|
||||
* Measures the pixel width of text using a hidden span element.
|
||||
* @param {string} text - The text to measure.
|
||||
* @param {HTMLElement} e - The input element for font style reference.
|
||||
* @returns {number} The text width in pixels.
|
||||
*/
|
||||
function getTextWidth(text, e) {
|
||||
// 替換空格為不斷行的空格
|
||||
@@ -205,8 +218,8 @@ function getTextWidth(text, e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證,根據新的 selectedColumns 更新 informData 和 repeatedData
|
||||
* @param {Array} data 已選擇的 type 的 data
|
||||
* Updates validation state (missing required and duplicate columns).
|
||||
* @param {Array} data - The currently selected column type assignments.
|
||||
*/
|
||||
function updateValidationData(data) {
|
||||
const nameOccurrences = {};
|
||||
@@ -237,25 +250,19 @@ function updateValidationData(data) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Button
|
||||
*/
|
||||
/** Resets all column selections to default. */
|
||||
function reset() {
|
||||
// 路徑不列入歷史紀錄
|
||||
selectedColumns.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Button
|
||||
*/
|
||||
/** Navigates back to the Files page without uploading. */
|
||||
function cancel() {
|
||||
// 路徑不列入歷史紀錄
|
||||
router.push({name: 'Files', replace: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload Button
|
||||
*/
|
||||
/** Submits the column mapping and triggers the second-stage upload. */
|
||||
async function submit() {
|
||||
// Post API Data
|
||||
const fetchData = {
|
||||
|
||||
Reference in New Issue
Block a user