forked from beimingwu/beimingwu
Merge pull request #101 from Learnware-LAMDA/feat(frontend)/hetersearch
feat(frontend)/hetersearch
This commit is contained in:
commit
bc000772c9
|
@ -78,9 +78,9 @@ watch(
|
|||
|
||||
<template>
|
||||
<v-app>
|
||||
<app-bar v-model:drawerOpen="drawerOpen" :routes="routes"></app-bar>
|
||||
<app-bar v-model="drawerOpen" :routes="routes"></app-bar>
|
||||
|
||||
<nav-drawer v-model:drawerOpen="drawerOpen" :routes="routes"></nav-drawer>
|
||||
<nav-drawer v-model="drawerOpen" :routes="routes"></nav-drawer>
|
||||
|
||||
<v-main class="bg-gray-100 bg-opacity-50">
|
||||
<router-view v-slot="{ Component }">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { downloadLearnware } from "./download.js";
|
||||
export { fetchex } from "./fetchex.js";
|
||||
export { default as saveContentToFile } from "./saveContentToFile.js";
|
||||
export { hex_md5 } from "./encrypt.js";
|
||||
export { downloadLearnware } from "./download";
|
||||
export { fetchex } from "./fetchex";
|
||||
export { default as saveContentToFile } from "@main/utils/saveContentToFile";
|
||||
export { hex_md5 } from "./encrypt";
|
||||
|
|
|
@ -342,7 +342,7 @@ async function handleClickExport(): Promise<void> {
|
|||
});
|
||||
}
|
||||
const csvContent = "\ufeff" + table.map((e) => e.join(",")).join("\n");
|
||||
saveContentToFile(csvContent, "user_list.csv");
|
||||
saveContentToFile(csvContent, "text/csv;charset=utf-8", "user_list.csv");
|
||||
}
|
||||
|
||||
watch(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export default {
|
||||
Description: "Description",
|
||||
Finish: "Finish",
|
||||
};
|
||||
|
|
|
@ -10,6 +10,8 @@ export default {
|
|||
Updated: "Updated",
|
||||
HeterogeneousSearch: "Heterogeneous Search",
|
||||
TurnOffHeterogeneousSearch: "Turn off heterogeneous search",
|
||||
UploadHeterogeneousRequirement: "Upload heterogeneous requirement",
|
||||
StartHeterogeneousSearch: "Start heterogeneous search",
|
||||
RecommendedMultipleLearnware: "Recommended multiple learnware",
|
||||
RecommendedMultipleLearnwareTips:
|
||||
"The learnwares listed below are highly recommended as they have the highest statistical specification similarity to your tasks. Combining these learnwares can lead to great effectiveness.",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export default {
|
||||
Description: "描述",
|
||||
Finish: "完成",
|
||||
};
|
||||
|
|
|
@ -10,6 +10,8 @@ export default {
|
|||
Updated: "更新于",
|
||||
HeterogeneousSearch: "异构查搜",
|
||||
TurnOffHeterogeneousSearch: "关闭异构查搜",
|
||||
UploadHeterogeneousRequirement: "上传异构需求",
|
||||
StartHeterogeneousSearch: "开始异构查搜",
|
||||
RecommendedMultipleLearnware: "推荐多个学件",
|
||||
RecommendedMultipleLearnwareTips:
|
||||
"以下学件被推荐,因为它们与您的任务具有最高的统计规约相似性。结合这些学件可以带来很好的效果。",
|
||||
|
|
|
@ -82,9 +82,9 @@ watch(
|
|||
|
||||
<template>
|
||||
<v-app>
|
||||
<app-bar v-model:drawerOpen="drawerOpen" :routes="routes"></app-bar>
|
||||
<app-bar v-model="drawerOpen" :routes="routes"></app-bar>
|
||||
|
||||
<nav-drawer v-model:drawerOpen="drawerOpen" :routes="routes"></nav-drawer>
|
||||
<nav-drawer v-model="drawerOpen" :routes="routes"></nav-drawer>
|
||||
|
||||
<v-main class="bg-gray-100 bg-opacity-50">
|
||||
<router-view v-slot="{ Component }">
|
||||
|
|
|
@ -7,11 +7,11 @@ import learnwareLogo from "/logo.svg?url";
|
|||
import type { Route } from "@beiming-system/types/route";
|
||||
|
||||
export interface Props {
|
||||
drawerOpen: boolean;
|
||||
modelValue: boolean;
|
||||
routes: Route[];
|
||||
}
|
||||
|
||||
const emit = defineEmits(["update:drawerOpen"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
@ -54,7 +54,7 @@ const filteredRoutes = computed<Route[]>(
|
|||
<div class="prepend">
|
||||
<v-app-bar-nav-icon
|
||||
v-if="['xs', 'sm'].includes(display.name.value)"
|
||||
@click="() => emit('update:drawerOpen', !drawerOpen)"
|
||||
@click="() => emit('update:modelValue', !modelValue)"
|
||||
></v-app-bar-nav-icon>
|
||||
<div class="logo" @click="() => router.push('/')">
|
||||
<img class="logo-img" :src="learnwareLogo" />
|
||||
|
|
|
@ -5,19 +5,19 @@ import { useDisplay } from "vuetify";
|
|||
import type { Route } from "@beiming-system/types/route";
|
||||
|
||||
export interface Props {
|
||||
drawerOpen: boolean;
|
||||
modelValue: boolean;
|
||||
routes: Route[];
|
||||
}
|
||||
|
||||
const display = useDisplay();
|
||||
|
||||
const emit = defineEmits(["update:drawerOpen"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const drawer = computed({
|
||||
get: () => props.drawerOpen && ["xs", "sm"].includes(display.name.value),
|
||||
set: (value) => emit("update:drawerOpen", value),
|
||||
get: () => props.modelValue && ["xs", "sm"].includes(display.name.value),
|
||||
set: (value) => emit("update:modelValue", value),
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
|
|
|
@ -7,11 +7,14 @@ import TaskTypeBtns from "../Specification/SpecTag/TaskType.vue";
|
|||
import LibraryTypeBtns from "../Specification/SpecTag/LibraryType.vue";
|
||||
import FileUpload from "../Specification/FileUpload.vue";
|
||||
import ScenarioListBtns from "../Specification/SpecTag/ScenarioList.vue";
|
||||
import DescriptionInput from "../Specification/DescriptionInput.vue";
|
||||
import { saveContentToFile } from "../../utils";
|
||||
import type { DataType, TaskType, LibraryType, Filter } from "@beiming-system/types/learnware";
|
||||
|
||||
export interface Props {
|
||||
modelValue: Filter;
|
||||
isAdmin?: boolean;
|
||||
isHeterogeneous?: boolean;
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -20,6 +23,7 @@ const emits = defineEmits(["update:modelValue"]);
|
|||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
isAdmin: false,
|
||||
isHeterogeneous: false,
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
@ -41,6 +45,27 @@ const scenarioList = ref(tryScenarioList);
|
|||
|
||||
const files = ref([]);
|
||||
|
||||
const heterDialog = ref(false);
|
||||
const heterTab = ref<"dataType" | "taskType">("dataType");
|
||||
const dataTypeDescription = ref({
|
||||
Dimension: 7,
|
||||
Description: {
|
||||
0: "gender",
|
||||
1: "age",
|
||||
2: "the description of feature 2",
|
||||
5: "the description of feature 5",
|
||||
},
|
||||
});
|
||||
const taskTypeDescription = ref({
|
||||
Dimension: 2,
|
||||
Description: {
|
||||
0: "the description of label 0",
|
||||
1: "the description of label 1",
|
||||
},
|
||||
});
|
||||
const tempDataTypeDescription = ref(dataTypeDescription.value);
|
||||
const tempTaskTypeDescription = ref(taskTypeDescription.value);
|
||||
|
||||
const requirement = computed(() => ({
|
||||
id: id.value,
|
||||
name: search.value,
|
||||
|
@ -49,8 +74,20 @@ const requirement = computed(() => ({
|
|||
libraryType: libraryType.value,
|
||||
scenarioList: scenarioList.value,
|
||||
files: files.value,
|
||||
dataTypeDescription: dataTypeDescription.value,
|
||||
taskTypeDescription: taskTypeDescription.value,
|
||||
}));
|
||||
|
||||
watch(
|
||||
() => heterDialog.value,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
dataTypeDescription.value = tempDataTypeDescription.value;
|
||||
taskTypeDescription.value = tempTaskTypeDescription.value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => requirement.value,
|
||||
() => {
|
||||
|
@ -94,17 +131,17 @@ watch(
|
|||
></v-text-field>
|
||||
</div>
|
||||
|
||||
<data-type-btns v-model:value="dataType" :cols="3" :md="2" :sm="2" :xs="2"></data-type-btns>
|
||||
<task-type-btns v-model:value="taskType" :cols="2" :md="2" :sm="2" :xs="2"></task-type-btns>
|
||||
<data-type-btns v-model="dataType" :cols="3" :md="2" :sm="2" :xs="2"></data-type-btns>
|
||||
<task-type-btns v-model="taskType" :cols="2" :md="2" :sm="2" :xs="2"></task-type-btns>
|
||||
<library-type-btns
|
||||
v-model:value="libraryType"
|
||||
v-model="libraryType"
|
||||
:cols="2"
|
||||
:md="2"
|
||||
:sm="2"
|
||||
:xs="2"
|
||||
></library-type-btns>
|
||||
<scenario-list-btns
|
||||
v-model:value="scenarioList"
|
||||
v-model="scenarioList"
|
||||
:cols="2"
|
||||
:md="2"
|
||||
:sm="2"
|
||||
|
@ -115,13 +152,103 @@ watch(
|
|||
</div>
|
||||
|
||||
<div class="p-4 pt-0 border-t-1 border-gray-300">
|
||||
<div ref="anchorRef" class="mt-3 mb-5 w-full text-h6 transition-all truncate">
|
||||
<template v-if="isHeterogeneous">
|
||||
<div class="mt-3 mb-5 w-full text-h6 transition-all truncate">
|
||||
<v-icon class="mr-3" icon="mdi-vector-difference" color="black" size="small"></v-icon>
|
||||
{{ t("Search.UploadHeterogeneousRequirement") }}
|
||||
</div>
|
||||
|
||||
<v-dialog v-model="heterDialog" width="1024">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-card v-bind="props" flat class="border-gray-500 border-2 rounded-lg bg-transparent">
|
||||
<v-card-text class="text-center text-base md:text-xl">
|
||||
{{ t("Search.StartHeterogeneousSearch") }}
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<v-card class="p-4 md:p-8 md:pt-4">
|
||||
<div>
|
||||
<v-tabs v-model="heterTab" align-tabs="center">
|
||||
<v-tab value="dataType">{{
|
||||
t("Submit.SemanticSpecification.DataType.DescriptionInput.Name")
|
||||
}}</v-tab>
|
||||
<v-tab value="taskType">{{
|
||||
t("Submit.SemanticSpecification.TaskType.DescriptionOutput.Name")
|
||||
}}</v-tab>
|
||||
</v-tabs>
|
||||
</div>
|
||||
|
||||
<v-window v-model="heterTab">
|
||||
<v-window-item value="dataType">
|
||||
<div class="flex justify-between">
|
||||
<div class="text-h4 font-semibold">
|
||||
{{ t("Submit.SemanticSpecification.DataType.DescriptionInput.Name") }}
|
||||
</div>
|
||||
<v-btn
|
||||
icon="mdi-download"
|
||||
variant="flat"
|
||||
@click="
|
||||
() =>
|
||||
saveContentToFile(
|
||||
JSON.stringify(tempDataTypeDescription, undefined, 2),
|
||||
'text/json',
|
||||
'dataTypeDescription.json',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<description-input
|
||||
v-model="tempDataTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.DataType.DescriptionInput.Name')"
|
||||
class="mt-4"
|
||||
>
|
||||
</description-input>
|
||||
</v-window-item>
|
||||
|
||||
<v-window-item value="taskType">
|
||||
<div class="flex justify-between">
|
||||
<div class="mt-4 text-h4 font-semibold">
|
||||
{{ t("Submit.SemanticSpecification.TaskType.DescriptionOutput.Name") }}
|
||||
</div>
|
||||
<v-btn
|
||||
icon="mdi-download"
|
||||
variant="flat"
|
||||
@click="
|
||||
() =>
|
||||
saveContentToFile(
|
||||
JSON.stringify(tempTaskTypeDescription, undefined, 2),
|
||||
'text/json',
|
||||
'dataTypeDescription.json',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<description-input
|
||||
v-model="tempTaskTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.TaskType.DescriptionOutput.Name')"
|
||||
class="mt-4"
|
||||
>
|
||||
</description-input>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<v-btn color="primary" rounded variant="flat" @click="heterDialog = false">
|
||||
{{ t("Public.Finish") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<div class="mt-3 mb-5 w-full text-h6 transition-all truncate">
|
||||
<v-icon class="mr-3" icon="mdi-upload" color="black" size="small"></v-icon>
|
||||
{{ t("Search.UploadStatisticalRequirement") }}
|
||||
</div>
|
||||
|
||||
<file-upload
|
||||
v-model:files="files"
|
||||
v-model="files"
|
||||
:height="28"
|
||||
:tips="t('Submit.File.DragFileHere', { file: 'json' })"
|
||||
/>
|
||||
|
@ -131,7 +258,7 @@ watch(
|
|||
|
||||
<style scoped lang="scss">
|
||||
.filter {
|
||||
@apply p-2 w-full md:h-full md:overflow-y-scroll sm:px-5;
|
||||
@apply p-2 w-full md:h-full md:overflow-y-auto sm:px-5;
|
||||
|
||||
* {
|
||||
@apply mt-2;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDisplay } from "vuetify";
|
||||
import { debounce } from "../../utils";
|
||||
|
||||
export interface Props {
|
||||
name: string;
|
||||
value: {
|
||||
modelValue: {
|
||||
Dimension: number;
|
||||
Description: Record<number, string>;
|
||||
};
|
||||
|
@ -14,29 +13,19 @@ export interface Props {
|
|||
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
const display = useDisplay();
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
|
||||
const emits = defineEmits(["update:value"]);
|
||||
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "feature",
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const errorMessages = ref<string>("");
|
||||
|
||||
const descriptionJSON = ref(props.value);
|
||||
const descriptionJSON = ref(props.modelValue);
|
||||
const descriptionArray = ref<(string | null)[]>(
|
||||
[...new Array(props.value.Dimension)].map((_, idx) => props.value?.Description[idx] || null),
|
||||
[...new Array(props.modelValue.Dimension)].map(
|
||||
(_, idx) => props.modelValue?.Description[idx] || null,
|
||||
),
|
||||
);
|
||||
const descriptionString = ref(JSON.stringify(props.value, null, 2));
|
||||
const descriptionString = ref(JSON.stringify(props.modelValue, null, 2));
|
||||
|
||||
const debouncedSetErrorMessages = debounce<string>((val) => {
|
||||
errorMessages.value = val;
|
||||
|
@ -50,7 +39,7 @@ watch(
|
|||
(_, idx) => newVal.Description[idx] || null,
|
||||
);
|
||||
descriptionString.value = JSON.stringify(newVal, null, 2);
|
||||
emits("update:value", newVal);
|
||||
emits("update:modelValue", newVal);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
|
@ -101,66 +90,85 @@ watch(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-3">
|
||||
<v-alert type="info" color="primary" closable>
|
||||
<slot v-if="['sm', 'xs'].includes(display.name.value)" name="msg-small" />
|
||||
<slot v-else name="msg" />
|
||||
</v-alert>
|
||||
<v-container class="mt-3">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" class="flex flex-col max-h-[600px]">
|
||||
<v-virtual-scroll :items="descriptionArray">
|
||||
<template #default="{ index: idx }">
|
||||
<v-hover>
|
||||
<template #default="{ isHovering, props: hoverProps }">
|
||||
<div v-bind="hoverProps">
|
||||
<v-text-field
|
||||
v-model="descriptionArray[idx]"
|
||||
:label="`${t('Public.Description')}: ${name} ${idx}`"
|
||||
class="mb-1"
|
||||
hide-details
|
||||
>
|
||||
<template v-if="isHovering" #append-inner>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-plus"
|
||||
@click="() => descriptionArray.splice(idx, 0, null)"
|
||||
/>
|
||||
<v-icon
|
||||
icon="mdi-delete"
|
||||
@click="
|
||||
() => (descriptionArray = descriptionArray.filter((_, i) => i !== idx))
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
</v-hover>
|
||||
<div class="grid grid-cols-1 gap-0 md:grid-cols-2 md:gap-3">
|
||||
<div class="flex flex-col max-h-[600px]">
|
||||
<v-virtual-scroll v-if="modelValue.Dimension > 7" :items="descriptionArray" class="flex-1">
|
||||
<template #default="{ index: idx }">
|
||||
<v-hover>
|
||||
<template #default="{ isHovering, props: hoverProps }">
|
||||
<div v-bind="hoverProps">
|
||||
<v-text-field
|
||||
v-model="descriptionArray[idx]"
|
||||
:label="`${t('Public.Description')}: ${name} ${idx}`"
|
||||
class="mb-1"
|
||||
hide-details
|
||||
>
|
||||
<template v-if="isHovering" #append-inner>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-plus"
|
||||
@click="() => descriptionArray.splice(idx, 0, null)"
|
||||
/>
|
||||
<v-icon
|
||||
icon="mdi-delete"
|
||||
@click="
|
||||
() => (descriptionArray = descriptionArray.filter((_, i) => i !== idx))
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
<div>
|
||||
<v-btn
|
||||
block
|
||||
variant="flat"
|
||||
class="mt-1"
|
||||
@click="descriptionArray = [...descriptionArray, null]"
|
||||
>
|
||||
<v-icon size="large" color="#555">mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" class="max-h-[600px]">
|
||||
<div class="h-full overflow-y-scroll">
|
||||
<v-textarea
|
||||
v-model="descriptionString"
|
||||
auto-grow
|
||||
:label="`${name}${locale != 'zh-cn' ? ' ' : ''}${t('Public.Description')}`"
|
||||
:error-messages="errorMessages"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-hover>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
<template v-else>
|
||||
<v-hover v-for="(_description, idx) in descriptionArray" :key="idx">
|
||||
<template #default="{ isHovering, props: hoverProps }">
|
||||
<div v-bind="hoverProps">
|
||||
<v-text-field
|
||||
v-model="descriptionArray[idx]"
|
||||
:label="`${t('Public.Description')}: ${name} ${idx}`"
|
||||
class="mb-1"
|
||||
hide-details
|
||||
>
|
||||
<template v-if="isHovering" #append-inner>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-plus"
|
||||
@click="() => descriptionArray.splice(idx, 0, null)"
|
||||
/>
|
||||
<v-icon
|
||||
icon="mdi-delete"
|
||||
@click="() => (descriptionArray = descriptionArray.filter((_, i) => i !== idx))"
|
||||
/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
</v-hover>
|
||||
</template>
|
||||
<div>
|
||||
<v-btn
|
||||
block
|
||||
variant="flat"
|
||||
class="mt-1"
|
||||
@click="descriptionArray = [...descriptionArray, null]"
|
||||
>
|
||||
<v-icon size="large" color="#555">mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-h-[600px]">
|
||||
<div class="h-full overflow-y-auto">
|
||||
<v-textarea
|
||||
v-model="descriptionString"
|
||||
auto-grow
|
||||
:label="`${name}${locale != 'zh-cn' ? ' ' : ''}${t('Public.Description')}`"
|
||||
:error-messages="errorMessages"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,16 +2,15 @@
|
|||
import { ref, computed } from "vue";
|
||||
|
||||
export interface Props {
|
||||
files: File[];
|
||||
modelValue: File[];
|
||||
tips: string;
|
||||
errorMessages?: string;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const emit = defineEmits(["update:files"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
files: () => [],
|
||||
errorMessages: "",
|
||||
height: 40,
|
||||
});
|
||||
|
@ -43,9 +42,9 @@ const computeFileSize = (byte: number): string => {
|
|||
};
|
||||
|
||||
const files = computed({
|
||||
get: () => props.files,
|
||||
get: () => props.modelValue,
|
||||
set: (val) => {
|
||||
emit("update:files", val);
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -71,37 +71,49 @@ const taskTypeDescription = computed({
|
|||
<v-alert class="w-full max-w-[900px] mx-auto" closable :text="errorMessages" type="error" />
|
||||
</v-card-actions>
|
||||
</v-scroll-y-transition>
|
||||
<data-type-btns v-model:value="dataType" />
|
||||
<description-input
|
||||
v-if="dataType === 'Table'"
|
||||
v-model:value="dataTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.DataType.DescriptionInput.Name')"
|
||||
>
|
||||
<template #msg>
|
||||
{{ t("Submit.SemanticSpecification.DataType.DescriptionInput.FeatureTips") }}
|
||||
</template>
|
||||
<template #msg-small>
|
||||
{{ t("Submit.SemanticSpecification.DataType.DescriptionInput.FeatureTipsSmall") }}
|
||||
</template>
|
||||
</description-input>
|
||||
<task-type-btns v-model:value="taskType" />
|
||||
<description-input
|
||||
<data-type-btns v-model="dataType" />
|
||||
|
||||
<template v-if="dataType === 'Table'">
|
||||
<v-alert class="mt-3" type="info" color="primary" closable>
|
||||
<span class="hidden sm:inline">{{
|
||||
t("Submit.SemanticSpecification.DataType.DescriptionInput.FeatureTips")
|
||||
}}</span>
|
||||
<span class="sm:hidden">{{
|
||||
t("Submit.SemanticSpecification.DataType.DescriptionInput.FeatureTipsSmall")
|
||||
}}</span>
|
||||
</v-alert>
|
||||
<description-input
|
||||
v-model="dataTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.DataType.DescriptionInput.Name')"
|
||||
class="mt-3"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<task-type-btns v-model="taskType" />
|
||||
|
||||
<template
|
||||
v-if="
|
||||
taskType === 'Classification' ||
|
||||
taskType === 'Regression' ||
|
||||
taskType === 'Feature Extraction'
|
||||
"
|
||||
v-model:value="taskTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.TaskType.DescriptionOutput.Name')"
|
||||
>
|
||||
<template #msg>
|
||||
{{ t("Submit.SemanticSpecification.TaskType.DescriptionOutput.LabelTips") }}
|
||||
</template>
|
||||
<template #msg-small>
|
||||
{{ t("Submit.SemanticSpecification.TaskType.DescriptionOutput.LabelTipsSmall") }}
|
||||
</template>
|
||||
</description-input>
|
||||
<library-type-btns v-model:value="libraryType" />
|
||||
<scenario-list-btns v-model:value="scenarioList" />
|
||||
<v-alert class="mt-3" type="info" color="primary" closable>
|
||||
<span class="hidden sm:inline">{{
|
||||
t("Submit.SemanticSpecification.TaskType.DescriptionOutput.LabelTips")
|
||||
}}</span>
|
||||
<span class="sm:hidden">{{
|
||||
t("Submit.SemanticSpecification.TaskType.DescriptionOutput.LabelTipsSmall")
|
||||
}}</span>
|
||||
</v-alert>
|
||||
<description-input
|
||||
v-model="taskTypeDescription"
|
||||
:name="t('Submit.SemanticSpecification.TaskType.DescriptionOutput.Name')"
|
||||
class="mt-3"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<library-type-btns v-model="libraryType" />
|
||||
<scenario-list-btns v-model="scenarioList" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { computed } from "vue";
|
||||
import GridBtns from "./GridBtns.vue";
|
||||
import AudioBtn from "../../../assets/images/specification/dataType/audio.svg?component";
|
||||
import VideoBtn from "../../../assets/images/specification/dataType/video.svg?component";
|
||||
|
@ -8,39 +8,33 @@ import ImageBtn from "../../../assets/images/specification/dataType/image.svg?co
|
|||
import TableBtn from "../../../assets/images/specification/dataType/table.svg?component";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
export interface DataTypeProps {
|
||||
modelValue: string;
|
||||
cols?: number;
|
||||
md?: number;
|
||||
sm?: number;
|
||||
xs?: number;
|
||||
}
|
||||
|
||||
const emit = defineEmits(["update:value"]);
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cols: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
md: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
sm: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
xs: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
const props = withDefaults(defineProps<DataTypeProps>(), {
|
||||
cols: 5,
|
||||
md: 4,
|
||||
sm: 4,
|
||||
xs: 2,
|
||||
});
|
||||
|
||||
const value = ref(props.value);
|
||||
const { t } = useI18n();
|
||||
|
||||
watch(
|
||||
() => value.value,
|
||||
(newVal) => emit("update:value", newVal),
|
||||
);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const modelValue = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
const dataTypeBtns = computed(() => [
|
||||
{
|
||||
|
@ -73,7 +67,7 @@ const dataTypeBtns = computed(() => [
|
|||
|
||||
<template>
|
||||
<grid-btns
|
||||
v-model:value="value"
|
||||
v-model="modelValue"
|
||||
:btns="dataTypeBtns"
|
||||
:title="t('Submit.SemanticSpecification.DataType.DataType')"
|
||||
:cols="cols"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { useDisplay } from "vuetify";
|
||||
import IconBtn from "./IconBtn.vue";
|
||||
import type { FunctionalComponent, SVGAttributes } from "vue";
|
||||
|
@ -11,7 +11,7 @@ export interface Btn {
|
|||
}
|
||||
|
||||
export interface Props {
|
||||
value: string;
|
||||
modelValue: string;
|
||||
btns: Btn[];
|
||||
title: string;
|
||||
cols?: number;
|
||||
|
@ -29,9 +29,16 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
xs: 3,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:value"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const value = ref(props.value);
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
const realCols = computed(() => {
|
||||
let { cols } = props;
|
||||
|
@ -48,13 +55,6 @@ const realCols = computed(() => {
|
|||
function clickBtn(btn: Btn): void {
|
||||
value.value = btn.value === value.value ? "" : btn.value;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => value.value,
|
||||
(newValue) => {
|
||||
emit("update:value", newValue);
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { computed } from "vue";
|
||||
import GridBtns from "./GridBtns.vue";
|
||||
import TensorFlowBtn from "../../../assets/images/specification/libraryType/tensorflow.svg?component";
|
||||
import PyTorchBtn from "../../../assets/images/specification/libraryType/pytorch.svg?component";
|
||||
|
@ -7,40 +7,33 @@ import ScikitLearnBtn from "../../../assets/images/specification/libraryType/sci
|
|||
import OthersBtn from "../../../assets/images/specification/libraryType/others.svg?component";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
export interface LibraryTypeProps {
|
||||
modelValue: string;
|
||||
cols?: number;
|
||||
md?: number;
|
||||
sm?: number;
|
||||
xs?: number;
|
||||
}
|
||||
|
||||
const emit = defineEmits(["update:value"]);
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cols: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
md: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
sm: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
xs: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
const props = withDefaults(defineProps<LibraryTypeProps>(), {
|
||||
cols: 4,
|
||||
md: 4,
|
||||
sm: 4,
|
||||
xs: 2,
|
||||
});
|
||||
|
||||
const value = ref(props.value);
|
||||
const { t } = useI18n();
|
||||
|
||||
watch(
|
||||
() => value.value,
|
||||
(newVal) => emit("update:value", newVal),
|
||||
{ deep: true },
|
||||
);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const modelValue = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
const libraryTypeBtns = computed(() => [
|
||||
{
|
||||
|
@ -68,7 +61,7 @@ const libraryTypeBtns = computed(() => [
|
|||
|
||||
<template>
|
||||
<grid-btns
|
||||
v-model:value="value"
|
||||
v-model="modelValue"
|
||||
:btns="libraryTypeBtns"
|
||||
:title="t('Submit.SemanticSpecification.LibraryType.LibraryType')"
|
||||
:cols="cols"
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useI18n } from "vue-i18n";
|
|||
import type { Scenario, ScenarioList } from "@beiming-system/types/learnware";
|
||||
|
||||
export interface Props {
|
||||
value: ScenarioList;
|
||||
modelValue: ScenarioList;
|
||||
cols?: number;
|
||||
md?: number;
|
||||
sm?: number;
|
||||
|
@ -14,7 +14,7 @@ export interface Props {
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const emit = defineEmits(["update:value"]);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const display = useDisplay();
|
||||
|
||||
|
@ -116,17 +116,19 @@ const items = computed<
|
|||
},
|
||||
]);
|
||||
|
||||
const allSelected = computed(() => props.value && props.value.length === items.value.length);
|
||||
const allSelected = computed(
|
||||
() => props.modelValue && props.modelValue.length === items.value.length,
|
||||
);
|
||||
|
||||
const selections = computed(() => {
|
||||
if (props.value) {
|
||||
return props.value.map((s) => items.value.find((item) => item.value === s));
|
||||
if (props.modelValue) {
|
||||
return props.modelValue.map((s) => items.value.find((item) => item.value === s));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
function click(value: Scenario): void {
|
||||
if (props.value && props.value.includes(value)) {
|
||||
if (props.modelValue && props.modelValue.includes(value)) {
|
||||
deleteSelect(value);
|
||||
} else {
|
||||
addSelect(value);
|
||||
|
@ -134,21 +136,21 @@ function click(value: Scenario): void {
|
|||
}
|
||||
|
||||
function addSelect(value: Scenario): void {
|
||||
if (props.value) {
|
||||
emit("update:value", [...props.value, value]);
|
||||
if (props.modelValue) {
|
||||
emit("update:modelValue", [...props.modelValue, value]);
|
||||
} else {
|
||||
emit("update:value", [value]);
|
||||
emit("update:modelValue", [value]);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSelect(value: Scenario): void {
|
||||
if (props.value) {
|
||||
if (props.modelValue) {
|
||||
emit(
|
||||
"update:value",
|
||||
props.value.filter((s) => s !== value),
|
||||
"update:modelValue",
|
||||
props.modelValue.filter((s) => s !== value),
|
||||
);
|
||||
} else {
|
||||
emit("update:value", []);
|
||||
emit("update:modelValue", []);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { computed } from "vue";
|
||||
import GridBtns from "./GridBtns.vue";
|
||||
import ClassificationBtn from "../../../assets/images/specification/taskType/classification.svg?component";
|
||||
import ClusteringBtn from "../../../assets/images/specification/taskType/clustering.svg?component";
|
||||
|
@ -11,39 +11,33 @@ import RankingBtn from "../../../assets/images/specification/taskType/ranking.sv
|
|||
import OthersBtn from "../../../assets/images/specification/taskType/others.svg?component";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
export interface TaskTypeProps {
|
||||
modelValue: string;
|
||||
cols?: number;
|
||||
md?: number;
|
||||
sm?: number;
|
||||
xs?: number;
|
||||
}
|
||||
|
||||
const emit = defineEmits(["update:value"]);
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cols: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
md: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
sm: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
xs: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
const props = withDefaults(defineProps<TaskTypeProps>(), {
|
||||
cols: 5,
|
||||
md: 4,
|
||||
sm: 4,
|
||||
xs: 2,
|
||||
});
|
||||
|
||||
const value = ref(props.value);
|
||||
const { t } = useI18n();
|
||||
|
||||
watch(
|
||||
() => value.value,
|
||||
(newVal) => emit("update:value", newVal),
|
||||
);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const modelValue = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
const taskTypeBtns = computed(() => [
|
||||
{
|
||||
|
@ -91,7 +85,7 @@ const taskTypeBtns = computed(() => [
|
|||
|
||||
<template>
|
||||
<grid-btns
|
||||
v-model:value="value"
|
||||
v-model="modelValue"
|
||||
:btns="taskTypeBtns"
|
||||
:title="t('Submit.SemanticSpecification.TaskType.TaskType')"
|
||||
:cols="cols"
|
||||
|
|
|
@ -44,7 +44,7 @@ function searchLearnware({
|
|||
scenarioList,
|
||||
files,
|
||||
isVerified,
|
||||
heterogeneousMode,
|
||||
isHeterogeneous,
|
||||
page,
|
||||
limit,
|
||||
}: {
|
||||
|
@ -56,7 +56,7 @@ function searchLearnware({
|
|||
scenarioList: ScenarioList;
|
||||
files: Files;
|
||||
isVerified: boolean;
|
||||
heterogeneousMode: boolean;
|
||||
isHeterogeneous: boolean;
|
||||
page: number;
|
||||
limit: number;
|
||||
}): Promise<{
|
||||
|
@ -85,7 +85,7 @@ function searchLearnware({
|
|||
}
|
||||
fd.append("semantic_specification", JSON.stringify(semanticSpec));
|
||||
fd.append("statistical_specification", (files.length > 0 && files[0]) || "");
|
||||
fd.append("heterogeneous_mode", String(heterogeneousMode));
|
||||
fd.append("is_heterogeneous", String(isHeterogeneous));
|
||||
fd.append("is_verified", String(isVerified));
|
||||
fd.append("limit", String(limit));
|
||||
fd.append("page", String(page));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export { downloadLearnwareSync } from "./download.js";
|
||||
export { hex_md5 } from "./encrypt.js";
|
||||
export { verifyLearnware } from "./verifyLearnware.js";
|
||||
export { promiseReadFile } from "./promiseReadFile.js";
|
||||
export { debounce, throttle } from "./control.js";
|
||||
export { downloadLearnwareSync } from "./download";
|
||||
export { hex_md5 } from "./encrypt";
|
||||
export { verifyLearnware } from "./verifyLearnware";
|
||||
export { promiseReadFile } from "./promiseReadFile";
|
||||
export { debounce, throttle } from "./control";
|
||||
export { default as saveContentToFile } from "./saveContentToFile";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default function saveContentToFile(content: BlobPart, fileName: string): void {
|
||||
const blob = new Blob([content], { type: "text/csv;charset=utf-8" });
|
||||
export default function saveContentToFile(content: BlobPart, type: string, fileName: string): void {
|
||||
const blob = new Blob([content], { type });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
document.body.appendChild(link);
|
|
@ -43,7 +43,7 @@ const singleRecommendedLearnwarePage = ref(1);
|
|||
const singleRecommendedLearnwarePageNum = ref(1);
|
||||
const singleRecommendedLearnwarePageSize = ref(Math.ceil(display.height.value / 900) * 10);
|
||||
const singleRecommendedLearnwareItems = ref<LearnwareCardInfo[]>([]);
|
||||
const heterogeneousMode = ref<boolean>(false);
|
||||
const isHeterogeneous = ref<boolean>(false);
|
||||
const rkmeTypeTable = ref<boolean>(false);
|
||||
const loading = ref(false);
|
||||
const isVerified = ref(route.query.is_verified ? route.query.is_verified === "true" : true);
|
||||
|
@ -82,7 +82,7 @@ function fetchByFilterAndPage(
|
|||
filters: Filter,
|
||||
page: number,
|
||||
isVerified: boolean = false,
|
||||
heterogeneousMode: boolean = false,
|
||||
isHeterogeneous: boolean = false,
|
||||
): void {
|
||||
showError.value = false;
|
||||
loading.value = true;
|
||||
|
@ -96,7 +96,7 @@ function fetchByFilterAndPage(
|
|||
scenarioList: filters.scenarioList,
|
||||
files: filters.files,
|
||||
isVerified,
|
||||
heterogeneousMode,
|
||||
isHeterogeneous,
|
||||
page,
|
||||
limit: singleRecommendedLearnwarePageSize.value,
|
||||
})
|
||||
|
@ -233,17 +233,17 @@ watch(
|
|||
filters.value,
|
||||
singleRecommendedLearnwarePage.value,
|
||||
isVerified.value,
|
||||
heterogeneousMode.value,
|
||||
isHeterogeneous.value,
|
||||
],
|
||||
(newVal) => {
|
||||
const [newFilters, newPage, newIsVerified, newHeterogeneousMode] = newVal as [
|
||||
const [newFilters, newPage, newIsVerified, newIsHeterogeneous] = newVal as [
|
||||
Filter,
|
||||
number,
|
||||
boolean,
|
||||
boolean,
|
||||
];
|
||||
|
||||
fetchByFilterAndPage(newFilters, newPage - 1, newIsVerified, newHeterogeneousMode);
|
||||
fetchByFilterAndPage(newFilters, newPage - 1, newIsVerified, newIsHeterogeneous);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
@ -257,7 +257,7 @@ watch(
|
|||
.then((res: ArrayBuffer) => new TextDecoder("ascii").decode(res))
|
||||
.then((res) => JSON.parse(res))
|
||||
.then((res) => {
|
||||
if (res.type === "table") {
|
||||
if (res.type === "RKMETableSpecification") {
|
||||
rkmeTypeTable.value = true;
|
||||
}
|
||||
})
|
||||
|
@ -286,7 +286,7 @@ function init(): void {
|
|||
filters.value,
|
||||
singleRecommendedLearnwarePage.value - 1,
|
||||
isVerified.value,
|
||||
heterogeneousMode.value,
|
||||
isHeterogeneous.value,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -295,16 +295,16 @@ onMounted(() => init());
|
|||
|
||||
<template>
|
||||
<div class="mx-auto w-full lg:flex">
|
||||
<v-scroll-y-transition class="fixed w-full z-index-10">
|
||||
<v-card-actions v-if="showError">
|
||||
<v-scroll-y-transition>
|
||||
<div v-if="showError" class="fixed w-full z-10">
|
||||
<v-alert
|
||||
class="w-full max-w-[900px] mx-auto"
|
||||
class="max-w-[900px] mx-auto z-10"
|
||||
closable
|
||||
:text="errorMsg"
|
||||
type="error"
|
||||
@click:close="showError = false"
|
||||
/>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-scroll-y-transition>
|
||||
|
||||
<div class="w-full lg:max-w-[460px]">
|
||||
|
@ -313,14 +313,15 @@ onMounted(() => init());
|
|||
class="bottom-0 w-full lg:fixed lg:max-w-[460px]"
|
||||
style="top: var(--v-layout-top)"
|
||||
:is-admin="isAdmin"
|
||||
:is-heterogeneous="isHeterogeneous"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-btn
|
||||
v-if="heterogeneousMode"
|
||||
v-if="isHeterogeneous"
|
||||
block
|
||||
variant="outlined"
|
||||
color="red"
|
||||
@click="() => (heterogeneousMode = false)"
|
||||
@click="() => (isHeterogeneous = false)"
|
||||
>
|
||||
{{ t("Search.TurnOffHeterogeneousSearch") }}
|
||||
</v-btn>
|
||||
|
@ -395,13 +396,8 @@ onMounted(() => init());
|
|||
/>
|
||||
</v-card>
|
||||
|
||||
<div v-if="showHeterogeneousSearchSwitch && !heterogeneousMode" class="text-center">
|
||||
<v-btn
|
||||
class="px-8"
|
||||
variant="outlined"
|
||||
color="red"
|
||||
@click="() => (heterogeneousMode = true)"
|
||||
>
|
||||
<div v-if="showHeterogeneousSearchSwitch && !isHeterogeneous" class="text-center">
|
||||
<v-btn class="px-8" variant="outlined" color="red" @click="() => (isHeterogeneous = true)">
|
||||
{{ t("Search.HeterogeneousSearch") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
|
|
|
@ -462,7 +462,7 @@ onActivated(init);
|
|||
<v-window-item :value="3">
|
||||
<div class="p-4 m-auto">
|
||||
<file-upload
|
||||
v-model:files="files.value"
|
||||
v-model="files.value"
|
||||
:error-messages="files.errorMessages"
|
||||
:tips="t('Submit.File.DragFileHere', { file: 'zip' })"
|
||||
class="text-xl"
|
||||
|
|
Loading…
Reference in New Issue