Fix notification deletion bug: save {} instead of [] when list is empty
When all notifications are deleted, the persistentNotifications array
becomes empty ([]). The backend doesn't accept an empty array as a
valid value, requiring an empty object ({}) instead. This fix modifies
both index.vue and UrgentTestDialog.vue to save {} when the
notification list is empty.
Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
This commit is contained in:
co-authored by
Sunwuyuan
parent
65b08653c8
commit
056225b6b3
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<v-app-bar :elevation="2">
|
||||
<template v-slot:prepend>
|
||||
<v-app-bar-nav-icon></v-app-bar-nav-icon>
|
||||
<template #prepend>
|
||||
<v-app-bar-nav-icon />
|
||||
</template>
|
||||
|
||||
<v-app-bar-title>作业</v-app-bar-title>
|
||||
|
||||
@@ -2,48 +2,85 @@
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<span>缓存管理</span>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn :loading="loading" color="error" @click="clearAllCaches">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
color="error"
|
||||
@click="clearAllCaches"
|
||||
>
|
||||
清除所有缓存
|
||||
</v-btn>
|
||||
<v-btn class="ml-2" icon @click="refreshCaches">
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
icon
|
||||
@click="refreshCaches"
|
||||
>
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-alert v-if="!serviceWorkerActive" class="mb-4" type="warning">
|
||||
<v-alert
|
||||
v-if="!serviceWorkerActive"
|
||||
class="mb-4"
|
||||
type="warning"
|
||||
>
|
||||
Service Worker 未激活,缓存管理功能不可用。
|
||||
</v-alert>
|
||||
|
||||
<v-alert v-if="message" :type="messageType" class="mb-4">
|
||||
<v-alert
|
||||
v-if="message"
|
||||
:type="messageType"
|
||||
class="mb-4"
|
||||
>
|
||||
{{ message }}
|
||||
</v-alert>
|
||||
|
||||
<v-expansion-panels v-if="caches.length > 0">
|
||||
<v-expansion-panel v-for="cache in caches" :key="cache.name">
|
||||
<v-expansion-panel
|
||||
v-for="cache in caches"
|
||||
:key="cache.name"
|
||||
>
|
||||
<v-expansion-panel-title>
|
||||
<div class="d-flex align-center">
|
||||
<span>{{ formatCacheName(cache.name) }}</span>
|
||||
<v-chip class="ml-2" size="small">{{ cache.urls.length }} 个文件</v-chip>
|
||||
<v-chip
|
||||
class="ml-2"
|
||||
size="small"
|
||||
>
|
||||
{{ cache.urls.length }} 个文件
|
||||
</v-chip>
|
||||
</div>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<div class="d-flex justify-end mb-2">
|
||||
<v-btn :loading="loading" color="error" size="small" @click="clearCache(cache.name)">
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
color="error"
|
||||
size="small"
|
||||
@click="clearCache(cache.name)"
|
||||
>
|
||||
清除此缓存
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-list lines="two">
|
||||
<v-list-item v-for="(url, index) in cache.urls" :key="index">
|
||||
<v-list-item
|
||||
v-for="(url, index) in cache.urls"
|
||||
:key="index"
|
||||
>
|
||||
<v-list-item-title class="text-truncate">
|
||||
{{ getFileName(url) }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-truncate">
|
||||
{{ url }}
|
||||
</v-list-item-subtitle>
|
||||
<template v-slot:append>
|
||||
<v-btn color="error" icon size="small" @click="clearUrl(cache.name, url)">
|
||||
<template #append>
|
||||
<v-btn
|
||||
color="error"
|
||||
icon
|
||||
size="small"
|
||||
@click="clearUrl(cache.name, url)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -53,9 +90,15 @@
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
|
||||
<v-skeleton-loader v-else-if="loading" type="article"/>
|
||||
<v-skeleton-loader
|
||||
v-else-if="loading"
|
||||
type="article"
|
||||
/>
|
||||
|
||||
<v-alert v-else type="info">
|
||||
<v-alert
|
||||
v-else
|
||||
type="info"
|
||||
>
|
||||
没有找到缓存数据。
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
@click:close="error = ''"
|
||||
>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-alert-circle</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-alert-circle
|
||||
</v-icon>
|
||||
{{ error }}
|
||||
</div>
|
||||
</v-alert>
|
||||
@@ -27,7 +29,9 @@
|
||||
@click:close="success = ''"
|
||||
>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-check-circle</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-check-circle
|
||||
</v-icon>
|
||||
{{ success }}
|
||||
</div>
|
||||
</v-alert>
|
||||
@@ -43,29 +47,49 @@
|
||||
<div class="d-flex align-center">
|
||||
<span class="font-weight-bold">配置验证失败,请检查以下问题:</span>
|
||||
</div>
|
||||
<v-list class="bg-transparent" density="compact">
|
||||
<v-list
|
||||
class="bg-transparent"
|
||||
density="compact"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(error, index) in validationErrors"
|
||||
:key="index"
|
||||
class="px-0 py-0"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="warning" size="small">mdi-circle-small</v-icon>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
color="warning"
|
||||
size="small"
|
||||
>
|
||||
mdi-circle-small
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">{{ error }}</v-list-item-title>
|
||||
<v-list-item-title class="text-body-2">
|
||||
{{ error }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-alert>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<v-card v-if="loading" class="my-4" outlined>
|
||||
<v-card
|
||||
v-if="loading"
|
||||
class="my-4"
|
||||
outlined
|
||||
>
|
||||
<v-card-text>
|
||||
<v-skeleton-loader class="mx-auto" type="article"></v-skeleton-loader>
|
||||
<v-skeleton-loader
|
||||
class="mx-auto"
|
||||
type="article"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 模式切换按钮和操作按钮 -->
|
||||
<div v-if="!loading" class="d-flex justify-space-between align-center mb-4">
|
||||
<div
|
||||
v-if="!loading"
|
||||
class="d-flex justify-space-between align-center mb-4"
|
||||
>
|
||||
<div class="d-flex align-center gap-2">
|
||||
<v-btn
|
||||
:disabled="!isValidConfig"
|
||||
@@ -96,19 +120,31 @@
|
||||
class="text-error"
|
||||
prepend-icon="mdi-delete"
|
||||
@click="confirmDelete"
|
||||
|
||||
>
|
||||
删除配置
|
||||
</v-btn>
|
||||
<v-btn :value="false" prepend-icon="mdi-eye"> 预览</v-btn>
|
||||
<v-btn :value="true" prepend-icon="mdi-pencil"> 编辑</v-btn>
|
||||
<v-btn
|
||||
:value="false"
|
||||
prepend-icon="mdi-eye"
|
||||
>
|
||||
预览
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:value="true"
|
||||
prepend-icon="mdi-pencil"
|
||||
>
|
||||
编辑
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</div>
|
||||
|
||||
<!-- 预览模式 -->
|
||||
<div v-if="!loading && !isEditMode">
|
||||
<div class="mb-8">
|
||||
<div class="text-h3 font-weight-bold" style="line-height: 1.2">
|
||||
<div
|
||||
class="text-h3 font-weight-bold"
|
||||
style="line-height: 1.2"
|
||||
>
|
||||
{{ localConfig.examName || "未设置考试名称" }}
|
||||
</div>
|
||||
<div
|
||||
@@ -117,8 +153,14 @@
|
||||
>
|
||||
{{ localConfig.message || "未设置考试提示" }}
|
||||
</div>
|
||||
<v-chip v-if="localConfig.room" class="px-4 py-2" size="large">
|
||||
<v-icon start>mdi-home</v-icon>
|
||||
<v-chip
|
||||
v-if="localConfig.room"
|
||||
class="px-4 py-2"
|
||||
size="large"
|
||||
>
|
||||
<v-icon start>
|
||||
mdi-home
|
||||
</v-icon>
|
||||
考场:{{ localConfig.room }}
|
||||
</v-chip>
|
||||
</div>
|
||||
@@ -134,20 +176,29 @@
|
||||
lg="4"
|
||||
md="6"
|
||||
>
|
||||
<v-card class="h-100" hover variant="tonal">
|
||||
<v-card
|
||||
class="h-100"
|
||||
hover
|
||||
variant="tonal"
|
||||
>
|
||||
<v-card-title class="bg-primary-lighten-5 pa-4">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-book-open-page-variant</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-book-open-page-variant
|
||||
</v-icon>
|
||||
<span class="">{{ examInfo.name || "未设置科目" }}</span>
|
||||
</div>
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex align-center mb-1">
|
||||
<v-icon class="mr-2" color="success" size="small"
|
||||
>mdi-clock-start
|
||||
</v-icon
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="success"
|
||||
size="small"
|
||||
>
|
||||
mdi-clock-start
|
||||
</v-icon>
|
||||
<span class="text-body-2 text-grey-darken-1">开始时间</span>
|
||||
</div>
|
||||
<div class="text-h6 font-weight-medium text-success">
|
||||
@@ -156,10 +207,13 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-center mb-1">
|
||||
<v-icon class="mr-2" color="error" size="small"
|
||||
>mdi-clock-end
|
||||
</v-icon
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
mdi-clock-end
|
||||
</v-icon>
|
||||
<span class="text-body-2 text-grey-darken-1">结束时间</span>
|
||||
</div>
|
||||
<div class="text-h6 font-weight-medium text-error">
|
||||
@@ -171,29 +225,50 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
<div v-else class="text-center py-12">
|
||||
<v-icon class="mb-4" color="grey-lighten-2" size="80">
|
||||
<div
|
||||
v-else
|
||||
class="text-center py-12"
|
||||
>
|
||||
<v-icon
|
||||
class="mb-4"
|
||||
color="grey-lighten-2"
|
||||
size="80"
|
||||
>
|
||||
mdi-calendar-blank
|
||||
</v-icon>
|
||||
<div class="text-h5 text-grey-darken-1 mb-2">暂无考试科目安排</div>
|
||||
<div class="text-h5 text-grey-darken-1 mb-2">
|
||||
暂无考试科目安排
|
||||
</div>
|
||||
<div class="text-body-1 text-grey mb-4">
|
||||
点击上方"添加科目"按钮开始配置考试时间表
|
||||
</div>
|
||||
<v-btn color="primary" variant="outlined" @click="quickEdit">
|
||||
<v-icon start>mdi-plus</v-icon>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
@click="quickEdit"
|
||||
>
|
||||
<v-icon start>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
立即添加
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- JSON预览 -->
|
||||
<v-card border class="mb-4" elevation="2">
|
||||
<v-card
|
||||
border
|
||||
class="mb-4"
|
||||
elevation="2"
|
||||
>
|
||||
<v-card-title
|
||||
class="d-flex align-center text-white cursor-pointer"
|
||||
@click="showJsonPreview = !showJsonPreview"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-code-json</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-code-json
|
||||
</v-icon>
|
||||
JSON配置预览
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="white"
|
||||
prepend-icon="mdi-content-copy"
|
||||
@@ -209,12 +284,17 @@
|
||||
color="white"
|
||||
size="small"
|
||||
variant="text"
|
||||
>
|
||||
</v-btn>
|
||||
/>
|
||||
</v-card-title>
|
||||
<v-expand-transition>
|
||||
<v-card-text v-show="showJsonPreview" class="pa-4">
|
||||
<v-card class="pa-4" variant="tonal">
|
||||
<v-card-text
|
||||
v-show="showJsonPreview"
|
||||
class="pa-4"
|
||||
>
|
||||
<v-card
|
||||
class="pa-4"
|
||||
variant="tonal"
|
||||
>
|
||||
<pre class="json-preview"><code>{{ formattedJson }}</code></pre>
|
||||
</v-card>
|
||||
</v-card-text>
|
||||
@@ -225,14 +305,23 @@
|
||||
<!-- 编辑模式 -->
|
||||
<div v-if="!loading && isEditMode">
|
||||
<!-- 基本信息 -->
|
||||
<v-card border class="mb-4" elevation="1">
|
||||
<v-card
|
||||
border
|
||||
class="mb-4"
|
||||
elevation="1"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-information</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-information
|
||||
</v-icon>
|
||||
基本信息
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="localConfig.examName"
|
||||
:rules="[(v) => !!v || '考试名称不能为空']"
|
||||
@@ -240,15 +329,18 @@
|
||||
prepend-inner-icon="mdi-calendar-text"
|
||||
required
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="localConfig.room"
|
||||
label="考场号(标准ES尚不支持此配置)"
|
||||
prepend-inner-icon="mdi-home"
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-textarea
|
||||
@@ -258,7 +350,7 @@
|
||||
prepend-inner-icon="mdi-message-text"
|
||||
rows="3"
|
||||
variant="outlined"
|
||||
></v-textarea>
|
||||
/>
|
||||
|
||||
<!-- 默认提示选项 -->
|
||||
<v-chip-group
|
||||
@@ -275,23 +367,39 @@
|
||||
variant="outlined"
|
||||
@click="selectDefaultTip(tip)"
|
||||
>
|
||||
<v-icon size="small" start>mdi-plus</v-icon>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
{{ tip }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
<div class="text-caption text-medium-emphasis ml-2">
|
||||
<v-icon class="mr-1" size="small">mdi-lightbulb-outline</v-icon>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
size="small"
|
||||
>
|
||||
mdi-lightbulb-outline
|
||||
</v-icon>
|
||||
点击上方选项快速添加常用考试提示
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 考试科目安排 -->
|
||||
<v-card border class="mb-4" elevation="1">
|
||||
<v-card
|
||||
border
|
||||
class="mb-4"
|
||||
elevation="1"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-format-list-bulleted</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-format-list-bulleted
|
||||
</v-icon>
|
||||
考试科目安排
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-plus"
|
||||
@@ -312,7 +420,10 @@
|
||||
>
|
||||
<div class="w-100">
|
||||
<v-row>
|
||||
<v-col cols="12" md="4">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="examInfo.name"
|
||||
:rules="[(v) => !!v || '科目名称不能为空']"
|
||||
@@ -320,9 +431,12 @@
|
||||
label="科目名称"
|
||||
prepend-inner-icon="mdi-book"
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="3"
|
||||
>
|
||||
<v-menu
|
||||
v-model="examInfo.startDateMenu"
|
||||
:close-on-content-click="false"
|
||||
@@ -330,7 +444,7 @@
|
||||
offset-y
|
||||
transition="scale-transition"
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<template #activator="{ props }">
|
||||
<v-text-field
|
||||
v-model="examInfo.startFormatted"
|
||||
:rules="[(v) => !!v || '开始时间不能为空']"
|
||||
@@ -340,7 +454,7 @@
|
||||
readonly
|
||||
v-bind="props"
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
/>
|
||||
</template>
|
||||
<v-card min-width="600">
|
||||
<v-card-title class="text-center py-2">
|
||||
@@ -348,7 +462,10 @@
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-0">
|
||||
<v-row no-gutters>
|
||||
<v-col class="border-e" cols="6">
|
||||
<v-col
|
||||
class="border-e"
|
||||
cols="6"
|
||||
>
|
||||
<v-date-picker
|
||||
v-model="examInfo.startDate"
|
||||
color="primary"
|
||||
@@ -356,7 +473,7 @@
|
||||
locale="zh-cn"
|
||||
show-adjacent-months
|
||||
@update:model-value="updateStartDateTime(index)"
|
||||
></v-date-picker>
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-time-picker
|
||||
@@ -366,12 +483,12 @@
|
||||
format="24hr"
|
||||
scrollable
|
||||
@update:model-value="updateStartDateTime(index)"
|
||||
></v-time-picker>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="grey"
|
||||
variant="text"
|
||||
@@ -390,7 +507,10 @@
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="3"
|
||||
>
|
||||
<v-menu
|
||||
v-model="examInfo.endDateMenu"
|
||||
:close-on-content-click="false"
|
||||
@@ -398,7 +518,7 @@
|
||||
offset-y
|
||||
transition="scale-transition"
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<template #activator="{ props }">
|
||||
<v-text-field
|
||||
v-model="examInfo.endFormatted"
|
||||
:rules="[(v) => !!v || '结束时间不能为空']"
|
||||
@@ -408,7 +528,7 @@
|
||||
readonly
|
||||
v-bind="props"
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
/>
|
||||
</template>
|
||||
<v-card min-width="600">
|
||||
<v-card-title class="text-center py-2">
|
||||
@@ -416,7 +536,10 @@
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-0">
|
||||
<v-row no-gutters>
|
||||
<v-col class="border-e" cols="6">
|
||||
<v-col
|
||||
class="border-e"
|
||||
cols="6"
|
||||
>
|
||||
<v-date-picker
|
||||
v-model="examInfo.endDate"
|
||||
color="primary"
|
||||
@@ -424,7 +547,7 @@
|
||||
locale="zh-cn"
|
||||
show-adjacent-months
|
||||
@update:model-value="updateEndDateTime(index)"
|
||||
></v-date-picker>
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-time-picker
|
||||
@@ -434,12 +557,12 @@
|
||||
format="24hr"
|
||||
scrollable
|
||||
@update:model-value="updateEndDateTime(index)"
|
||||
></v-time-picker>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="grey"
|
||||
variant="text"
|
||||
@@ -458,7 +581,11 @@
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
<v-col class="d-flex align-center" cols="12" md="2">
|
||||
<v-col
|
||||
class="d-flex align-center"
|
||||
cols="12"
|
||||
md="2"
|
||||
>
|
||||
<v-btn
|
||||
color="error"
|
||||
icon="mdi-delete"
|
||||
@@ -493,14 +620,25 @@
|
||||
</div>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<div v-else class="text-center py-8">
|
||||
<v-icon class="mb-2" color="grey-lighten-1" size="48">
|
||||
<div
|
||||
v-else
|
||||
class="text-center py-8"
|
||||
>
|
||||
<v-icon
|
||||
class="mb-2"
|
||||
color="grey-lighten-1"
|
||||
size="48"
|
||||
>
|
||||
mdi-book-plus
|
||||
</v-icon>
|
||||
<p class="text-body-2 text-grey-darken-1 mb-4">
|
||||
暂无考试科目,点击"添加科目"按钮开始添加
|
||||
</p>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="addExamInfo">
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-plus"
|
||||
@click="addExamInfo"
|
||||
>
|
||||
添加科目
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -509,10 +647,18 @@
|
||||
</div>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
<v-dialog v-model="deleteDialog" max-width="400">
|
||||
<v-dialog
|
||||
v-model="deleteDialog"
|
||||
max-width="400"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" color="error">mdi-delete-alert</v-icon>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="error"
|
||||
>
|
||||
mdi-delete-alert
|
||||
</v-icon>
|
||||
确认删除配置
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -520,7 +666,7 @@
|
||||
<br><small class="text-grey">此操作不可撤销,将会删除所有相关数据</small>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="grey"
|
||||
variant="text"
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
elevation="4"
|
||||
rounded="xl"
|
||||
>
|
||||
<v-btn-group class="toolbar-buttons" variant="text">
|
||||
<v-btn-group
|
||||
class="toolbar-buttons"
|
||||
variant="text"
|
||||
>
|
||||
<v-btn
|
||||
v-ripple
|
||||
:title="'查看昨天'"
|
||||
@@ -32,7 +35,10 @@
|
||||
variant="text"
|
||||
@click="$emit('zoom', 'up')"
|
||||
/>
|
||||
<v-menu :close-on-content-click="false" location="top">
|
||||
<v-menu
|
||||
:close-on-content-click="false"
|
||||
location="top"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
v-ripple
|
||||
@@ -43,7 +49,10 @@
|
||||
variant="text"
|
||||
/>
|
||||
</template>
|
||||
<v-card border class="date-picker-card">
|
||||
<v-card
|
||||
border
|
||||
class="date-picker-card"
|
||||
>
|
||||
<v-date-picker
|
||||
:model-value="selectedDate"
|
||||
color="primary"
|
||||
@@ -88,7 +97,9 @@
|
||||
size="large"
|
||||
text="复制作业内容到今天"
|
||||
@click="$emit('copy-to-today')"
|
||||
>复制到今天</v-btn>
|
||||
>
|
||||
复制到今天
|
||||
</v-btn>
|
||||
</v-slide-x-reverse-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,14 +8,28 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon :icon="icons[message?.type] || icons.info" class="mr-2"/>
|
||||
<v-icon
|
||||
:icon="icons[message?.type] || icons.info"
|
||||
class="mr-2"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-subtitle-2 font-weight-medium">{{ message?.title }}</div>
|
||||
<div v-if="message?.content" class="text-body-2">{{ message?.content }}</div>
|
||||
<div class="text-subtitle-2 font-weight-medium">
|
||||
{{ message?.title }}
|
||||
</div>
|
||||
<div
|
||||
v-if="message?.content"
|
||||
class="text-body-2"
|
||||
>
|
||||
{{ message?.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #actions>
|
||||
<v-btn icon="mdi-close" variant="text" @click="snackbar = false"/>
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="snackbar = false"
|
||||
/>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
@@ -11,12 +11,16 @@
|
||||
/>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="text-body-2 font-weight-light mb-n1">Welcome to</div>
|
||||
<div class="text-body-2 font-weight-light mb-n1">
|
||||
Welcome to
|
||||
</div>
|
||||
|
||||
<h1 class="text-h2 font-weight-bold">Vuetify</h1>
|
||||
<h1 class="text-h2 font-weight-bold">
|
||||
Vuetify
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="py-4"/>
|
||||
<div class="py-4" />
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
@@ -29,17 +33,20 @@
|
||||
variant="outlined"
|
||||
>
|
||||
<template #image>
|
||||
<v-img position="top right"/>
|
||||
<v-img position="top right" />
|
||||
</template>
|
||||
|
||||
<template #title>
|
||||
<h2 class="text-h5 font-weight-bold">Get started</h2>
|
||||
<h2 class="text-h5 font-weight-bold">
|
||||
Get started
|
||||
</h2>
|
||||
</template>
|
||||
|
||||
<template #subtitle>
|
||||
<div class="text-subtitle-1">
|
||||
Replace this page by removing
|
||||
<v-kbd>{{ `
|
||||
<v-kbd>
|
||||
{{ `
|
||||
<HelloWorld/>
|
||||
` }}
|
||||
</v-kbd>
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
<v-card-title class="d-flex align-center">
|
||||
{{ title }}
|
||||
<v-spacer />
|
||||
<v-btn icon="mdi-close" variant="text" @click="handleClose" />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</v-card-title>
|
||||
<v-card-subtitle>
|
||||
{{ autoSave ? autoSavePromptText : manualSavePromptText }}
|
||||
@@ -31,13 +35,21 @@
|
||||
/>
|
||||
|
||||
<!-- Template Buttons Section -->
|
||||
<div v-if="templateData" class="mt-4">
|
||||
<div v-if="hasTemplates" class="template-buttons">
|
||||
|
||||
|
||||
<div
|
||||
v-if="templateData"
|
||||
class="mt-4"
|
||||
>
|
||||
<div
|
||||
v-if="hasTemplates"
|
||||
class="template-buttons"
|
||||
>
|
||||
<!-- Subject specific books -->
|
||||
<template v-if="subjectBooks">
|
||||
<div v-for="(pages, book) in subjectBooks" :key="book" class="button-group">
|
||||
<div
|
||||
v-for="(pages, book) in subjectBooks"
|
||||
:key="book"
|
||||
class="button-group"
|
||||
>
|
||||
<v-chip
|
||||
:color="isBookSelected(book) ? 'success' : 'default'"
|
||||
:variant="isBookSelected(book) ? 'elevated' : 'flat'"
|
||||
@@ -48,7 +60,10 @@
|
||||
</v-chip>
|
||||
|
||||
<!-- Show pages only if book is selected -->
|
||||
<div v-if="isBookSelected(book)" class="pages-container mt-2">
|
||||
<div
|
||||
v-if="isBookSelected(book)"
|
||||
class="pages-container mt-2"
|
||||
>
|
||||
<v-chip
|
||||
v-for="page in pages"
|
||||
:key="page"
|
||||
@@ -65,7 +80,11 @@
|
||||
|
||||
<!-- Common books -->
|
||||
<template v-if="commonBooks">
|
||||
<div v-for="(pages, book) in commonBooks" :key="book" class="button-group">
|
||||
<div
|
||||
v-for="(pages, book) in commonBooks"
|
||||
:key="book"
|
||||
class="button-group"
|
||||
>
|
||||
<v-chip
|
||||
:color="isBookSelected(book) ? 'success' : 'default'"
|
||||
:variant="isBookSelected(book) ? 'elevated' : 'flat'"
|
||||
@@ -76,7 +95,10 @@
|
||||
</v-chip>
|
||||
|
||||
<!-- Show pages only if book is selected -->
|
||||
<div v-if="isBookSelected(book)" class="pages-container mt-2">
|
||||
<div
|
||||
v-if="isBookSelected(book)"
|
||||
class="pages-container mt-2"
|
||||
>
|
||||
<v-chip
|
||||
v-for="page in pages"
|
||||
:key="page"
|
||||
@@ -92,7 +114,10 @@
|
||||
</template>
|
||||
|
||||
<!-- Actions -->
|
||||
<div v-if="templateData.actions?.length" class="button-group">
|
||||
<div
|
||||
v-if="templateData.actions?.length"
|
||||
class="button-group"
|
||||
>
|
||||
<v-chip
|
||||
v-for="action in templateData.actions"
|
||||
:key="action"
|
||||
@@ -105,14 +130,21 @@
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-body-2 text-disabled mt-2">
|
||||
<div
|
||||
v-else
|
||||
class="text-center text-body-2 text-disabled mt-2"
|
||||
>
|
||||
暂无可用的模板
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Tools Section -->
|
||||
<div v-if="showQuickTools && !isMobile" class="quick-tools ml-4" style="min-width: 180px;">
|
||||
<div
|
||||
v-if="showQuickTools && !isMobile"
|
||||
class="quick-tools ml-4"
|
||||
style="min-width: 180px;"
|
||||
>
|
||||
<!-- Numeric Keypad -->
|
||||
<div class="numeric-keypad mb-4">
|
||||
<div class="keypad-row">
|
||||
@@ -223,10 +255,11 @@
|
||||
border-color="warning"
|
||||
prominent
|
||||
>
|
||||
<template #prepend>
|
||||
</template>
|
||||
<template #prepend />
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-h6 mb-1">你打算修改历史?</div>
|
||||
<div class="text-h6 mb-1">
|
||||
你打算修改历史?
|
||||
</div>
|
||||
<div class="text-body-2">
|
||||
这是 {{ new Date(currentDateString.slice(0,4), currentDateString.slice(4,6)-1, currentDateString.slice(6,8)).toLocaleDateString() }} 的作业 • 请谨慎操作,确保不会覆盖重要数据
|
||||
</div>
|
||||
@@ -236,7 +269,6 @@
|
||||
<div class="text-center text-body-2 text-disabled mb-5">
|
||||
点击空白处完成编辑
|
||||
</div>
|
||||
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
<template>
|
||||
<v-navigation-drawer v-if="drawer" v-model="drawer" location="right" temporary width="400">
|
||||
<v-navigation-drawer
|
||||
v-if="drawer"
|
||||
v-model="drawer"
|
||||
location="right"
|
||||
temporary
|
||||
width="400"
|
||||
>
|
||||
<v-toolbar color="primary">
|
||||
<v-toolbar-title>消息记录</v-toolbar-title>
|
||||
|
||||
</v-toolbar>
|
||||
|
||||
<v-list>
|
||||
<v-list-item v-for="msg in messages" :key="msg.id" rounded>
|
||||
<v-list-item
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
rounded
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon :color="colors[msg.type]" :icon="icons[msg.type]" size="20"/>
|
||||
<v-icon
|
||||
:color="colors[msg.type]"
|
||||
:icon="icons[msg.type]"
|
||||
size="20"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>{{ msg.title }}</v-list-item-title>
|
||||
<v-list-item-subtitle v-if="msg.content">{{
|
||||
msg.content
|
||||
<v-list-item-subtitle v-if="msg.content">
|
||||
{{
|
||||
msg.content
|
||||
}}
|
||||
</v-list-item-subtitle>
|
||||
<span class="text-caption text-grey">
|
||||
{{ new Date(msg.timestamp).toLocaleTimeString() }}
|
||||
</span>
|
||||
|
||||
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item v-if="!messages.length">
|
||||
<template #prepend>
|
||||
<v-icon color="grey" icon="mdi-inbox"/>
|
||||
<v-icon
|
||||
color="grey"
|
||||
icon="mdi-inbox"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title class="text-grey">暂无消息</v-list-item-title>
|
||||
<v-list-item-title class="text-grey">
|
||||
暂无消息
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
@@ -4,17 +4,23 @@
|
||||
<v-card-title>迁移设置</v-card-title>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="classNumber"
|
||||
hint="请输入需要迁移的班级编号"
|
||||
label="班级编号"
|
||||
persistent-hint
|
||||
prepend-icon="mdi-account-group"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="machineId"
|
||||
hint="系统已自动填充设备标识,通常无需修改"
|
||||
@@ -22,23 +28,35 @@
|
||||
persistent-hint
|
||||
prepend-icon="mdi-identifier"
|
||||
readonly
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-radio-group v-model="migrationType" class="mt-2">
|
||||
<v-radio label="本地数据迁移" value="local"></v-radio>
|
||||
<v-radio label="服务器数据迁移" value="server"></v-radio>
|
||||
<v-radio-group
|
||||
v-model="migrationType"
|
||||
class="mt-2"
|
||||
>
|
||||
<v-radio
|
||||
label="本地数据迁移"
|
||||
value="local"
|
||||
/>
|
||||
<v-radio
|
||||
label="服务器数据迁移"
|
||||
value="server"
|
||||
/>
|
||||
</v-radio-group>
|
||||
|
||||
<div v-if="migrationType === 'server'" class="mt-4">
|
||||
<div
|
||||
v-if="migrationType === 'server'"
|
||||
class="mt-4"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="serverUrl"
|
||||
hint="输入服务器域名,例如:https://example.com"
|
||||
label="服务器地址"
|
||||
persistent-hint
|
||||
prepend-icon="mdi-server"
|
||||
></v-text-field>
|
||||
/>
|
||||
|
||||
<v-alert
|
||||
class="mt-2"
|
||||
@@ -46,33 +64,44 @@
|
||||
type="info"
|
||||
variant="outlined"
|
||||
>
|
||||
服务器接口格式:<br/>
|
||||
- 配置接口:域名/班号/config<br/>
|
||||
服务器接口格式:<br>
|
||||
- 配置接口:域名/班号/config<br>
|
||||
- 作业数据接口:域名/班号/homework?date=YYYY-MM-DD
|
||||
</v-alert>
|
||||
|
||||
<div class="d-flex align-center mt-4">
|
||||
<v-icon class="mr-2" color="warning">mdi-calendar-range</v-icon>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="warning"
|
||||
>
|
||||
mdi-calendar-range
|
||||
</v-icon>
|
||||
<span class="text-subtitle-1">选择迁移时间范围:</span>
|
||||
</div>
|
||||
|
||||
<v-row class="mt-1">
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="startDate"
|
||||
label="开始日期"
|
||||
prepend-icon="mdi-calendar-start"
|
||||
type="date"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="endDate"
|
||||
label="结束日期"
|
||||
prepend-icon="mdi-calendar-end"
|
||||
type="date"
|
||||
></v-text-field>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
@@ -85,7 +114,7 @@
|
||||
<span>{{
|
||||
migrationType === "local" ? "本地数据库内容" : "服务器数据内容"
|
||||
}}</span>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:loading="loading || scanning"
|
||||
color="primary"
|
||||
@@ -104,9 +133,9 @@
|
||||
type="info"
|
||||
>
|
||||
{{
|
||||
migrationType === "local"
|
||||
? '尚未扫描本地数据或未找到可迁移的数据。点击"扫描数据"按钮开始扫描。'
|
||||
: '尚未预览服务器数据或未找到可迁移的数据。点击"加载数据"按钮开始查询。'
|
||||
migrationType === "local"
|
||||
? '尚未扫描本地数据或未找到可迁移的数据。点击"扫描数据"按钮开始扫描。'
|
||||
: '尚未预览服务器数据或未找到可迁移的数据。点击"加载数据"按钮开始查询。'
|
||||
}}
|
||||
</v-alert>
|
||||
|
||||
@@ -144,7 +173,7 @@
|
||||
<v-skeleton-loader
|
||||
v-if="loading || scanning"
|
||||
type="table"
|
||||
></v-skeleton-loader>
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
@@ -152,18 +181,27 @@
|
||||
<v-card-title>迁移目标</v-card-title>
|
||||
<v-card-text>
|
||||
<v-radio-group v-model="targetStorage">
|
||||
<v-radio label="本地 KV 存储" value="kv-local"></v-radio>
|
||||
<v-radio label="服务器 KV 存储" value="kv-server"></v-radio>
|
||||
<v-radio
|
||||
label="本地 KV 存储"
|
||||
value="kv-local"
|
||||
/>
|
||||
<v-radio
|
||||
label="服务器 KV 存储"
|
||||
value="kv-server"
|
||||
/>
|
||||
</v-radio-group>
|
||||
|
||||
<div v-if="targetStorage === 'kv-server'" class="mt-4">
|
||||
<div
|
||||
v-if="targetStorage === 'kv-server'"
|
||||
class="mt-4"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="targetServerUrl"
|
||||
hint="输入KV服务器地址,例如:https://example.com/kv-api"
|
||||
label="目标服务器地址"
|
||||
persistent-hint
|
||||
prepend-icon="mdi-server-network"
|
||||
></v-text-field>
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@@ -179,16 +217,26 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-dialog v-model="showResult" max-width="600">
|
||||
<v-dialog
|
||||
v-model="showResult"
|
||||
max-width="600"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon :color="migrationSuccess ? 'success' : 'error'" class="mr-2">
|
||||
<v-icon
|
||||
:color="migrationSuccess ? 'success' : 'error'"
|
||||
class="mr-2"
|
||||
>
|
||||
{{ migrationSuccess ? "mdi-check-circle" : "mdi-alert-circle" }}
|
||||
</v-icon>
|
||||
<span>{{ migrationSuccess ? "迁移成功" : "迁移失败" }}</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert v-if="migrationError" class="mb-4" type="error">
|
||||
<v-alert
|
||||
v-if="migrationError"
|
||||
class="mb-4"
|
||||
type="error"
|
||||
>
|
||||
{{ migrationError }}
|
||||
</v-alert>
|
||||
|
||||
@@ -197,7 +245,7 @@
|
||||
成功迁移 {{ migrationStats.success }} 项数据到
|
||||
{{ targetStorage === "kv-local" ? "本地" : "服务器" }} KV 存储。
|
||||
</p>
|
||||
<v-divider class="my-4"></v-divider>
|
||||
<v-divider class="my-4" />
|
||||
<v-list>
|
||||
<v-list-subheader>迁移详情</v-list-subheader>
|
||||
<v-list-item
|
||||
@@ -215,8 +263,13 @@
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" @click="showResult = false"> 关闭</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="showResult = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -5,16 +5,32 @@
|
||||
max-width="600"
|
||||
persistent
|
||||
>
|
||||
<v-card border class="random-picker-card" rounded="xl">
|
||||
<v-card
|
||||
border
|
||||
class="random-picker-card"
|
||||
rounded="xl"
|
||||
>
|
||||
<v-card-title class="text-h5 d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-account-question"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-account-question"
|
||||
/>
|
||||
随机点名
|
||||
<v-spacer/>
|
||||
<v-btn icon="mdi-close" variant="text" @click="dialog = false"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="dialog = false"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text v-if="!isPickingStarted" class="text-center py-6">
|
||||
<div class="text-h6 mb-4">请选择抽取人数</div>
|
||||
<v-card-text
|
||||
v-if="!isPickingStarted"
|
||||
class="text-center py-6"
|
||||
>
|
||||
<div class="text-h6 mb-4">
|
||||
请选择抽取人数
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-center align-center counter-container">
|
||||
<v-btn
|
||||
@@ -52,14 +68,29 @@
|
||||
mandatory
|
||||
rounded="pill"
|
||||
>
|
||||
<v-btn prepend-icon="mdi-account" value="name">姓名模式</v-btn>
|
||||
<v-btn prepend-icon="mdi-numeric" value="number">学号模式</v-btn>
|
||||
<v-btn
|
||||
prepend-icon="mdi-account"
|
||||
value="name"
|
||||
>
|
||||
姓名模式
|
||||
</v-btn>
|
||||
<v-btn
|
||||
prepend-icon="mdi-numeric"
|
||||
value="number"
|
||||
>
|
||||
学号模式
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</div>
|
||||
|
||||
<!-- 学号范围设置 -->
|
||||
<div v-if="pickerMode === 'number'" class="number-range-container mt-4">
|
||||
<div class="text-subtitle-1 mb-2">学号范围设置</div>
|
||||
<div
|
||||
v-if="pickerMode === 'number'"
|
||||
class="number-range-container mt-4"
|
||||
>
|
||||
<div class="text-subtitle-1 mb-2">
|
||||
学号范围设置
|
||||
</div>
|
||||
<div class="d-flex justify-center align-center gap-4">
|
||||
<v-text-field
|
||||
v-model.number="minNumber"
|
||||
@@ -98,7 +129,10 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredStudents.length === 0" class="mt-4 text-error">
|
||||
<div
|
||||
v-if="filteredStudents.length === 0"
|
||||
class="mt-4 text-error"
|
||||
>
|
||||
<template v-if="pickerMode === 'name'">
|
||||
没有可抽取的学生,请调整过滤选项
|
||||
</template>
|
||||
@@ -109,8 +143,11 @@
|
||||
|
||||
<div class="mt-4 text-caption">
|
||||
当前可抽取学生: {{ filteredStudents.length }}人
|
||||
<v-tooltip v-if="pickerMode === 'name'" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-tooltip
|
||||
v-if="pickerMode === 'name'"
|
||||
location="bottom"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-icon
|
||||
class="ml-1"
|
||||
icon="mdi-information-outline"
|
||||
@@ -132,7 +169,10 @@
|
||||
</v-tooltip>
|
||||
|
||||
<!-- 添加临时过滤选项 -->
|
||||
<div v-if="pickerMode === 'name'" class="d-flex flex-wrap justify-center gap-2 mt-4">
|
||||
<div
|
||||
v-if="pickerMode === 'name'"
|
||||
class="d-flex flex-wrap justify-center gap-2 mt-4"
|
||||
>
|
||||
<v-chip
|
||||
:color="tempFilters.excludeLate ? 'warning' : 'default'"
|
||||
:variant="tempFilters.excludeLate ? 'elevated' : 'text'"
|
||||
@@ -165,8 +205,14 @@
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text v-else class="text-center py-6">
|
||||
<div v-if="isAnimating" class="animation-container">
|
||||
<v-card-text
|
||||
v-else
|
||||
class="text-center py-6"
|
||||
>
|
||||
<div
|
||||
v-if="isAnimating"
|
||||
class="animation-container"
|
||||
>
|
||||
<div class="animation-wrapper">
|
||||
<transition-group
|
||||
class="shuffle-container"
|
||||
@@ -185,8 +231,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="result-container">
|
||||
<div class="text-h6 mb-4">抽取结果</div>
|
||||
<div
|
||||
v-else
|
||||
class="result-container"
|
||||
>
|
||||
<div class="text-h6 mb-4">
|
||||
抽取结果
|
||||
</div>
|
||||
<v-card
|
||||
v-for="(student, index) in pickedStudents"
|
||||
:key="index"
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
<template>
|
||||
<v-dialog v-model="isVisible" max-width="500" persistent>
|
||||
<v-dialog
|
||||
v-model="isVisible"
|
||||
max-width="500"
|
||||
persistent
|
||||
>
|
||||
<v-card class="rate-limit-modal">
|
||||
<v-card-title class="text-center pa-4 bg-error text-white">
|
||||
<v-icon class="mr-2" icon="mdi-clock-alert-outline" size="large"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-clock-alert-outline"
|
||||
size="large"
|
||||
/>
|
||||
请求频率超限
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-6">
|
||||
<div class="text-body-1 mb-4">您的请求过于频繁,请稍后再试。</div>
|
||||
<div class="text-body-1 mb-4">
|
||||
您的请求过于频繁,请稍后再试。
|
||||
</div>
|
||||
|
||||
<v-card v-if="activeRequests.length > 0" class="mb-4" flat>
|
||||
<v-card
|
||||
v-if="activeRequests.length > 0"
|
||||
class="mb-4"
|
||||
flat
|
||||
>
|
||||
<v-card-text>
|
||||
<v-list
|
||||
v-for="(request, index) in activeRequests"
|
||||
:key="index"
|
||||
class="mb-4"
|
||||
>
|
||||
<v-list-item color="primary" prepend-icon="mdi-web">
|
||||
<v-list-item
|
||||
color="primary"
|
||||
prepend-icon="mdi-web"
|
||||
>
|
||||
<v-list-item-title>
|
||||
等待时间:
|
||||
<span class="text-primary font-weight-bold">{{
|
||||
@@ -27,12 +44,11 @@
|
||||
{{ request.method }} {{ request.path }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list
|
||||
>
|
||||
</v-list>
|
||||
<v-divider
|
||||
v-if="index < activeRequests.length - 1"
|
||||
class="my-3"
|
||||
></v-divider>
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
@@ -42,8 +58,14 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-4 pt-0">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" variant="tonal" @click="close"> 我知道了</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
@click="close"
|
||||
>
|
||||
我知道了
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@click:close="dismissed = true"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-lock-alert"/>
|
||||
<v-icon icon="mdi-lock-alert" />
|
||||
</template>
|
||||
<v-alert-title>当前使用只读 Token</v-alert-title>
|
||||
<div class="text-body-2">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<v-card class="settings-card rounded-lg" elevation="2">
|
||||
<v-card
|
||||
class="settings-card rounded-lg"
|
||||
elevation="2"
|
||||
>
|
||||
<v-card-item>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
@@ -8,7 +11,9 @@
|
||||
size="large"
|
||||
/>
|
||||
</template>
|
||||
<v-card-title class="text-h6">{{ title }}</v-card-title>
|
||||
<v-card-title class="text-h6">
|
||||
{{ title }}
|
||||
</v-card-title>
|
||||
</v-card-item>
|
||||
|
||||
<v-card-text>
|
||||
@@ -18,11 +23,14 @@
|
||||
color="primary"
|
||||
indeterminate
|
||||
/>
|
||||
<slot/>
|
||||
<slot />
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions v-if="$slots.actions" class="pa-4">
|
||||
<slot name="actions"/>
|
||||
<v-card-actions
|
||||
v-if="$slots.actions"
|
||||
class="pa-4"
|
||||
>
|
||||
<slot name="actions" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 统一链接生成器卡片 -->
|
||||
<v-card border class="unified-link-generator">
|
||||
<v-card
|
||||
border
|
||||
class="unified-link-generator"
|
||||
>
|
||||
<v-card-title class="text-h6">
|
||||
<v-icon class="mr-2" icon="mdi-link-variant" start/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-link-variant"
|
||||
start
|
||||
/>
|
||||
统一链接生成器
|
||||
</v-card-title>
|
||||
|
||||
@@ -13,15 +20,23 @@
|
||||
</div>
|
||||
|
||||
<!-- 预配置认证信息部分 -->
|
||||
<v-card class="mb-4" variant="tonal">
|
||||
<v-card
|
||||
class="mb-4"
|
||||
variant="tonal"
|
||||
>
|
||||
<v-card-title class="text-subtitle-1">
|
||||
<v-icon start>mdi-account-key</v-icon>
|
||||
<v-icon start>
|
||||
mdi-account-key
|
||||
</v-icon>
|
||||
预配置认证信息
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="preconfigForm.namespace"
|
||||
hint="设备的命名空间标识符"
|
||||
@@ -32,7 +47,10 @@
|
||||
variant="outlined"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="preconfigForm.authCode"
|
||||
hint="留空则需要用户手动输入"
|
||||
@@ -64,9 +82,19 @@
|
||||
type="info"
|
||||
variant="tonal"
|
||||
>
|
||||
<div class="text-subtitle-2 mb-2">预配置信息:</div>
|
||||
<v-chip class="mr-2 mb-1" size="small">
|
||||
<v-icon size="small" start>mdi-identifier</v-icon>
|
||||
<div class="text-subtitle-2 mb-2">
|
||||
预配置信息:
|
||||
</div>
|
||||
<v-chip
|
||||
class="mr-2 mb-1"
|
||||
size="small"
|
||||
>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-identifier
|
||||
</v-icon>
|
||||
命名空间: {{ preconfigForm.namespace }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
@@ -75,12 +103,27 @@
|
||||
color="warning"
|
||||
size="small"
|
||||
>
|
||||
<v-icon size="small" start>mdi-lock</v-icon>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-lock
|
||||
</v-icon>
|
||||
认证码: {{ preconfigForm.authCode.length > 8 ? preconfigForm.authCode.substring(0, 8) + "..." :
|
||||
preconfigForm.authCode }}
|
||||
preconfigForm.authCode }}
|
||||
</v-chip>
|
||||
<v-chip v-else class="mr-2 mb-1" color="grey" size="small">
|
||||
<v-icon size="small" start>mdi-lock-open</v-icon>
|
||||
<v-chip
|
||||
v-else
|
||||
class="mr-2 mb-1"
|
||||
color="grey"
|
||||
size="small"
|
||||
>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-lock-open
|
||||
</v-icon>
|
||||
无认证码
|
||||
</v-chip>
|
||||
<v-chip
|
||||
@@ -88,8 +131,12 @@
|
||||
class="mr-2 mb-1"
|
||||
size="small"
|
||||
>
|
||||
<v-icon size="small" start>{{
|
||||
preconfigForm.autoExecute ? "mdi-play-circle" : "mdi-hand-back-left"
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
{{
|
||||
preconfigForm.autoExecute ? "mdi-play-circle" : "mdi-hand-back-left"
|
||||
}}
|
||||
</v-icon>
|
||||
{{ preconfigForm.autoExecute ? "自动认证" : "手动认证" }}
|
||||
@@ -99,9 +146,14 @@
|
||||
</v-card>
|
||||
|
||||
<!-- 设置分享部分 -->
|
||||
<v-card class="mb-4" variant="tonal">
|
||||
<v-card
|
||||
class="mb-4"
|
||||
variant="tonal"
|
||||
>
|
||||
<v-card-title class="text-subtitle-1">
|
||||
<v-icon start>mdi-cog-transfer</v-icon>
|
||||
<v-icon start>
|
||||
mdi-cog-transfer
|
||||
</v-icon>
|
||||
设置分享(可选)
|
||||
</v-card-title>
|
||||
|
||||
@@ -152,7 +204,10 @@
|
||||
|
||||
<!-- 选择摘要 -->
|
||||
<div class="d-flex align-center mb-3 flex-wrap gap-2">
|
||||
<v-chip class="mr-2" color="primary">
|
||||
<v-chip
|
||||
class="mr-2"
|
||||
color="primary"
|
||||
>
|
||||
已选 {{ selectedItems.length }} 项设置
|
||||
</v-chip>
|
||||
|
||||
@@ -183,7 +238,9 @@
|
||||
<v-expansion-panel-title>
|
||||
<template #default="{ expanded }">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2">{{ expanded ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
{{ expanded ? 'mdi-chevron-up' : 'mdi-chevron-down' }}
|
||||
</v-icon>
|
||||
显示设置列表详情
|
||||
</div>
|
||||
</template>
|
||||
@@ -261,9 +318,14 @@
|
||||
</v-card>
|
||||
|
||||
<!-- 链接生成和操作部分 -->
|
||||
<v-card class="mb-4" variant="outlined">
|
||||
<v-card
|
||||
class="mb-4"
|
||||
variant="outlined"
|
||||
>
|
||||
<v-card-title class="text-subtitle-1">
|
||||
<v-icon start>mdi-link</v-icon>
|
||||
<v-icon start>
|
||||
mdi-link
|
||||
</v-icon>
|
||||
生成的统一链接
|
||||
</v-card-title>
|
||||
|
||||
@@ -317,10 +379,20 @@
|
||||
type="success"
|
||||
variant="tonal"
|
||||
>
|
||||
<div class="text-subtitle-2 mb-2">链接包含内容:</div>
|
||||
<div class="text-subtitle-2 mb-2">
|
||||
链接包含内容:
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
<v-chip color="primary" size="small">
|
||||
<v-icon size="small" start>mdi-account-key</v-icon>
|
||||
<v-chip
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-account-key
|
||||
</v-icon>
|
||||
预配置认证
|
||||
</v-chip>
|
||||
<v-chip
|
||||
@@ -328,11 +400,25 @@
|
||||
color="secondary"
|
||||
size="small"
|
||||
>
|
||||
<v-icon size="small" start>mdi-cog</v-icon>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-cog
|
||||
</v-icon>
|
||||
{{ selectedItems.length }} 项设置
|
||||
</v-chip>
|
||||
<v-chip v-else color="grey" size="small">
|
||||
<v-icon size="small" start>mdi-cog-off</v-icon>
|
||||
<v-chip
|
||||
v-else
|
||||
color="grey"
|
||||
size="small"
|
||||
>
|
||||
<v-icon
|
||||
size="small"
|
||||
start
|
||||
>
|
||||
mdi-cog-off
|
||||
</v-icon>
|
||||
无额外设置
|
||||
</v-chip>
|
||||
</div>
|
||||
@@ -341,8 +427,13 @@
|
||||
</v-card>
|
||||
|
||||
<!-- 安全提醒 -->
|
||||
<v-alert type="warning" variant="tonal">
|
||||
<div class="text-subtitle-2 mb-2">⚠️ 安全提醒</div>
|
||||
<v-alert
|
||||
type="warning"
|
||||
variant="tonal"
|
||||
>
|
||||
<div class="text-subtitle-2 mb-2">
|
||||
⚠️ 安全提醒
|
||||
</div>
|
||||
<ul class="text-body-2 pl-4">
|
||||
<li>认证码和设置信息会在URL中传输,请谨慎分发</li>
|
||||
<li>建议仅在受信任的网络环境中使用</li>
|
||||
@@ -509,6 +600,45 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听选择变化,自动生成统一链接
|
||||
selectedItems: {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
// 监听预配置表单变化,自动生成统一链接
|
||||
"preconfigForm.namespace": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
} else {
|
||||
this.unifiedLink = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
"preconfigForm.authCode": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
"preconfigForm.autoExecute": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 处理表格选择变化
|
||||
@@ -782,44 +912,5 @@ export default {
|
||||
this.linkCopied = false;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听选择变化,自动生成统一链接
|
||||
selectedItems: {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
// 监听预配置表单变化,自动生成统一链接
|
||||
"preconfigForm.namespace": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
} else {
|
||||
this.unifiedLink = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
"preconfigForm.authCode": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
"preconfigForm.autoExecute": {
|
||||
handler() {
|
||||
if (this.preconfigForm.namespace.trim()) {
|
||||
this.generateUnifiedLink();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
>
|
||||
稍后设置
|
||||
</v-btn>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:disabled="!selectedName || saving"
|
||||
:loading="saving"
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 发送者信息(使用 Vuetify Card) -->
|
||||
<v-card variant="flat" color="white">
|
||||
<v-card
|
||||
variant="flat"
|
||||
color="white"
|
||||
>
|
||||
<v-card-title>发送者信息</v-card-title>
|
||||
<v-card-text>
|
||||
<v-chip
|
||||
@@ -26,7 +29,12 @@
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
<v-icon left size="16"> mdi-account </v-icon>
|
||||
<v-icon
|
||||
left
|
||||
size="16"
|
||||
>
|
||||
mdi-account
|
||||
</v-icon>
|
||||
{{ senderName }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
@@ -35,7 +43,12 @@
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
<v-icon left size="16"> mdi-devices </v-icon>
|
||||
<v-icon
|
||||
left
|
||||
size="16"
|
||||
>
|
||||
mdi-devices
|
||||
</v-icon>
|
||||
{{ deviceType }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
@@ -44,18 +57,33 @@
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
<v-icon left size="16"> mdi-clock </v-icon>
|
||||
<v-icon
|
||||
left
|
||||
size="16"
|
||||
>
|
||||
mdi-clock
|
||||
</v-icon>
|
||||
{{ formatTime(currentNotification?.timestamp) }}
|
||||
</v-chip>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 多通知导航 -->
|
||||
<div v-if="hasMultipleNotifications" class="navigation-controls mt-6">
|
||||
<v-card variant="flat" color="rgba(255,255,255,0.1)">
|
||||
<div
|
||||
v-if="hasMultipleNotifications"
|
||||
class="navigation-controls mt-6"
|
||||
>
|
||||
<v-card
|
||||
variant="flat"
|
||||
color="rgba(255,255,255,0.1)"
|
||||
>
|
||||
<v-card-text class="text-center">
|
||||
<div class="notification-counter mb-3">
|
||||
<v-chip color="white" variant="flat" size="small">
|
||||
<v-chip
|
||||
color="white"
|
||||
variant="flat"
|
||||
size="small"
|
||||
>
|
||||
{{ notificationCountText }}
|
||||
</v-chip>
|
||||
</div>
|
||||
@@ -88,8 +116,15 @@
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mt-8">
|
||||
<v-btn color="white" size="large" variant="flat" @click="close">
|
||||
<v-icon left> mdi-check </v-icon>
|
||||
<v-btn
|
||||
color="white"
|
||||
size="large"
|
||||
variant="flat"
|
||||
@click="close"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
我知道了
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
|
||||
|
||||
<v-card-text>
|
||||
<v-form>
|
||||
<v-row>
|
||||
@@ -42,15 +40,14 @@
|
||||
label="强调通知"
|
||||
color="red"
|
||||
inset
|
||||
>
|
||||
</v-switch>
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="notificationForm.isPersistent"
|
||||
label="常驻展示"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mt-0"
|
||||
></v-checkbox>
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
@@ -81,8 +78,6 @@
|
||||
</v-btn>
|
||||
|
||||
<v-spacer />
|
||||
|
||||
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
@@ -93,11 +88,16 @@
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<v-icon class="mr-2">mdi-pin</v-icon>
|
||||
<v-icon class="mr-2">
|
||||
mdi-pin
|
||||
</v-icon>
|
||||
常驻通知管理
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div v-if="persistentNotifications.length === 0" class="text-center text-grey py-4">
|
||||
<div
|
||||
v-if="persistentNotifications.length === 0"
|
||||
class="text-center text-grey py-4"
|
||||
>
|
||||
暂无常驻通知
|
||||
</div>
|
||||
<v-list v-else>
|
||||
@@ -108,14 +108,25 @@
|
||||
:subtitle="formatTime(item.timestamp)"
|
||||
lines="two"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<template #prepend>
|
||||
<v-icon :color="item.isUrgent ? 'error' : 'primary'">
|
||||
{{ item.isUrgent ? 'mdi-alert-circle' : 'mdi-information' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-btn icon="mdi-pencil" variant="text" size="small" @click="openEditDialog(item)"></v-btn>
|
||||
<v-btn icon="mdi-delete" variant="text" color="error" size="small" @click="deletePersistentNotification(item.id)"></v-btn>
|
||||
<template #append>
|
||||
<v-btn
|
||||
icon="mdi-pencil"
|
||||
variant="text"
|
||||
size="small"
|
||||
@click="openEditDialog(item)"
|
||||
/>
|
||||
<v-btn
|
||||
icon="mdi-delete"
|
||||
variant="text"
|
||||
color="error"
|
||||
size="small"
|
||||
@click="deletePersistentNotification(item.id)"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -201,17 +212,14 @@
|
||||
>
|
||||
<v-card-text class="pa-2">
|
||||
<div class="align-center">
|
||||
|
||||
<span class="text-body-2 font-weight-medium">{{ device.deviceName }} </span>
|
||||
<br/>
|
||||
<br>
|
||||
|
||||
{{ device.deviceType }}
|
||||
|
||||
</div>
|
||||
<div class="text-caption mt-1">
|
||||
已读于 {{ formatDeviceTime(device.timestamp) }}
|
||||
</div>
|
||||
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
@@ -226,7 +234,6 @@
|
||||
>
|
||||
<v-card-text class="pa-2">
|
||||
<div class="align-center">
|
||||
|
||||
<span class="text-body-2 font-weight-medium">{{ device.deviceName }}</span>
|
||||
<v-spacer />
|
||||
<span class="text-caption text-grey">
|
||||
@@ -238,9 +245,9 @@
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
</div>
|
||||
<div v-else> <v-card
|
||||
<div v-else>
|
||||
<v-card
|
||||
|
||||
color="info-lighten-4"
|
||||
variant="outlined"
|
||||
@@ -249,9 +256,10 @@
|
||||
title="无设备在线"
|
||||
>
|
||||
<v-card-text>
|
||||
如果数秒后任然显示这个提示,则可能没有任何设备在线接收通知。
|
||||
如果数秒后任然显示这个提示,则可能没有任何设备在线接收通知。
|
||||
</v-card-text>
|
||||
</v-card></div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
@@ -266,12 +274,22 @@
|
||||
<EventSender ref="eventSender" />
|
||||
|
||||
<!-- 编辑常驻通知对话框 -->
|
||||
<v-dialog v-model="editDialog" max-width="500" :fullscreen="$vuetify.display.xs">
|
||||
<v-dialog
|
||||
v-model="editDialog"
|
||||
max-width="500"
|
||||
:fullscreen="$vuetify.display.xs"
|
||||
>
|
||||
<v-card>
|
||||
<v-toolbar flat density="compact">
|
||||
<v-toolbar
|
||||
flat
|
||||
density="compact"
|
||||
>
|
||||
<v-toolbar-title>编辑常驻通知</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon="mdi-close" @click="editDialog = false"></v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
@click="editDialog = false"
|
||||
/>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-form>
|
||||
@@ -280,38 +298,66 @@
|
||||
label="通知内容"
|
||||
rows="3"
|
||||
auto-grow
|
||||
></v-textarea>
|
||||
/>
|
||||
<v-switch
|
||||
v-model="editForm.isUrgent"
|
||||
label="强调通知"
|
||||
color="error"
|
||||
hide-details
|
||||
></v-switch>
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="editForm.resend"
|
||||
label="保存并重新发送通知"
|
||||
hint="勾选后将作为新通知发送给所有在线设备"
|
||||
persistent-hint
|
||||
></v-checkbox>
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="editDialog = false">取消</v-btn>
|
||||
<v-btn color="primary" :loading="savingEdit" @click="saveEdit">保存</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="editDialog = false"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
:loading="savingEdit"
|
||||
@click="saveEdit"
|
||||
>
|
||||
保存
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
<v-dialog v-model="deleteConfirmDialog" max-width="400">
|
||||
<v-dialog
|
||||
v-model="deleteConfirmDialog"
|
||||
max-width="400"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="text-h5">确认删除</v-card-title>
|
||||
<v-card-title class="text-h5">
|
||||
确认删除
|
||||
</v-card-title>
|
||||
<v-card-text>确定要删除这条常驻通知吗?此操作无法撤销。</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="grey-darken-1" variant="text" @click="deleteConfirmDialog = false">取消</v-btn>
|
||||
<v-btn color="error" variant="text" @click="executeDelete">删除</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="grey-darken-1"
|
||||
variant="text"
|
||||
@click="deleteConfirmDialog = false"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="text"
|
||||
@click="executeDelete"
|
||||
>
|
||||
删除
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@@ -681,7 +727,9 @@ export default {
|
||||
|
||||
try {
|
||||
this.persistentNotifications = this.persistentNotifications.filter(n => n.id !== id)
|
||||
await dataProvider.saveData('notification-list', this.persistentNotifications)
|
||||
// 当通知列表为空时,保存空对象 {} 而不是空数组 [],因为后端不接受空数组
|
||||
const dataToSave = this.persistentNotifications.length > 0 ? this.persistentNotifications : {}
|
||||
await dataProvider.saveData('notification-list', dataToSave)
|
||||
this.$message?.success('已删除')
|
||||
} catch (e) {
|
||||
console.error('删除失败', e)
|
||||
|
||||
@@ -8,19 +8,35 @@
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-account-group" />
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-account-group"
|
||||
/>
|
||||
出勤状态管理
|
||||
<v-spacer />
|
||||
<v-chip v-if="!isMobile" class="ml-2" color="primary" size="small">
|
||||
<v-chip
|
||||
v-if="!isMobile"
|
||||
class="ml-2"
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
{{ dateString }}
|
||||
</v-chip>
|
||||
<v-btn v-if="isMobile" icon="mdi-close" variant="text" @click="$emit('update:modelValue', false)" />
|
||||
<v-btn
|
||||
v-if="isMobile"
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="$emit('update:modelValue', false)"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<!-- 批量操作和搜索 -->
|
||||
<v-row class="mb-4">
|
||||
<v-col cols="12" md="12">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="12"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="attendanceSearch"
|
||||
clearable
|
||||
@@ -128,7 +144,10 @@
|
||||
md="6"
|
||||
sm="6"
|
||||
>
|
||||
<v-card border class="student-card">
|
||||
<v-card
|
||||
border
|
||||
class="student-card"
|
||||
>
|
||||
<v-card-text class="d-flex align-center pa-2">
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-center">
|
||||
@@ -137,11 +156,13 @@
|
||||
class="mr-2"
|
||||
size="24"
|
||||
>
|
||||
<v-icon size="small"
|
||||
>{{ getStudentStatusIcon(student) }}
|
||||
<v-icon size="small">
|
||||
{{ getStudentStatusIcon(student) }}
|
||||
</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-subtitle-1">{{ student }}</div>
|
||||
<div class="text-subtitle-1">
|
||||
{{ student }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attendance-actions">
|
||||
@@ -183,10 +204,19 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-card class="mb-4" color="primary" variant="tonal">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="12"
|
||||
>
|
||||
<v-card
|
||||
class="mb-4"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
>
|
||||
<v-card-text>
|
||||
<div class="text-subtitle-2 mb-2">批量操作</div>
|
||||
<div class="text-subtitle-2 mb-2">
|
||||
批量操作
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<v-btn
|
||||
class="flex-grow-1"
|
||||
@@ -232,8 +262,13 @@
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
|
||||
<v-btn color="primary" @click="$emit('save')">
|
||||
<v-icon start>mdi-content-save</v-icon>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="$emit('save')"
|
||||
>
|
||||
<v-icon start>
|
||||
mdi-content-save
|
||||
</v-icon>
|
||||
保存
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
<span style="white-space: nowrap">
|
||||
{{
|
||||
studentList.length -
|
||||
attendance.absent.length -
|
||||
attendance.late.length -
|
||||
attendance.exclude.length
|
||||
attendance.absent.length -
|
||||
attendance.late.length -
|
||||
attendance.exclude.length
|
||||
}}人
|
||||
</span>
|
||||
</h2>
|
||||
@@ -45,8 +45,7 @@
|
||||
:key="'absent-' + index"
|
||||
class="gray-text"
|
||||
>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span
|
||||
><span style="white-space: nowrap">{{ name }}</span>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name }}</span>
|
||||
</h3>
|
||||
<h2>
|
||||
<span style="white-space: nowrap">迟到</span>
|
||||
@@ -60,8 +59,7 @@
|
||||
:key="'late-' + index"
|
||||
class="gray-text"
|
||||
>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span
|
||||
><span style="white-space: nowrap">{{ name }}</span>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name }}</span>
|
||||
</h3>
|
||||
<h2>
|
||||
<span style="white-space: nowrap">不参与</span>
|
||||
@@ -75,8 +73,7 @@
|
||||
:key="'exclude-' + index"
|
||||
class="gray-text"
|
||||
>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span
|
||||
><span style="white-space: nowrap">{{ name }}</span>
|
||||
<span v-if="display.lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name }}</span>
|
||||
</h3>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="showCancel"
|
||||
variant="text"
|
||||
|
||||
@@ -41,9 +41,7 @@
|
||||
label="命名空间"
|
||||
prepend-inner-icon="mdi-identifier"
|
||||
variant="outlined"
|
||||
>
|
||||
|
||||
</v-text-field>
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="form.password"
|
||||
@@ -51,9 +49,7 @@
|
||||
prepend-inner-icon="mdi-lock-outline"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
>
|
||||
|
||||
</v-text-field>
|
||||
/>
|
||||
|
||||
<v-alert
|
||||
v-if="error"
|
||||
@@ -77,7 +73,7 @@
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:disabled="!form.namespace || authenticating"
|
||||
:loading="authenticating"
|
||||
|
||||
@@ -139,8 +139,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 步骤 3: 询问使用场景 -->
|
||||
@@ -553,7 +551,7 @@
|
||||
</v-icon>
|
||||
上一步
|
||||
</v-btn>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="currentStep < totalSteps && currentStep !== 4"
|
||||
:disabled="currentStep === 3 && !storageType"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-information"/>
|
||||
<v-icon icon="mdi-information" />
|
||||
</template>
|
||||
系统将自动为您创建设备并获取访问令牌,无需手动配置
|
||||
</v-alert>
|
||||
@@ -52,7 +52,7 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-check-circle"/>
|
||||
<v-icon icon="mdi-check-circle" />
|
||||
</template>
|
||||
设备注册成功!已自动获取访问令牌
|
||||
</v-alert>
|
||||
@@ -60,7 +60,7 @@
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-identifier"/>
|
||||
<v-icon icon="mdi-identifier" />
|
||||
</template>
|
||||
<v-list-item-title>设备名称</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ deviceInfo.deviceName }}</v-list-item-subtitle>
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-key"/>
|
||||
<v-icon icon="mdi-key" />
|
||||
</template>
|
||||
<v-list-item-title>设备 UUID</v-list-item-title>
|
||||
<v-list-item-subtitle class="font-mono text-caption">
|
||||
@@ -83,7 +83,7 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-information"/>
|
||||
<v-icon icon="mdi-information" />
|
||||
</template>
|
||||
您可以点击下方按钮访问云端控制台来设置密码和管理高级功能
|
||||
</v-alert>
|
||||
@@ -97,7 +97,7 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon icon="mdi-alert-circle"/>
|
||||
<v-icon icon="mdi-alert-circle" />
|
||||
</template>
|
||||
{{ errorMessage }}
|
||||
</v-alert>
|
||||
@@ -105,7 +105,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
|
||||
<!-- 注册按钮 -->
|
||||
<v-btn
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="showCancel"
|
||||
variant="text"
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
<template>
|
||||
<v-container class="fill-height">
|
||||
<v-responsive class="align-centerfill-height mx-auto" max-width="900">
|
||||
<v-img class="mb-4" height="150" src="@/assets/logo.svg"/>
|
||||
<v-responsive
|
||||
class="align-centerfill-height mx-auto"
|
||||
max-width="900"
|
||||
>
|
||||
<v-img
|
||||
class="mb-4"
|
||||
height="150"
|
||||
src="@/assets/logo.svg"
|
||||
/>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="text-body-2 font-weight-light mb-n1">出现了错误</div>
|
||||
<div class="text-body-2 font-weight-light mb-n1">
|
||||
出现了错误
|
||||
</div>
|
||||
|
||||
<h1 class="text-h2 font-weight-bold">404</h1>
|
||||
<h1 class="text-h2 font-weight-bold">
|
||||
404
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="py-4"/>
|
||||
<div class="py-4" />
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
@@ -21,15 +32,19 @@
|
||||
variant="outlined"
|
||||
>
|
||||
<template #image>
|
||||
<v-img position="top right"/>
|
||||
<v-img position="top right" />
|
||||
</template>
|
||||
|
||||
<template #title>
|
||||
<h2 class="text-h5 font-weight-bold">为什么会出现此错误?</h2>
|
||||
<h2 class="text-h5 font-weight-bold">
|
||||
为什么会出现此错误?
|
||||
</h2>
|
||||
</template>
|
||||
|
||||
<template #subtitle>
|
||||
<div class="text-subtitle-1">大概是页面未找到</div>
|
||||
<div class="text-subtitle-1">
|
||||
大概是页面未找到
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-overlay
|
||||
@@ -70,7 +85,7 @@
|
||||
rounded="lg"
|
||||
title="返回上一页"
|
||||
variant="text"
|
||||
@click="this.$router.back()"
|
||||
@click="$router.back()"
|
||||
>
|
||||
<v-overlay
|
||||
contained
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
>
|
||||
上传
|
||||
</v-btn>
|
||||
<v-btn v-else color="success" size="large" @click="$emit('show-sync-message')">
|
||||
<v-btn
|
||||
v-else
|
||||
color="success"
|
||||
size="large"
|
||||
@click="$emit('show-sync-message')"
|
||||
>
|
||||
同步完成
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@@ -76,7 +81,11 @@
|
||||
variant="tonal"
|
||||
>
|
||||
<v-card-title class="text-subtitle-1">
|
||||
<v-icon icon="mdi-shield-check" size="small" start />
|
||||
<v-icon
|
||||
icon="mdi-shield-check"
|
||||
size="small"
|
||||
start
|
||||
/>
|
||||
屏幕保护技术已启用
|
||||
</v-card-title>
|
||||
<v-card-text class="text-body-2">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div ref="gridContainer" class="grid-masonry">
|
||||
<div
|
||||
ref="gridContainer"
|
||||
class="grid-masonry"
|
||||
>
|
||||
<TransitionGroup name="grid">
|
||||
<div
|
||||
v-for="item in sortedItems"
|
||||
@@ -22,7 +25,11 @@
|
||||
@touchmove="handleTouchMove"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" color="primary" icon="mdi-account-group" />
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="primary"
|
||||
icon="mdi-account-group"
|
||||
/>
|
||||
出勤统计
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -31,36 +38,78 @@
|
||||
<span class="text-h6">
|
||||
{{ item.data.total - item.data.exclude.length }}/{{
|
||||
item.data.total -
|
||||
item.data.absent.length -
|
||||
item.data.late.length -
|
||||
item.data.exclude.length
|
||||
item.data.absent.length -
|
||||
item.data.late.length -
|
||||
item.data.exclude.length
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<v-divider class="mb-2" />
|
||||
|
||||
<div v-if="item.data.absent.length > 0" class="mb-2">
|
||||
<div class="text-error text-caption mb-1">请假 ({{ item.data.absent.length }})</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 4px">
|
||||
<v-chip v-for="name in item.data.absent" :key="name" color="error" size="x-small" variant="flat">
|
||||
<div
|
||||
v-if="item.data.absent.length > 0"
|
||||
class="mb-2"
|
||||
>
|
||||
<div class="text-error text-caption mb-1">
|
||||
请假 ({{ item.data.absent.length }})
|
||||
</div>
|
||||
<div
|
||||
class="d-flex flex-wrap"
|
||||
style="gap: 4px"
|
||||
>
|
||||
<v-chip
|
||||
v-for="name in item.data.absent"
|
||||
:key="name"
|
||||
color="error"
|
||||
size="x-small"
|
||||
variant="flat"
|
||||
>
|
||||
{{ name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.data.late.length > 0" class="mb-2">
|
||||
<div class="text-warning text-caption mb-1">迟到 ({{ item.data.late.length }})</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 4px">
|
||||
<v-chip v-for="name in item.data.late" :key="name" color="warning" size="x-small" variant="flat">
|
||||
<div
|
||||
v-if="item.data.late.length > 0"
|
||||
class="mb-2"
|
||||
>
|
||||
<div class="text-warning text-caption mb-1">
|
||||
迟到 ({{ item.data.late.length }})
|
||||
</div>
|
||||
<div
|
||||
class="d-flex flex-wrap"
|
||||
style="gap: 4px"
|
||||
>
|
||||
<v-chip
|
||||
v-for="name in item.data.late"
|
||||
:key="name"
|
||||
color="warning"
|
||||
size="x-small"
|
||||
variant="flat"
|
||||
>
|
||||
{{ name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.data.exclude.length > 0" class="mb-2">
|
||||
<div class="text-grey text-caption mb-1">不参与 ({{ item.data.exclude.length }})</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 4px">
|
||||
<v-chip v-for="name in item.data.exclude" :key="name" color="grey" size="x-small" variant="flat">
|
||||
<div
|
||||
v-if="item.data.exclude.length > 0"
|
||||
class="mb-2"
|
||||
>
|
||||
<div class="text-grey text-caption mb-1">
|
||||
不参与 ({{ item.data.exclude.length }})
|
||||
</div>
|
||||
<div
|
||||
class="d-flex flex-wrap"
|
||||
style="gap: 4px"
|
||||
>
|
||||
<v-chip
|
||||
v-for="name in item.data.exclude"
|
||||
:key="name"
|
||||
color="grey"
|
||||
size="x-small"
|
||||
variant="flat"
|
||||
>
|
||||
{{ name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
@@ -69,8 +118,8 @@
|
||||
<div
|
||||
v-if="
|
||||
item.data.absent.length === 0 &&
|
||||
item.data.late.length === 0 &&
|
||||
item.data.exclude.length === 0
|
||||
item.data.late.length === 0 &&
|
||||
item.data.exclude.length === 0
|
||||
"
|
||||
class="text-success text-center mt-2"
|
||||
>
|
||||
@@ -91,7 +140,11 @@
|
||||
@touchmove="handleTouchMove"
|
||||
>
|
||||
<v-card-title class="text-primary">
|
||||
<v-icon class="mr-2" icon="mdi-card-text-outline" size="small" />
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-card-text-outline"
|
||||
size="small"
|
||||
/>
|
||||
{{ item.name }}
|
||||
</v-card-title>
|
||||
<v-card-text :style="contentStyle">
|
||||
@@ -129,7 +182,10 @@
|
||||
<!-- 单独显示空科目 -->
|
||||
<div class="empty-subjects mt-4">
|
||||
<!-- 移动端优化视图 -->
|
||||
<div v-if="isMobile" class="d-flex flex-wrap justify-center">
|
||||
<div
|
||||
v-if="isMobile"
|
||||
class="d-flex flex-wrap justify-center"
|
||||
>
|
||||
<v-chip
|
||||
v-for="subject in unusedSubjects"
|
||||
:key="subject.name"
|
||||
@@ -138,24 +194,37 @@
|
||||
variant="tonal"
|
||||
@click="handleCardClick('dialog', subject.name)"
|
||||
>
|
||||
<v-icon start size="small">mdi-plus</v-icon>
|
||||
<v-icon
|
||||
start
|
||||
size="small"
|
||||
>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
{{ subject.name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<template v-else-if="emptySubjectDisplay === 'button'">
|
||||
<v-btn-group divided variant="tonal">
|
||||
<v-btn-group
|
||||
divided
|
||||
variant="tonal"
|
||||
>
|
||||
<v-btn
|
||||
v-for="subject in unusedSubjects"
|
||||
:key="subject.name"
|
||||
@click="handleCardClick('dialog', subject.name)"
|
||||
>
|
||||
<v-icon start> mdi-plus</v-icon>
|
||||
<v-icon start>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
{{ subject.name }}
|
||||
</v-btn>
|
||||
</v-btn-group>
|
||||
</template>
|
||||
<div v-else class="empty-subjects-grid">
|
||||
<div
|
||||
v-else
|
||||
class="empty-subjects-grid"
|
||||
>
|
||||
<TransitionGroup name="v-list">
|
||||
<v-card
|
||||
v-for="subject in unusedSubjects"
|
||||
@@ -168,8 +237,15 @@
|
||||
{{ subject.name }}
|
||||
</v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-icon color="grey" size="small"> mdi-plus</v-icon>
|
||||
<div class="text-caption text-grey">点击添加作业</div>
|
||||
<v-icon
|
||||
color="grey"
|
||||
size="small"
|
||||
>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
<div class="text-caption text-grey">
|
||||
点击添加作业
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</TransitionGroup>
|
||||
|
||||
@@ -1,25 +1,46 @@
|
||||
<template>
|
||||
<v-card border hover rounded="xl">
|
||||
<v-card
|
||||
border
|
||||
hover
|
||||
rounded="xl"
|
||||
>
|
||||
<v-card-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-2" icon="mdi-information" size="large"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-information"
|
||||
size="large"
|
||||
/>
|
||||
</template>
|
||||
<v-card-title class="text-h6">关于</v-card-title>
|
||||
<v-card-title class="text-h6">
|
||||
关于
|
||||
</v-card-title>
|
||||
</v-card-item>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col class="mx-auto" cols="12" md="8">
|
||||
<v-col
|
||||
class="mx-auto"
|
||||
cols="12"
|
||||
md="8"
|
||||
>
|
||||
<div class="d-flex flex-column align-start">
|
||||
<v-avatar class="mb-4" size="120">
|
||||
<v-avatar
|
||||
class="mb-4"
|
||||
size="120"
|
||||
>
|
||||
<v-img
|
||||
alt="Classworks"
|
||||
src="../../assets/cslogo.png"
|
||||
/>
|
||||
</v-avatar>
|
||||
|
||||
<h2 class="text-h5 mb-2">Classworks</h2>
|
||||
<p class="text-body-1 mb-4">适用于班级大屏的作业板小工具</p>
|
||||
<h2 class="text-h5 mb-2">
|
||||
Classworks
|
||||
</h2>
|
||||
<p class="text-body-1 mb-4">
|
||||
适用于班级大屏的作业板小工具
|
||||
</p>
|
||||
|
||||
<div class="d-flex gap-2 flex-wrap mb-6">
|
||||
<v-btn
|
||||
@@ -57,9 +78,11 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-divider class="mb-4 w-100"></v-divider>
|
||||
<v-divider class="mb-4 w-100" />
|
||||
|
||||
<h3 class="text-h6 mb-2">备注与致谢</h3>
|
||||
<h3 class="text-h6 mb-2">
|
||||
备注与致谢
|
||||
</h3>
|
||||
<v-list class="mb-4 bg-transparent">
|
||||
<v-list-item
|
||||
append-icon="mdi-link"
|
||||
@@ -98,7 +121,7 @@
|
||||
新一代,开源,编程社区
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-divider class="ma-1"></v-divider>
|
||||
<v-divider class="ma-1" />
|
||||
<v-list-item
|
||||
append-icon="mdi-link"
|
||||
href="https://github.com/HUSX100/IslandCaller"
|
||||
@@ -141,12 +164,14 @@
|
||||
fullscreen
|
||||
transition="dialog-bottom-transition"
|
||||
>
|
||||
<v-card
|
||||
>
|
||||
<v-card>
|
||||
<v-toolbar>
|
||||
<v-btn icon="mdi-close" @click="showDeps = false"></v-btn>
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
@click="showDeps = false"
|
||||
/>
|
||||
<v-toolbar-title>使用的第三方库</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-list>
|
||||
@@ -170,45 +195,108 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 报告问题对话框 -->
|
||||
<v-dialog v-model="showReportDialog" max-width="640">
|
||||
<v-dialog
|
||||
v-model="showReportDialog"
|
||||
max-width="640"
|
||||
>
|
||||
<v-card>
|
||||
<v-toolbar density="compact">
|
||||
<v-btn icon="mdi-close" @click="showReportDialog = false"></v-btn>
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
@click="showReportDialog = false"
|
||||
/>
|
||||
<v-toolbar-title>报告问题</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<p class="mb-4">
|
||||
调试ID与下方的浏览器环境信息将帮助我们快速定位问题,请在反馈中一并附上。
|
||||
</p>
|
||||
<v-sheet class="mb-3 pa-3 bg-grey-lighten-4 rounded" style="max-height: 260px; overflow: auto;">
|
||||
<pre class="text-body-2" style="white-space: pre-wrap; margin: 0;">{{ envBoxText }}</pre>
|
||||
<v-sheet
|
||||
class="mb-3 pa-3 bg-grey-lighten-4 rounded"
|
||||
style="max-height: 260px; overflow: auto;"
|
||||
>
|
||||
<pre
|
||||
class="text-body-2"
|
||||
style="white-space: pre-wrap; margin: 0;"
|
||||
>{{ envBoxText }}</pre>
|
||||
</v-sheet>
|
||||
<div class="d-flex gap-2 flex-wrap mb-4">
|
||||
<v-btn size="small" variant="text" prepend-icon="mdi-refresh" @click="reloadVisitorId" :loading="visitorLoading">刷新</v-btn>
|
||||
<v-btn size="small" variant="text" prepend-icon="mdi-content-copy" @click="copyEnvInfo">复制信息</v-btn>
|
||||
<v-btn size="small" variant="text" prepend-icon="mdi-open-in-new" @click="goToDebug">查看 /debug 页面</v-btn>
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
prepend-icon="mdi-refresh"
|
||||
:loading="visitorLoading"
|
||||
@click="reloadVisitorId"
|
||||
>
|
||||
刷新
|
||||
</v-btn>
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
prepend-icon="mdi-content-copy"
|
||||
@click="copyEnvInfo"
|
||||
>
|
||||
复制信息
|
||||
</v-btn>
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
prepend-icon="mdi-open-in-new"
|
||||
@click="goToDebug"
|
||||
>
|
||||
查看 /debug 页面
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-alert v-if="copyOk" type="success" density="compact" class="mb-4">已复制到剪贴板</v-alert>
|
||||
<h4 class="text-subtitle-1 mb-2">反馈渠道</h4>
|
||||
<v-list lines="one" class="bg-transparent">
|
||||
<v-list-item :href="qqGroupLink" target="_blank" prepend-icon="mdi-qqchat">
|
||||
<v-alert
|
||||
v-if="copyOk"
|
||||
type="success"
|
||||
density="compact"
|
||||
class="mb-4"
|
||||
>
|
||||
已复制到剪贴板
|
||||
</v-alert>
|
||||
<h4 class="text-subtitle-1 mb-2">
|
||||
反馈渠道
|
||||
</h4>
|
||||
<v-list
|
||||
lines="one"
|
||||
class="bg-transparent"
|
||||
>
|
||||
<v-list-item
|
||||
:href="qqGroupLink"
|
||||
target="_blank"
|
||||
prepend-icon="mdi-qqchat"
|
||||
>
|
||||
<v-list-item-title>QQ群 ({{ qqGroupNumber }})</v-list-item-title>
|
||||
<v-list-item-subtitle>964979747</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item :href="githubIssueUrl" target="_blank" prepend-icon="mdi-github">
|
||||
<v-list-item
|
||||
:href="githubIssueUrl"
|
||||
target="_blank"
|
||||
prepend-icon="mdi-github"
|
||||
>
|
||||
<v-list-item-title>GitHub Issue</v-list-item-title>
|
||||
<v-list-item-subtitle>ZeroCatDev/Classworks</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item :href="mailtoLink" target="_blank" prepend-icon="mdi-email">
|
||||
<v-list-item
|
||||
:href="mailtoLink"
|
||||
target="_blank"
|
||||
prepend-icon="mdi-email"
|
||||
>
|
||||
<v-list-item-title>邮件</v-list-item-title>
|
||||
<v-list-item-subtitle>sun@wuyuan.dev</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="showReportDialog = false">关闭</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="showReportDialog = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<v-card :border="border" class="setting-group">
|
||||
<v-card-title v-if="title" class="d-flex align-center">
|
||||
<v-icon v-if="icon" :icon="icon" class="mr-2"/>
|
||||
<v-card
|
||||
:border="border"
|
||||
class="setting-group"
|
||||
>
|
||||
<v-card-title
|
||||
v-if="title"
|
||||
class="d-flex align-center"
|
||||
>
|
||||
<v-icon
|
||||
v-if="icon"
|
||||
:icon="icon"
|
||||
class="mr-2"
|
||||
/>
|
||||
{{ title }}
|
||||
</v-card-title>
|
||||
|
||||
@@ -18,7 +28,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions v-if="$slots.actions">
|
||||
<slot name="actions"></slot>
|
||||
<slot name="actions" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<v-list-item :disabled="disabled" class="setting-item">
|
||||
<v-list-item
|
||||
:disabled="disabled"
|
||||
class="setting-item"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon :icon="settingIcon"/>
|
||||
<v-icon :icon="settingIcon" />
|
||||
</template>
|
||||
|
||||
<v-list-item-title class="text-wrap">
|
||||
@@ -14,7 +17,10 @@
|
||||
|
||||
<template #append>
|
||||
<div class="d-flex flex-column flex-sm-row align-center">
|
||||
<div v-if="type !== 'string' || hasOptions" class="me-2">
|
||||
<div
|
||||
v-if="type !== 'string' || hasOptions"
|
||||
class="me-2"
|
||||
>
|
||||
<!-- 根据设置类型渲染不同的控件 -->
|
||||
<v-switch
|
||||
v-if="type === 'boolean'"
|
||||
@@ -40,7 +46,10 @@
|
||||
@update:model-value="updateSetting"
|
||||
/>
|
||||
|
||||
<div v-else-if="type === 'number'" class="d-flex align-center">
|
||||
<div
|
||||
v-else-if="type === 'number'"
|
||||
class="d-flex align-center"
|
||||
>
|
||||
<v-btn
|
||||
:disabled="disabled || localValue <= minValue"
|
||||
icon="mdi-minus"
|
||||
@@ -76,7 +85,7 @@
|
||||
</div>
|
||||
|
||||
<v-menu location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
:disabled="disabled"
|
||||
class="ml-2"
|
||||
@@ -88,24 +97,36 @@
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="copySettingId">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-key" size="small"/>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
icon="mdi-key"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>复制设置ID</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item @click="copySettingValue">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-content-copy" size="small"/>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
icon="mdi-content-copy"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>复制设置值</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider></v-divider>
|
||||
<v-divider />
|
||||
|
||||
<v-list-item :disabled="isDefaultValue" @click="resetToDefault">
|
||||
<template v-slot:prepend>
|
||||
<v-icon icon="mdi-restore" size="small"/>
|
||||
<v-list-item
|
||||
:disabled="isDefaultValue"
|
||||
@click="resetToDefault"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
icon="mdi-restore"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>重置为默认值</v-list-item-title>
|
||||
</v-list-item>
|
||||
@@ -116,7 +137,10 @@
|
||||
</v-list-item>
|
||||
|
||||
<!-- 文本框显示在下方 -->
|
||||
<div v-if="type === 'string' && !hasOptions" class="px-4 pb-2 pt-0">
|
||||
<div
|
||||
v-if="type === 'string' && !hasOptions"
|
||||
class="px-4 pb-2 pt-0"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="localValue"
|
||||
:disabled="disabled"
|
||||
|
||||
@@ -1,30 +1,46 @@
|
||||
<template>
|
||||
<div class="settings-explorer">
|
||||
|
||||
|
||||
<div>
|
||||
<v-text-field v-model="searchQuery" class="mb-4" clearable density="comfortable" label="搜索设置"
|
||||
prepend-inner-icon="mdi-magnify" variant="outlined"/>
|
||||
<v-text-field
|
||||
v-model="searchQuery"
|
||||
class="mb-4"
|
||||
clearable
|
||||
density="comfortable"
|
||||
label="搜索设置"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
variant="outlined"
|
||||
/>
|
||||
|
||||
|
||||
<v-list>
|
||||
<div v-for="setting in allSettings" :key="setting.key">
|
||||
<setting-item :key="setting.key" :disabled="setting.requireDeveloper && !isDeveloperMode"
|
||||
:setting-key="setting.key" @error="onSettingError"
|
||||
@update="onSettingUpdate"/>
|
||||
<v-divider class="my-2"/>
|
||||
<div
|
||||
v-for="setting in allSettings"
|
||||
:key="setting.key"
|
||||
>
|
||||
<setting-item
|
||||
:key="setting.key"
|
||||
:disabled="setting.requireDeveloper && !isDeveloperMode"
|
||||
:setting-key="setting.key"
|
||||
@error="onSettingError"
|
||||
@update="onSettingUpdate"
|
||||
/>
|
||||
<v-divider class="my-2" />
|
||||
</div>
|
||||
</v-list>
|
||||
<v-card border>
|
||||
<v-card-title class="text-subtitle-1">当前配置</v-card-title>
|
||||
<v-card-title class="text-subtitle-1">
|
||||
当前配置
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<pre class="settings-json">{{ formattedSettings }}</pre>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn @click="copySettingsToClipboard">
|
||||
复制到剪贴板
|
||||
<v-icon right>mdi-content-copy</v-icon>
|
||||
<v-icon right>
|
||||
mdi-content-copy
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
@@ -6,11 +6,20 @@
|
||||
>
|
||||
<v-card-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-2" icon="mdi-account-group" size="large"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-account-group"
|
||||
size="large"
|
||||
/>
|
||||
</template>
|
||||
<v-card-title class="text-h6">学生列表</v-card-title>
|
||||
<v-card-title class="text-h6">
|
||||
学生列表
|
||||
</v-card-title>
|
||||
<template #append>
|
||||
<unsaved-warning :show="unsavedChanges" message="有未保存的更改"/>
|
||||
<unsaved-warning
|
||||
:show="unsavedChanges"
|
||||
message="有未保存的更改"
|
||||
/>
|
||||
<v-btn
|
||||
:disabled="modelValue.list.length === 0"
|
||||
class="mr-2"
|
||||
@@ -39,7 +48,13 @@
|
||||
indeterminate
|
||||
/>
|
||||
|
||||
<v-alert v-if="error" class="mb-4" closable type="error" variant="tonal">
|
||||
<v-alert
|
||||
v-if="error"
|
||||
class="mb-4"
|
||||
closable
|
||||
type="error"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
@@ -47,7 +62,11 @@
|
||||
<!-- 普通编辑模式 -->
|
||||
<div v-if="!modelValue.advanced">
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="4"
|
||||
sm="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="newStudentName"
|
||||
class="mb-4"
|
||||
@@ -88,7 +107,10 @@
|
||||
v-bind="props"
|
||||
>
|
||||
<v-card-text class="d-flex align-center pa-3">
|
||||
<v-menu :open-on-hover="!isMobile" location="bottom">
|
||||
<v-menu
|
||||
:open-on-hover="!isMobile"
|
||||
location="bottom"
|
||||
>
|
||||
<template #activator="{ props: menuProps }">
|
||||
<v-btn
|
||||
class="mr-3 font-weight-medium"
|
||||
@@ -100,7 +122,10 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-list density="compact" nav>
|
||||
<v-list
|
||||
density="compact"
|
||||
nav
|
||||
>
|
||||
<v-list-item
|
||||
:disabled="index === 0"
|
||||
prepend-icon="mdi-arrow-up-bold"
|
||||
@@ -108,7 +133,7 @@
|
||||
>
|
||||
置顶
|
||||
</v-list-item>
|
||||
<v-divider/>
|
||||
<v-divider />
|
||||
<v-list-item
|
||||
:disabled="index === 0"
|
||||
prepend-icon="mdi-arrow-up"
|
||||
@@ -172,7 +197,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 高级编辑模式 -->
|
||||
<div v-else class="pt-2">
|
||||
<div
|
||||
v-else
|
||||
class="pt-2"
|
||||
>
|
||||
<v-textarea
|
||||
v-model="modelValue.text"
|
||||
hint="使用文本编辑模式批量编辑学生名单,保存时会自动去除空行"
|
||||
@@ -187,7 +215,10 @@
|
||||
</v-expand-transition>
|
||||
|
||||
<v-row class="mt-6">
|
||||
<v-col class="d-flex gap-2" cols="12">
|
||||
<v-col
|
||||
class="d-flex gap-2"
|
||||
cols="12"
|
||||
>
|
||||
<v-btn
|
||||
:disabled="loading"
|
||||
:loading="loading"
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<v-card :disabled="!hasNamespaceInfo" :loading="loading" class="my-4">
|
||||
<v-card
|
||||
:disabled="!hasNamespaceInfo"
|
||||
:loading="loading"
|
||||
class="my-4"
|
||||
>
|
||||
<template #loader>
|
||||
<v-progress-linear v-if="loading" color="primary" indeterminate/>
|
||||
<v-progress-linear
|
||||
v-if="loading"
|
||||
color="primary"
|
||||
indeterminate
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -175,7 +183,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@@ -213,7 +221,7 @@
|
||||
<p>您确定要重新初始化云端存储吗?</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="showReinitDialog = false"
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
<template>
|
||||
<settings-card icon="mdi-database-cog" title="数据源设置">
|
||||
<settings-card
|
||||
icon="mdi-database-cog"
|
||||
title="数据源设置"
|
||||
>
|
||||
<v-list>
|
||||
<!-- 服务器模式设置 -->
|
||||
<template
|
||||
v-if="
|
||||
currentProvider === 'kv-server' ||
|
||||
currentProvider === 'classworkscloud'
|
||||
currentProvider === 'classworkscloud'
|
||||
"
|
||||
>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-lan-connect"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-lan-connect"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>检查服务器连接</v-list-item-title>
|
||||
<template #append>
|
||||
@@ -22,90 +28,131 @@
|
||||
测试连接
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item
|
||||
><!-- 数据迁移,仅对KV本地存储有效 -->
|
||||
</v-list-item><!-- 数据迁移,仅对KV本地存储有效 -->
|
||||
</template>
|
||||
|
||||
<!-- 本地存储设置 -->
|
||||
<template v-if="currentProvider === 'kv-local'">
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-database"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-database"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>清除数据库缓存</v-list-item-title>
|
||||
<v-list-item-subtitle
|
||||
>这将清除所有本地数据库中的数据
|
||||
</v-list-item-subtitle
|
||||
>
|
||||
<v-list-item-subtitle>
|
||||
这将清除所有本地数据库中的数据
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn color="error" variant="tonal" @click="confirmClearIndexedDB">
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
@click="confirmClearIndexedDB"
|
||||
>
|
||||
清除
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-database-export"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-database-export"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>导出数据库</v-list-item-title>
|
||||
<template #append>
|
||||
<v-btn variant="tonal" @click="exportData"> 导出</v-btn>
|
||||
<v-btn
|
||||
variant="tonal"
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-database-import"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-database-import"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>迁移旧数据</v-list-item-title>
|
||||
<v-list-item-subtitle
|
||||
>将旧的存储格式数据转移到新的KV存储
|
||||
</v-list-item-subtitle
|
||||
>
|
||||
<v-list-item-subtitle>
|
||||
将旧的存储格式数据转移到新的KV存储
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn :loading="migrateLoading" variant="tonal" @click="migrateData">
|
||||
<v-btn
|
||||
:loading="migrateLoading"
|
||||
variant="tonal"
|
||||
@click="migrateData"
|
||||
>
|
||||
迁移
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item
|
||||
><!-- 显示机器ID -->
|
||||
</v-list-item><!-- 显示机器ID -->
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-identifier"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-identifier"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>本机唯一标识符</v-list-item-title>
|
||||
<v-list-item-subtitle v-if="machineId">{{
|
||||
machineId
|
||||
<v-list-item-subtitle v-if="machineId">
|
||||
{{
|
||||
machineId
|
||||
}}
|
||||
</v-list-item-subtitle>
|
||||
<v-list-item-subtitle v-else>正在加载...</v-list-item-subtitle>
|
||||
<v-list-item-subtitle v-else>
|
||||
正在加载...
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-lan-connect"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-lan-connect"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>查看本地缓存</v-list-item-title>
|
||||
<template #append>
|
||||
<v-btn to="/cachemanagement" variant="tonal"> 查看</v-btn>
|
||||
<v-btn
|
||||
to="/cachemanagement"
|
||||
variant="tonal"
|
||||
>
|
||||
查看
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<!-- 确认对话框 -->
|
||||
<v-dialog v-model="confirmDialog" max-width="400">
|
||||
<v-dialog
|
||||
v-model="confirmDialog"
|
||||
max-width="400"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>{{ confirmTitle }}</v-card-title>
|
||||
<v-card-text>{{ confirmMessage }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="grey" variant="text" @click="confirmDialog = false"
|
||||
>取消
|
||||
</v-btn
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="grey"
|
||||
variant="text"
|
||||
@click="confirmDialog = false"
|
||||
>
|
||||
<v-btn color="error" variant="tonal" @click="handleConfirm"
|
||||
>确认
|
||||
</v-btn
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
确认
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
<template>
|
||||
<settings-card border icon="mdi-monitor" title="显示设置">
|
||||
<settings-card
|
||||
border
|
||||
icon="mdi-monitor"
|
||||
title="显示设置"
|
||||
>
|
||||
<v-list>
|
||||
<setting-item :setting-key="'display.emptySubjectDisplay'"/>
|
||||
<setting-item :setting-key="'display.emptySubjectDisplay'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.dynamicSort'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.dynamicSort'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.showRandomButton'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.showRandomButton'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.showFullscreenButton'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.showFullscreenButton'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.cardHoverEffect'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.cardHoverEffect'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.enhancedTouchMode'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.enhancedTouchMode'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.showQuickTools'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.showQuickTools'" />
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.showAntiScreenBurnCard'"/>
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'display.showExamScheduleButton'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.showAntiScreenBurnCard'" />
|
||||
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'display.showExamScheduleButton'" />
|
||||
</v-list>
|
||||
</settings-card>
|
||||
</template>
|
||||
|
||||
@@ -6,13 +6,22 @@
|
||||
@click="handleClick"
|
||||
>
|
||||
<v-card-text>
|
||||
<div ref="typewriter" class="typewriter-text"></div>
|
||||
<div ref="sourceWriter" class="source-text"></div>
|
||||
<div
|
||||
ref="typewriter"
|
||||
class="typewriter-text"
|
||||
/>
|
||||
<div
|
||||
ref="sourceWriter"
|
||||
class="source-text"
|
||||
/>
|
||||
</v-card-text>
|
||||
<transition name="fade">
|
||||
<v-chip v-if="currentQuote?.contributor" class="contributor">
|
||||
<v-chip
|
||||
v-if="currentQuote?.contributor"
|
||||
class="contributor"
|
||||
>
|
||||
<v-avatar start>
|
||||
<v-img :src="`https://github.com/${currentQuote.contributor}.png`"/>
|
||||
<v-img :src="`https://github.com/${currentQuote.contributor}.png`" />
|
||||
</v-avatar>
|
||||
{{ currentQuote.contributor }}
|
||||
</v-chip>
|
||||
@@ -49,6 +58,10 @@ export default {
|
||||
this.initTypewriters();
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
[this.typewriter, this.sourceWriter].forEach(writer => writer?.stop());
|
||||
},
|
||||
|
||||
methods: {
|
||||
initTypewriters() {
|
||||
this.typewriter = new Typewriter(this.$refs.typewriter, TYPEWRITER_CONFIG.main);
|
||||
@@ -93,10 +106,6 @@ export default {
|
||||
console.error("复制失败:", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
[this.typewriter, this.sourceWriter].forEach(writer => writer?.stop());
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
<template>
|
||||
<settings-card icon="mdi-cog" title="编辑设置">
|
||||
<settings-card
|
||||
icon="mdi-cog"
|
||||
title="编辑设置"
|
||||
>
|
||||
<v-list>
|
||||
<setting-item :setting-key="'edit.autoSave'"/>
|
||||
<setting-item :setting-key="'edit.autoSave'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'edit.blockNonTodayAutoSave'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'edit.blockNonTodayAutoSave'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'edit.confirmNonTodaySave'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'edit.confirmNonTodaySave'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'edit.refreshBeforeEdit'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'edit.refreshBeforeEdit'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'edit.autoSavePromptText'"/>
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'edit.autoSavePromptText'" />
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item :setting-key="'edit.manualSavePromptText'"/>
|
||||
|
||||
<v-divider class="my-2" />
|
||||
<setting-item :setting-key="'edit.manualSavePromptText'" />
|
||||
</v-list>
|
||||
</settings-card>
|
||||
</template>
|
||||
|
||||
@@ -48,8 +48,15 @@
|
||||
</div>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<setting-group border icon="mdi-book" title="科目配置">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<setting-group
|
||||
border
|
||||
icon="mdi-book"
|
||||
title="科目配置"
|
||||
>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-text-field
|
||||
@@ -63,8 +70,14 @@
|
||||
/>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item v-for="subject in subjectList" :key="subject">
|
||||
<v-card border class="w-100 mb-2">
|
||||
<v-list-item
|
||||
v-for="subject in subjectList"
|
||||
:key="subject"
|
||||
>
|
||||
<v-card
|
||||
border
|
||||
class="w-100 mb-2"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-text-field
|
||||
v-model="editedSubjects[subject]"
|
||||
@@ -74,7 +87,7 @@
|
||||
variant="plain"
|
||||
@blur="updateSubject(subject)"
|
||||
/>
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="error"
|
||||
icon="mdi-delete"
|
||||
@@ -95,17 +108,24 @@
|
||||
@keyup.enter="() => addBookType(subject)"
|
||||
/>
|
||||
|
||||
<v-list border density="compact" rounded>
|
||||
<v-list
|
||||
border
|
||||
density="compact"
|
||||
rounded
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(books, bookType) in config.subjects[subject].books"
|
||||
:key="bookType"
|
||||
:title="bookType"
|
||||
@click="openSubjectBookDialog(subject, bookType, books)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-2" icon="mdi-book-open-variant"/>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-book-open-variant"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<template #append>
|
||||
<v-chip
|
||||
class="mr-2"
|
||||
color="info"
|
||||
@@ -130,8 +150,15 @@
|
||||
</setting-group>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<setting-group border icon="mdi-cog" title="通用配置">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<setting-group
|
||||
border
|
||||
icon="mdi-cog"
|
||||
title="通用配置"
|
||||
>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-text-field
|
||||
@@ -146,17 +173,24 @@
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list border density="compact" rounded>
|
||||
<v-list
|
||||
border
|
||||
density="compact"
|
||||
rounded
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(books, bookType) in config.commonSubject.books"
|
||||
:key="bookType"
|
||||
:title="bookType"
|
||||
@click="openSubjectBookDialog('common', bookType, books)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-2" icon="mdi-book-multiple"/>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-book-multiple"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<template #append>
|
||||
<v-chip
|
||||
class="mr-2"
|
||||
color="info"
|
||||
@@ -176,7 +210,7 @@
|
||||
</v-list>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<v-list-item>
|
||||
<v-text-field
|
||||
@@ -191,14 +225,18 @@
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list border density="compact" rounded>
|
||||
<v-list
|
||||
border
|
||||
density="compact"
|
||||
rounded
|
||||
>
|
||||
<v-list-item
|
||||
v-for="action in config.actions"
|
||||
:key="action"
|
||||
:title="action"
|
||||
@click="openActionDialog(action)"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<template #append>
|
||||
<v-btn
|
||||
color="error"
|
||||
icon="mdi-delete"
|
||||
@@ -216,7 +254,10 @@
|
||||
</v-row>
|
||||
|
||||
<!-- 编辑弹框 -->
|
||||
<v-dialog v-model="dialog.show" max-width="600px">
|
||||
<v-dialog
|
||||
v-model="dialog.show"
|
||||
max-width="600px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="text-h5 pa-4">
|
||||
{{ dialog.title }}
|
||||
@@ -235,22 +276,43 @@
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="dialog.editedItem.type === 'subjectBook'" cols="12">
|
||||
<div class="text-subtitle-2 mb-2">所属科目</div>
|
||||
<v-chip color="primary">{{ dialog.editedItem.subject }}</v-chip>
|
||||
<v-col
|
||||
v-if="dialog.editedItem.type === 'subjectBook'"
|
||||
cols="12"
|
||||
>
|
||||
<div class="text-subtitle-2 mb-2">
|
||||
所属科目
|
||||
</div>
|
||||
<v-chip color="primary">
|
||||
{{ dialog.editedItem.subject }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="['subjectBook', 'commonBook'].includes(dialog.editedItem.type)" cols="12">
|
||||
<v-col
|
||||
v-if="['subjectBook', 'commonBook'].includes(dialog.editedItem.type)"
|
||||
cols="12"
|
||||
>
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="text-subtitle-1 py-2">需完成部分</v-card-title>
|
||||
<v-card-title class="text-subtitle-1 py-2">
|
||||
需完成部分
|
||||
</v-card-title>
|
||||
<v-card-text class="pt-0">
|
||||
<v-list border class="mb-2" density="compact" rounded>
|
||||
<v-list
|
||||
border
|
||||
class="mb-2"
|
||||
density="compact"
|
||||
rounded
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(task, index) in dialog.editedItem.tasks"
|
||||
:key="index"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-2" icon="mdi-checkbox-blank-circle-outline" size="small"/>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-checkbox-blank-circle-outline"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<v-text-field
|
||||
v-model="dialog.editedItem.tasks[index]"
|
||||
@@ -258,7 +320,7 @@
|
||||
hide-details
|
||||
variant="plain"
|
||||
/>
|
||||
<template v-slot:append>
|
||||
<template #append>
|
||||
<v-btn
|
||||
color="error"
|
||||
icon="mdi-delete"
|
||||
@@ -287,7 +349,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="elevated"
|
||||
|
||||
@@ -1,36 +1,60 @@
|
||||
<template>
|
||||
<settings-card :loading="loading" icon="mdi-database-edit" title="KV数据库管理">
|
||||
<settings-card
|
||||
:loading="loading"
|
||||
icon="mdi-database-edit"
|
||||
title="KV数据库管理"
|
||||
>
|
||||
<v-list>
|
||||
<!-- 数据库连接状态 -->
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon :color="connectionColor" :icon="connectionIcon" class="mr-3"/>
|
||||
<v-icon
|
||||
:color="connectionColor"
|
||||
:icon="connectionIcon"
|
||||
class="mr-3"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>数据库状态</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ connectionStatus }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn :loading="loading" variant="tonal" @click="refreshConnection">
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
variant="tonal"
|
||||
@click="refreshConnection"
|
||||
>
|
||||
刷新
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-format-list-bulleted"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-format-list-bulleted"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>数据条目</v-list-item-title>
|
||||
<v-list-item-subtitle>共 {{ kvData.length }} 条记录</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn-group variant="tonal">
|
||||
<v-btn :loading="loadingData" @click="loadKvData">
|
||||
<v-btn
|
||||
:loading="loadingData"
|
||||
@click="loadKvData"
|
||||
>
|
||||
加载数据
|
||||
</v-btn>
|
||||
<v-btn :disabled="!isKvProvider" @click="createNewItem">
|
||||
<v-icon class="mr-1" icon="mdi-plus"/>
|
||||
<v-btn
|
||||
:disabled="!isKvProvider"
|
||||
@click="createNewItem"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-plus"
|
||||
/>
|
||||
新建
|
||||
</v-btn>
|
||||
</v-btn-group>
|
||||
@@ -39,11 +63,18 @@
|
||||
</v-list>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<v-card v-if="kvData.length > 0" class="mt-4" variant="outlined">
|
||||
<v-card
|
||||
v-if="kvData.length > 0"
|
||||
class="mt-4"
|
||||
variant="outlined"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-table"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-table"
|
||||
/>
|
||||
KV数据列表
|
||||
<v-spacer/>
|
||||
<v-spacer />
|
||||
<v-text-field
|
||||
v-model="searchQuery"
|
||||
clearable
|
||||
@@ -69,7 +100,10 @@
|
||||
</template>
|
||||
|
||||
<template #[`item.actions`]="{ item }">
|
||||
<v-btn-group density="compact" variant="text">
|
||||
<v-btn-group
|
||||
density="compact"
|
||||
variant="text"
|
||||
>
|
||||
<v-btn
|
||||
icon="mdi-eye"
|
||||
size="small"
|
||||
@@ -102,13 +136,23 @@
|
||||
</v-card>
|
||||
|
||||
<!-- 查看数据对话框 -->
|
||||
<v-dialog v-model="viewDialog" max-width="800px">
|
||||
<v-dialog
|
||||
v-model="viewDialog"
|
||||
max-width="800px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-eye"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-eye"
|
||||
/>
|
||||
查看数据
|
||||
<v-spacer/>
|
||||
<v-btn icon="mdi-close" variant="text" @click="viewDialog = false"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="viewDialog = false"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-subtitle v-if="selectedItem">
|
||||
@@ -128,12 +172,21 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-btn variant="tonal" @click="copyToClipboard(selectedItem?.value)">
|
||||
<v-icon class="mr-1" icon="mdi-content-copy"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="tonal"
|
||||
@click="copyToClipboard(selectedItem?.value)"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-content-copy"
|
||||
/>
|
||||
复制数据
|
||||
</v-btn>
|
||||
<v-btn variant="text" @click="viewDialog = false">
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="viewDialog = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -141,13 +194,23 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 编辑数据对话框 -->
|
||||
<v-dialog v-model="editDialog" max-width="800px">
|
||||
<v-dialog
|
||||
v-model="editDialog"
|
||||
max-width="800px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-pencil"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-pencil"
|
||||
/>
|
||||
编辑数据
|
||||
<v-spacer/>
|
||||
<v-btn icon="mdi-close" variant="text" @click="closeEditDialog"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="closeEditDialog"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-subtitle v-if="editingItem">
|
||||
@@ -167,8 +230,11 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-btn variant="text" @click="closeEditDialog">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="closeEditDialog"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@@ -185,13 +251,23 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 新建数据对话框 -->
|
||||
<v-dialog v-model="createDialog" max-width="800px">
|
||||
<v-dialog
|
||||
v-model="createDialog"
|
||||
max-width="800px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-plus"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-plus"
|
||||
/>
|
||||
新建数据
|
||||
<v-spacer/>
|
||||
<v-btn icon="mdi-close" variant="text" @click="closeCreateDialog"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="closeCreateDialog"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
@@ -211,15 +287,18 @@
|
||||
:error-messages="isValidNewJson ? [] : ['请输入有效的JSON格式']"
|
||||
class="font-monospace"
|
||||
label="数据内容 (JSON格式)"
|
||||
placeholder='请输入JSON数据,如:{"name": "value"}'
|
||||
placeholder="请输入JSON数据,如:{"name": "value"}"
|
||||
rows="15"
|
||||
variant="outlined"
|
||||
/>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-btn variant="text" @click="closeCreateDialog">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="closeCreateDialog"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@@ -236,13 +315,23 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 云端地址对话框 -->
|
||||
<v-dialog v-model="cloudUrlDialog" max-width="800px">
|
||||
<v-dialog
|
||||
v-model="cloudUrlDialog"
|
||||
max-width="800px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-cloud-download"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-cloud-download"
|
||||
/>
|
||||
获取云端访问地址
|
||||
<v-spacer/>
|
||||
<v-btn icon="mdi-close" variant="text" @click="cloudUrlDialog = false"/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="cloudUrlDialog = false"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-subtitle v-if="selectedCloudItem">
|
||||
@@ -250,19 +339,43 @@
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-alert v-if="cloudUrlError" class="mb-4" type="error" variant="tonal">
|
||||
<v-alert
|
||||
v-if="cloudUrlError"
|
||||
class="mb-4"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ cloudUrlError }}
|
||||
</v-alert>
|
||||
|
||||
<v-alert v-if="cloudUrlResult && cloudUrlResult.success" class="mb-4" type="success" variant="tonal">
|
||||
<v-alert
|
||||
v-if="cloudUrlResult && cloudUrlResult.success"
|
||||
class="mb-4"
|
||||
type="success"
|
||||
variant="tonal"
|
||||
>
|
||||
<v-alert-title>云端地址获取成功</v-alert-title>
|
||||
<div class="mt-2">
|
||||
<div v-if="cloudUrlResult.migrated" class="mb-2">
|
||||
<v-icon class="mr-1" color="success" icon="mdi-database-arrow-up"/>
|
||||
<div
|
||||
v-if="cloudUrlResult.migrated"
|
||||
class="mb-2"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
color="success"
|
||||
icon="mdi-database-arrow-up"
|
||||
/>
|
||||
数据已从本地迁移到云端
|
||||
</div>
|
||||
<div v-if="cloudUrlResult.configured" class="mb-2">
|
||||
<v-icon class="mr-1" color="info" icon="mdi-cog"/>
|
||||
<div
|
||||
v-if="cloudUrlResult.configured"
|
||||
class="mb-2"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
color="info"
|
||||
icon="mdi-cog"
|
||||
/>
|
||||
云端配置已自动设置
|
||||
</div>
|
||||
</div>
|
||||
@@ -279,10 +392,16 @@
|
||||
@click:append-inner="copyCloudUrl"
|
||||
/>
|
||||
|
||||
<v-expansion-panels v-if="cloudUrlResult && cloudUrlResult.url" class="mt-4">
|
||||
<v-expansion-panels
|
||||
v-if="cloudUrlResult && cloudUrlResult.url"
|
||||
class="mt-4"
|
||||
>
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon class="mr-2" icon="mdi-cog"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-cog"
|
||||
/>
|
||||
高级选项
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
@@ -303,7 +422,10 @@
|
||||
variant="tonal"
|
||||
@click="refreshCloudUrl"
|
||||
>
|
||||
<v-icon class="mr-1" icon="mdi-refresh"/>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-refresh"
|
||||
/>
|
||||
重新获取
|
||||
</v-btn>
|
||||
</v-expansion-panel-text>
|
||||
@@ -312,8 +434,11 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-btn variant="text" @click="cloudUrlDialog = false">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="cloudUrlDialog = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@@ -322,7 +447,10 @@
|
||||
variant="tonal"
|
||||
@click="openCloudUrl"
|
||||
>
|
||||
<v-icon class="mr-1" icon="mdi-open-in-new"/>
|
||||
<v-icon
|
||||
class="mr-1"
|
||||
icon="mdi-open-in-new"
|
||||
/>
|
||||
在新窗口打开
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -330,24 +458,37 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
<v-dialog v-model="deleteDialog" max-width="400px">
|
||||
<v-dialog
|
||||
v-model="deleteDialog"
|
||||
max-width="400px"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center text-error">
|
||||
<v-icon class="mr-2" icon="mdi-alert"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-alert"
|
||||
/>
|
||||
确认删除
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
确定要删除键名为 <code>{{ itemToDelete?.key }}</code> 的数据吗?
|
||||
<br><br>
|
||||
<v-alert class="mt-2" type="warning" variant="tonal">
|
||||
<v-alert
|
||||
class="mt-2"
|
||||
type="warning"
|
||||
variant="tonal"
|
||||
>
|
||||
此操作不可撤销,请谨慎操作!
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer/>
|
||||
<v-btn variant="text" @click="deleteDialog = false">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="deleteDialog = false"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
<template>
|
||||
<settings-card icon="mdi-cog" title="编辑设置">
|
||||
<settings-card
|
||||
icon="mdi-cog"
|
||||
title="编辑设置"
|
||||
>
|
||||
<v-list>
|
||||
<setting-item setting-key="randomPicker.enabled"/>
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item setting-key="randomPicker.enabled" />
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<setting-item setting-key="randomPicker.mode"/>
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item setting-key="randomPicker.minNumber"/>
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item setting-key="randomPicker.maxNumber"/>
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item setting-key="randomPicker.defaultCount"/>
|
||||
|
||||
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item setting-key="randomPicker.animation"/>
|
||||
<setting-item setting-key="randomPicker.mode" />
|
||||
<v-divider class="my-2" />
|
||||
<setting-item setting-key="randomPicker.minNumber" />
|
||||
<v-divider class="my-2" />
|
||||
<setting-item setting-key="randomPicker.maxNumber" />
|
||||
<v-divider class="my-2" />
|
||||
<setting-item setting-key="randomPicker.defaultCount" />
|
||||
|
||||
|
||||
<v-divider class="my-2" />
|
||||
<setting-item setting-key="randomPicker.animation" />
|
||||
</v-list>
|
||||
</settings-card>
|
||||
</template>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<template>
|
||||
<settings-card icon="mdi-refresh-circle" title="刷新设置">
|
||||
<settings-card
|
||||
icon="mdi-refresh-circle"
|
||||
title="刷新设置"
|
||||
>
|
||||
<v-form>
|
||||
<v-list>
|
||||
<setting-item setting-key="refresh.auto" title="自动刷新"/>
|
||||
<v-divider class="my-2"/>
|
||||
<setting-item
|
||||
setting-key="refresh.auto"
|
||||
title="自动刷新"
|
||||
/>
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<setting-item setting-key="refresh.interval" title="刷新间隔"/>
|
||||
<setting-item
|
||||
setting-key="refresh.interval"
|
||||
title="刷新间隔"
|
||||
/>
|
||||
</v-list>
|
||||
|
||||
</v-form>
|
||||
</settings-card>
|
||||
</template>
|
||||
|
||||
@@ -58,10 +58,16 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加新科目 -->
|
||||
<v-card class="mb-4" variant="outlined">
|
||||
<v-card
|
||||
class="mb-4"
|
||||
variant="outlined"
|
||||
>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="newSubjectName"
|
||||
:rules="[v => !!v || '科目名称不能为空']"
|
||||
@@ -85,7 +91,7 @@
|
||||
v-for="(subject, index) in subjects"
|
||||
:key="subject.order"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<template #prepend>
|
||||
<div class="d-flex flex-column align-center mr-2">
|
||||
<v-btn
|
||||
:disabled="index === 0"
|
||||
@@ -114,7 +120,7 @@
|
||||
/>
|
||||
</v-list-item-title>
|
||||
|
||||
<template v-slot:append>
|
||||
<template #append>
|
||||
<v-btn
|
||||
color="error"
|
||||
icon="mdi-delete"
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<settings-card icon="mdi-palette" title="主题设置">
|
||||
<settings-card
|
||||
icon="mdi-palette"
|
||||
title="主题设置"
|
||||
>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-3" icon="mdi-theme-light-dark"/>
|
||||
<v-icon
|
||||
class="mr-3"
|
||||
icon="mdi-theme-light-dark"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item-title>主题模式</v-list-item-title>
|
||||
<v-list-item-subtitle>选择明亮或暗黑主题</v-list-item-subtitle>
|
||||
@@ -14,11 +20,17 @@
|
||||
density="comfortable"
|
||||
>
|
||||
<v-btn value="light">
|
||||
<v-icon class="mr-2" icon="mdi-white-balance-sunny"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-white-balance-sunny"
|
||||
/>
|
||||
明亮
|
||||
</v-btn>
|
||||
<v-btn value="dark">
|
||||
<v-icon class="mr-2" icon="mdi-moon-waning-crescent"/>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
icon="mdi-moon-waning-crescent"
|
||||
/>
|
||||
暗黑
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
@@ -37,6 +49,11 @@ export default {
|
||||
name: 'ThemeSettingsCard',
|
||||
components: {SettingsCard},
|
||||
|
||||
setup() {
|
||||
const theme = useTheme();
|
||||
return {theme};
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
localTheme: getSetting('theme.mode')
|
||||
@@ -50,11 +67,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
const theme = useTheme();
|
||||
return {theme};
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateTheme(mode) {
|
||||
this.theme.global.name.value = mode;
|
||||
|
||||
Reference in New Issue
Block a user