Revert unrelated linting changes, keep only HitokotoCard modifications

Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-24 12:33:02 +00:00
co-authored by Sunwuyuan
parent bf56de8299
commit 7bbae7fd73
63 changed files with 1182 additions and 3353 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
<template>
<v-app-bar :elevation="2">
<template #prepend>
<v-app-bar-nav-icon />
<template v-slot:prepend>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
</template>
<v-app-bar-title>作业</v-app-bar-title>
+13 -56
View File
@@ -2,85 +2,48 @@
<v-card>
<v-card-title class="d-flex align-center">
<span>缓存管理</span>
<v-spacer />
<v-btn
:loading="loading"
color="error"
@click="clearAllCaches"
>
<v-spacer></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 #append>
<v-btn
color="error"
icon
size="small"
@click="clearUrl(cache.name, url)"
>
<template v-slot:append>
<v-btn color="error" icon size="small" @click="clearUrl(cache.name, url)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
@@ -90,15 +53,9 @@
</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>
+125 -308
View File
@@ -11,9 +11,7 @@
@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>
@@ -29,9 +27,7 @@
@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>
@@ -47,49 +43,29 @@
<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 #prepend>
<v-icon
color="warning"
size="small"
>
mdi-circle-small
</v-icon>
<template v-slot: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 class="mx-auto" type="article"></v-skeleton-loader>
</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"
@@ -153,31 +129,19 @@
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
@@ -186,14 +150,8 @@
>
{{ 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>
@@ -209,29 +167,20 @@
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"
<v-icon class="mr-2" color="success" size="small"
>mdi-clock-start
</v-icon
>
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">
@@ -240,13 +189,10 @@
</div>
<div>
<div class="d-flex align-center mb-1">
<v-icon
class="mr-2"
color="error"
size="small"
<v-icon class="mr-2" color="error" size="small"
>mdi-clock-end
</v-icon
>
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">
@@ -258,50 +204,29 @@
</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>
配置预览
<v-spacer />
<v-spacer></v-spacer>
<v-btn
color="white"
@@ -319,17 +244,12 @@
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>{{ formattedStorageJson }}</code></pre>
</v-card>
</v-card-text>
@@ -340,23 +260,14 @@
<!-- 编辑模式 -->
<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 bg-primary-lighten-5 pa-4">
<v-icon class="mr-2">
mdi-information
</v-icon>
<v-icon class="mr-2">mdi-information</v-icon>
<span class="font-weight-bold">基本信息</span>
</v-card-title>
<v-card-text class="pa-6">
<v-row>
<v-col
cols="12"
md="6"
>
<v-col cols="12" md="6">
<v-text-field
v-model="localConfig.examName"
:rules="[
@@ -371,12 +282,9 @@
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"
clearable
@@ -385,16 +293,13 @@
placeholder="如:一号考场"
prepend-inner-icon="mdi-home"
variant="outlined"
/>
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<span class="text-subtitle-2 font-weight-bold d-block mb-2">
<v-icon
size="small"
class="mr-1"
>mdi-message-text</v-icon>
<v-icon size="small" class="mr-1">mdi-message-text</v-icon>
考试提示
</span>
<v-textarea
@@ -410,13 +315,10 @@
placeholder="例如:请保持卷面整洁,诚信应考。在听到终考铃时立刻停止作答。"
rows="3"
variant="outlined"
/>
></v-textarea>
<!-- 默认提示选项 -->
<div
v-if="!localConfig.message || localConfig.message.trim() === ''"
class="mt-3"
>
<div v-if="!localConfig.message || localConfig.message.trim() === ''" class="mt-3">
<v-chip-group
class="d-flex gap-2"
column
@@ -430,22 +332,12 @@
variant="outlined"
@click="selectDefaultTip(tip)"
>
<v-icon
size="small"
start
>
mdi-plus
</v-icon>
<v-icon size="small" start>mdi-plus</v-icon>
{{ tip.substring(0, 20) }}...
</v-chip>
</v-chip-group>
<div class="text-caption text-medium-emphasis mt-2 ml-2">
<v-icon
class="mr-1"
size="x-small"
>
mdi-lightbulb-outline
</v-icon>
<v-icon class="mr-1" size="x-small">mdi-lightbulb-outline</v-icon>
点击上方选项快速添加常用考试提示
</div>
</div>
@@ -455,17 +347,11 @@
</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 bg-success-lighten-5 pa-4">
<v-icon class="mr-2">
mdi-format-list-bulleted
</v-icon>
<v-icon class="mr-2">mdi-format-list-bulleted</v-icon>
<span class="font-weight-bold">考试科目安排</span>
<v-spacer />
<v-spacer></v-spacer>
<!-- 提醒时间开关 -->
<div class="d-flex align-center mr-4">
@@ -476,7 +362,7 @@
hide-details
@change="toggleAlertTimeMode"
>
<template #label>
<template v-slot:label>
<span class="text-body-2">自定义提醒时间</span>
</template>
</v-switch>
@@ -511,19 +397,14 @@
variant="tonal"
class="mr-3"
>
<v-icon
start
size="small"
>
mdi-numeric-{{ index + 1 }}-circle
</v-icon>
<v-icon start size="small">mdi-numeric-{{ index + 1 }}-circle</v-icon>
第 {{ index + 1 }} 科目
</v-chip>
<!-- 考试时长显示 -->
<v-spacer />
<v-spacer></v-spacer>
<div class="d-flex gap-1">
<v-btn
v-if="index > 0"
@@ -533,12 +414,7 @@
variant="text"
@click="moveExamInfo(index, -1)"
>
<v-tooltip
activator="parent"
location="bottom"
>
上移
</v-tooltip>
<v-tooltip activator="parent" location="bottom">上移</v-tooltip>
</v-btn>
<v-btn
v-if="index < localConfig.examInfos.length - 1"
@@ -548,12 +424,7 @@
variant="text"
@click="moveExamInfo(index, 1)"
>
<v-tooltip
activator="parent"
location="bottom"
>
下移
</v-tooltip>
<v-tooltip activator="parent" location="bottom">下移</v-tooltip>
</v-btn>
<v-btn
color="error"
@@ -562,21 +433,13 @@
variant="text"
@click="removeExamInfo(index)"
>
<v-tooltip
activator="parent"
location="bottom"
>
删除
</v-tooltip>
<v-tooltip activator="parent" location="bottom">删除</v-tooltip>
</v-btn>
</div>
</div>
<v-row class="align-start">
<v-col
cols="12"
md="4"
>
<v-col cols="12" md="4">
<!-- 科目名称自动完成选择器 -->
<v-autocomplete
v-model="examInfo.name"
@@ -593,23 +456,20 @@
prepend-inner-icon="mdi-book"
variant="outlined"
>
<template #prepend-item>
<template v-slot:prepend-item>
<v-list-item
v-if="customSubjectInput"
title="自定义:"
>
<template #append>
<template v-slot:append>
<span class="text-primary font-weight-bold">{{ customSubjectInput }}</span>
</template>
</v-list-item>
<v-divider v-if="customSubjectInput" />
<v-divider v-if="customSubjectInput"></v-divider>
</template>
</v-autocomplete>
</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"
@@ -617,7 +477,7 @@
offset-y
transition="scale-transition"
>
<template #activator="{ props }">
<template v-slot:activator="{ props }">
<v-text-field
v-model="examInfo.startFormatted"
:rules="[
@@ -633,27 +493,19 @@
variant="outlined"
@blur="updateStartDateTimeFromInput(index)"
>
<template #append>
<template v-slot:append>
<v-icon>mdi-calendar-clock</v-icon>
</template>
</v-text-field>
</template>
<v-card min-width="500">
<v-card-title class="text-center py-3 bg-primary-lighten-5">
<v-icon
class="mr-2"
color="primary"
>
mdi-clock-start
</v-icon>
<v-icon class="mr-2" color="primary">mdi-clock-start</v-icon>
选择开始时间
</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"
@@ -661,7 +513,7 @@
locale="zh-cn"
show-adjacent-months
@update:model-value="updateStartDateTime(index)"
/>
></v-date-picker>
</v-col>
<v-col cols="6">
<v-time-picker
@@ -671,12 +523,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"
@@ -688,10 +540,7 @@
</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"
@@ -699,7 +548,7 @@
offset-y
transition="scale-transition"
>
<template #activator="{ props }">
<template v-slot:activator="{ props }">
<v-text-field
v-model="examInfo.endFormatted"
:rules="[
@@ -716,27 +565,19 @@
variant="outlined"
@blur="updateEndDateTimeFromInput(index)"
>
<template #append>
<template v-slot:append>
<v-icon>mdi-calendar-clock</v-icon>
</template>
</v-text-field>
</template>
<v-card min-width="500">
<v-card-title class="text-center py-3 bg-error-lighten-5">
<v-icon
class="mr-2"
color="error"
>
mdi-clock-end
</v-icon>
<v-icon class="mr-2" color="error">mdi-clock-end</v-icon>
选择结束时间
</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="error"
@@ -744,7 +585,7 @@
locale="zh-cn"
show-adjacent-months
@update:model-value="updateEndDateTime(index)"
/>
></v-date-picker>
</v-col>
<v-col cols="6">
<v-time-picker
@@ -754,12 +595,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"
@@ -771,76 +612,60 @@
</v-card>
</v-menu>
</v-col>
<v-col
cols="12"
md="2"
>
<v-text-field
v-model="examInfo.durationMinutes"
:rules="[
(v) => !!v || '时长不能为空',
(v) => !isNaN(v) || '时长必须是数字',
(v) => parseInt(v) > 0 || '时长必须大于0',
(v) => parseInt(v) <= 1440 || '时长不能超过1440分钟(24小时)'
]"
type="number"
min="1"
max="1440"
density="comfortable"
label="时长(分钟)"
placeholder="例如:120"
prepend-inner-icon="mdi-timer"
variant="outlined"
:hint="durationHint(examInfo)"
persistent-hint
@blur="updateDurationFromInput(index)"
/>
</v-col>
<v-col cols="12" md="2">
<v-text-field
v-model="examInfo.durationMinutes"
:rules="[
(v) => !!v || '时长不能为空',
(v) => !isNaN(v) || '时长必须是数字',
(v) => parseInt(v) > 0 || '时长必须大于0',
(v) => parseInt(v) <= 1440 || '时长不能超过1440分钟(24小时)'
]"
type="number"
min="1"
max="1440"
density="comfortable"
label="时长(分钟)"
placeholder="例如:120"
prepend-inner-icon="mdi-timer"
variant="outlined"
:hint="durationHint(examInfo)"
persistent-hint
@blur="updateDurationFromInput(index)"
/>
</v-col>
<!-- 提醒时间输入框(仅在启用自定义时显示) -->
<v-col
v-if="enableCustomAlertTime"
cols="12"
md="2"
>
<v-text-field
v-model="examInfo.alertTime"
:rules="[
(v) => !!v || '提醒时间不能为空',
(v) => !isNaN(v) || '提醒时间必须是数字',
(v) => parseInt(v) >= 0 || '提醒时间不能为负数',
(v) => parseInt(v) <= 120 || '提醒时间不能超过120分钟'
]"
type="number"
min="0"
max="120"
density="comfortable"
label="提醒时间(分钟)"
placeholder="例如:15"
prepend-inner-icon="mdi-bell-ring"
variant="outlined"
hint="考试结束前提醒"
persistent-hint
/>
</v-col>
<!-- 提醒时间输入框(仅在启用自定义时显示) -->
<v-col v-if="enableCustomAlertTime" cols="12" md="2">
<v-text-field
v-model="examInfo.alertTime"
:rules="[
(v) => !!v || '提醒时间不能为空',
(v) => !isNaN(v) || '提醒时间必须是数字',
(v) => parseInt(v) >= 0 || '提醒时间不能为负数',
(v) => parseInt(v) <= 120 || '提醒时间不能超过120分钟'
]"
type="number"
min="0"
max="120"
density="comfortable"
label="提醒时间(分钟)"
placeholder="例如:15"
prepend-inner-icon="mdi-bell-ring"
variant="outlined"
hint="考试结束前提醒"
persistent-hint
/>
</v-col>
</v-row>
</div>
</v-list-item>
</v-list>
<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>
@@ -859,18 +684,10 @@
</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>
@@ -878,7 +695,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"
+4 -15
View File
@@ -7,10 +7,7 @@
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="'查看昨天'"
@@ -35,10 +32,7 @@
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
@@ -49,10 +43,7 @@
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"
@@ -97,9 +88,7 @@
size="large"
text="复制作业内容到今天"
@click="$emit('copy-to-today')"
>
复制到今天
</v-btn>
>复制到今天</v-btn>
</v-slide-x-reverse-transition>
</div>
</template>
+4 -18
View File
@@ -8,28 +8,14 @@
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>
+6 -13
View File
@@ -11,16 +11,12 @@
/>
<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">
@@ -33,20 +29,17 @@
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>
+56 -180
View File
@@ -1,26 +1,15 @@
<template>
<div>
<setting-group
title="一言设置"
icon="mdi-comment-quote"
>
<setting-group title="一言设置" icon="mdi-comment-quote">
<setting-item setting-key="hitokoto.enabled" />
<setting-item setting-key="hitokoto.refreshInterval" />
</setting-group>
<setting-group
title="数据源配置"
icon="mdi-cloud-sync"
class="mt-4"
>
<div class="text-caption text-grey px-4 pt-2 pb-2">
以下配置将同步到云端,对所有连接此班级的设备生效。
</div>
<setting-group title="数据源配置" icon="mdi-cloud-sync" class="mt-4">
<div class="text-caption text-grey px-4 pt-2 pb-2">以下配置将同步到云端,对所有连接此班级的设备生效。</div>
<v-list-item>
<v-list-item-title class="mb-2">
启用数据源
</v-list-item-title>
<v-list-item-title class="mb-2">启用数据源</v-list-item-title>
<div class="d-flex flex-wrap gap-2">
<v-checkbox
v-model="kvConfig.sources"
@@ -53,13 +42,7 @@
/>
</div>
<div class="text-caption text-orange mt-2">
<v-icon
size="x-small"
color="orange"
class="mr-1"
>
mdi-alert
</v-icon>
<v-icon size="x-small" color="orange" class="mr-1">mdi-alert</v-icon>
一言(Hitokoto)数据源已收到关于存在负面内容的大量反馈,请用户谨慎启用。
</div>
</v-list-item>
@@ -116,62 +99,39 @@
</v-list-item>
<v-list-item>
<v-checkbox
v-model="enableCloudSensitiveWords"
label="启用云端敏感词列表"
hide-details
v-model="enableCloudSensitiveWords"
density="compact"
disabled
class="mb-2"
/>
<div class="text-caption text-grey">
已启用的数据源将在获取一言时随机尝试,直到成功获取内容为止。<br>
敏感词过滤会将包含任意敏感词的句子过滤掉,避免显示不当内容。<br>
已启用的数据源将在获取一言时随机尝试,直到成功获取内容为止。<br/>
敏感词过滤会将包含任意敏感词的句子过滤掉,避免显示不当内容。<br/>
</div>
</v-list-item>
</v-list-item>
<div
v-if="loading"
class="text-center pb-4"
>
<v-progress-circular
indeterminate
size="24"
color="primary"
/>
<div v-if="loading" class="text-center pb-4">
<v-progress-circular indeterminate size="24" color="primary" />
<span class="ml-2 text-caption">正在同步配置...</span>
</div>
</setting-group>
<v-dialog
v-model="testResultDialog"
max-width="600"
>
<v-card
v-if="testResultData"
class="rounded-lg"
>
<v-dialog v-model="testResultDialog" max-width="600">
<v-card v-if="testResultData" class="rounded-lg">
<v-card-text class="pa-0">
<v-list
lines="two"
class="py-0"
>
<v-list lines="two" class="py-0">
<v-list-item class="px-4 py-3">
<template #prepend>
<v-avatar
color="primary"
variant="tonal"
class="mr-2"
>
<template v-slot:prepend>
<v-avatar color="primary" variant="tonal" class="mr-2">
<v-icon icon="mdi-key-variant" />
</v-avatar>
</template>
<v-list-item-title class="text-subtitle-2 font-weight-bold mb-1">
Token
</v-list-item-title>
<v-list-item-subtitle
class="text-body-2 text-high-emphasis"
style="word-break: break-all;"
>
<v-list-item-title class="text-subtitle-2 font-weight-bold mb-1">Token</v-list-item-title>
<v-list-item-subtitle class="text-body-2 text-high-emphasis" style="word-break: break-all;">
{{ testResultData.data.token }}
</v-list-item-subtitle>
</v-list-item>
@@ -181,36 +141,20 @@
<v-row no-gutters>
<v-col cols="6">
<v-list-item class="px-4 py-2">
<template #prepend>
<v-icon
icon="mdi-ip-network"
color="grey-darken-1"
class="mr-2"
/>
<template v-slot:prepend>
<v-icon icon="mdi-ip-network" color="grey-darken-1" class="mr-2" />
</template>
<v-list-item-title class="text-caption text-grey-darken-1">
IP 地址
</v-list-item-title>
<v-list-item-subtitle class="text-body-2">
{{ testResultData.data.ip }}
</v-list-item-subtitle>
<v-list-item-title class="text-caption text-grey-darken-1">IP 地址</v-list-item-title>
<v-list-item-subtitle class="text-body-2">{{ testResultData.data.ip }}</v-list-item-subtitle>
</v-list-item>
</v-col>
<v-col cols="6">
<v-list-item class="px-4 py-2">
<template #prepend>
<v-icon
icon="mdi-map-marker-radius"
color="grey-darken-1"
class="mr-2"
/>
<template v-slot:prepend>
<v-icon icon="mdi-map-marker-radius" color="grey-darken-1" class="mr-2" />
</template>
<v-list-item-title class="text-caption text-grey-darken-1">
地区
</v-list-item-title>
<v-list-item-subtitle class="text-body-2">
{{ testResultData.data.region }}
</v-list-item-subtitle>
<v-list-item-title class="text-caption text-grey-darken-1">地区</v-list-item-title>
<v-list-item-subtitle class="text-body-2">{{ testResultData.data.region }}</v-list-item-subtitle>
</v-list-item>
</v-col>
</v-row>
@@ -221,123 +165,59 @@
<v-container class="px-4 py-3">
<v-row dense>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-thermometer"
color="orange"
class="mr-2"
/>
<v-icon icon="mdi-thermometer" color="orange" class="mr-2" />
<div>
<div class="text-caption text-grey">
温度
</div>
<div class="text-body-1 font-weight-medium">
{{ testResultData.data.weatherData.temperature }}°C
</div>
<div class="text-caption text-grey">温度</div>
<div class="text-body-1 font-weight-medium">{{ testResultData.data.weatherData.temperature }}°C</div>
</div>
</div>
</v-col>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-weather-cloudy"
color="blue-grey"
class="mr-2"
/>
<v-icon icon="mdi-weather-cloudy" color="blue-grey" class="mr-2" />
<div>
<div class="text-caption text-grey">
天气
</div>
<div class="text-body-1 font-weight-medium">
{{ testResultData.data.weatherData.weather }}
</div>
<div class="text-caption text-grey">天气</div>
<div class="text-body-1 font-weight-medium">{{ testResultData.data.weatherData.weather }}</div>
</div>
</div>
</v-col>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-water-percent"
color="blue"
class="mr-2"
/>
<v-icon icon="mdi-water-percent" color="blue" class="mr-2" />
<div>
<div class="text-caption text-grey">
湿度
</div>
<div class="text-body-1 font-weight-medium">
{{ testResultData.data.weatherData.humidity }}%
</div>
<div class="text-caption text-grey">湿度</div>
<div class="text-body-1 font-weight-medium">{{ testResultData.data.weatherData.humidity }}%</div>
</div>
</div>
</v-col>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-weather-windy"
color="teal"
class="mr-2"
/>
<v-icon icon="mdi-weather-windy" color="teal" class="mr-2" />
<div>
<div class="text-caption text-grey">
风向/风力
</div>
<div class="text-caption text-grey">风向/风力</div>
<div class="text-body-2 font-weight-medium">
{{ testResultData.data.weatherData.windDirection }} {{ testResultData.data.weatherData.windPower }}级
</div>
</div>
</div>
</v-col>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-blur"
color="grey"
class="mr-2"
/>
<v-icon icon="mdi-blur" color="grey" class="mr-2" />
<div>
<div class="text-caption text-grey">
PM2.5
</div>
<div class="text-body-1 font-weight-medium">
{{ testResultData.data.weatherData.pm25 }}
</div>
<div class="text-caption text-grey">PM2.5</div>
<div class="text-body-1 font-weight-medium">{{ testResultData.data.weatherData.pm25 }}</div>
</div>
</div>
</v-col>
<v-col
cols="6"
sm="4"
>
<v-col cols="6" sm="4">
<div class="d-flex align-center mb-2">
<v-icon
icon="mdi-eye"
color="indigo"
class="mr-2"
/>
<v-icon icon="mdi-eye" color="indigo" class="mr-2" />
<div>
<div class="text-caption text-grey">
能见度
</div>
<div class="text-body-1 font-weight-medium">
{{ testResultData.data.weatherData.visibility }}
</div>
<div class="text-caption text-grey">能见度</div>
<div class="text-body-1 font-weight-medium">{{ testResultData.data.weatherData.visibility }}</div>
</div>
</div>
</v-col>
@@ -347,9 +227,7 @@
<v-divider />
<div class="px-4 py-3">
<div class="text-caption text-grey mb-2">
环境标签
</div>
<div class="text-caption text-grey mb-2">环境标签</div>
<div class="d-flex flex-wrap gap-2">
<v-chip
v-for="tag in testResultData.data.tags"
@@ -367,12 +245,8 @@
<v-divider />
<v-list-item class="px-4 py-2">
<template #prepend>
<v-icon
icon="mdi-clock-outline"
size="small"
class="mr-2"
/>
<template v-slot:prepend>
<v-icon icon="mdi-clock-outline" size="small" class="mr-2" />
</template>
<v-list-item-title class="text-caption text-grey-darken-1">
北京时间: {{ new Date(testResultData.data.beijingTime).toLocaleString() }}
@@ -380,6 +254,8 @@
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
</div>
+16 -48
View File
@@ -11,11 +11,7 @@
<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 }}
@@ -35,21 +31,13 @@
/>
<!-- 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'"
@@ -60,10 +48,7 @@
</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"
@@ -80,11 +65,7 @@
<!-- 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'"
@@ -95,10 +76,7 @@
</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"
@@ -114,10 +92,7 @@
</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"
@@ -130,21 +105,14 @@
</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">
@@ -255,11 +223,10 @@
border-color="warning"
prominent
>
<template #prepend />
<template #prepend>
</template>
<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>
@@ -269,6 +236,7 @@
<div class="text-center text-body-2 text-disabled mb-5">
点击空白处完成编辑
</div>
</v-card>
</v-dialog>
</template>
+10 -27
View File
@@ -1,50 +1,33 @@
<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>
+18 -69
View File
@@ -5,32 +5,16 @@
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
@@ -68,29 +52,14 @@
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"
@@ -129,10 +98,7 @@
</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>
@@ -143,11 +109,8 @@
<div class="mt-4 text-caption">
当前可抽取学生: {{ filteredStudents.length }}人
<v-tooltip
v-if="pickerMode === 'name'"
location="bottom"
>
<template #activator="{ props }">
<v-tooltip v-if="pickerMode === 'name'" location="bottom">
<template v-slot:activator="{ props }">
<v-icon
class="ml-1"
icon="mdi-information-outline"
@@ -169,10 +132,7 @@
</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'"
@@ -205,14 +165,8 @@
</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"
@@ -231,13 +185,8 @@
</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"
+10 -32
View File
@@ -1,39 +1,22 @@
<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">{{
@@ -44,11 +27,12 @@
{{ 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>
@@ -58,14 +42,8 @@
</v-card-text>
<v-card-actions class="pa-4 pt-0">
<v-spacer />
<v-btn
color="primary"
variant="tonal"
@click="close"
>
我知道了
</v-btn>
<v-spacer></v-spacer>
<v-btn color="primary" variant="tonal" @click="close"> 我知道了</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
+1 -1
View File
@@ -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">
+5 -13
View File
@@ -1,8 +1,5 @@
<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
@@ -11,9 +8,7 @@
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>
@@ -23,14 +18,11 @@
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>
+68 -159
View File
@@ -1,16 +1,9 @@
<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>
@@ -20,23 +13,15 @@
</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="设备的命名空间标识符"
@@ -47,10 +32,7 @@
variant="outlined"
/>
</v-col>
<v-col
cols="12"
md="6"
>
<v-col cols="12" md="6">
<v-text-field
v-model="preconfigForm.authCode"
hint="留空则需要用户手动输入"
@@ -82,19 +64,9 @@
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
@@ -103,27 +75,12 @@
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
@@ -131,12 +88,8 @@
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 ? "自动认证" : "手动认证" }}
@@ -146,14 +99,9 @@
</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>
@@ -204,10 +152,7 @@
<!-- 选择摘要 -->
<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>
@@ -238,9 +183,7 @@
<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>
@@ -318,14 +261,9 @@
</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>
@@ -379,20 +317,10 @@
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
@@ -400,25 +328,11 @@
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>
@@ -427,13 +341,8 @@
</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>
@@ -600,45 +509,6 @@ 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: {
/**
* 处理表格选择变化
@@ -912,5 +782,44 @@ 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>
+1 -1
View File
@@ -43,7 +43,7 @@
>
稍后设置
</v-btn>
<v-spacer />
<v-spacer/>
<v-btn
:disabled="!selectedName || saving"
:loading="saving"
+9 -44
View File
@@ -17,10 +17,7 @@
</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
@@ -29,12 +26,7 @@
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
@@ -43,12 +35,7 @@
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
@@ -57,33 +44,18 @@
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>
@@ -116,15 +88,8 @@
<!-- 操作按钮 -->
<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>
+37 -83
View File
@@ -28,6 +28,8 @@
<v-row>
<v-col cols="12">
<v-card>
<v-card-text>
<v-form>
<v-row>
@@ -40,14 +42,15 @@
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
@@ -78,6 +81,8 @@
</v-btn>
<v-spacer />
</v-card-actions>
</v-card>
</v-col>
@@ -88,16 +93,11 @@
<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,25 +108,14 @@
:subtitle="formatTime(item.timestamp)"
lines="two"
>
<template #prepend>
<template v-slot:prepend>
<v-icon :color="item.isUrgent ? 'error' : 'primary'">
{{ item.isUrgent ? 'mdi-alert-circle' : 'mdi-information' }}
</v-icon>
</template>
<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-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>
</v-list-item>
</v-list>
@@ -212,14 +201,17 @@
>
<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>
@@ -234,6 +226,7 @@
>
<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">
@@ -245,9 +238,9 @@
</div>
</v-card-text>
</v-card>
</div>
<div v-else>
<v-card
<div v-else> <v-card
color="info-lighten-4"
variant="outlined"
@@ -256,10 +249,9 @@
title="无设备在线"
>
<v-card-text>
如果数秒后任然显示这个提示,则可能没有任何设备在线接收通知。
如果数秒后任然显示这个提示,则可能没有任何设备在线接收通知。
</v-card-text>
</v-card>
</div>
</v-card></div>
</v-col>
</v-row>
</v-card-text>
@@ -274,22 +266,12 @@
<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-btn
icon="mdi-close"
@click="editDialog = false"
/>
<v-spacer></v-spacer>
<v-btn icon="mdi-close" @click="editDialog = false"></v-btn>
</v-toolbar>
<v-card-text>
<v-form>
@@ -298,66 +280,38 @@
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-btn
variant="text"
@click="editDialog = false"
>
取消
</v-btn>
<v-btn
color="primary"
:loading="savingEdit"
@click="saveEdit"
>
保存
</v-btn>
<v-spacer></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-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-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>
@@ -8,35 +8,19 @@
>
<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
@@ -144,10 +128,7 @@
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">
@@ -156,13 +137,11 @@
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">
@@ -204,19 +183,10 @@
</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"
@@ -262,13 +232,8 @@
<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,7 +45,8 @@
: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>
@@ -59,7 +60,8 @@
: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>
@@ -73,7 +75,8 @@
: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"
+7 -3
View File
@@ -41,7 +41,9 @@
label="命名空间"
prepend-inner-icon="mdi-identifier"
variant="outlined"
/>
>
</v-text-field>
<v-text-field
v-model="form.password"
@@ -49,7 +51,9 @@
prepend-inner-icon="mdi-lock-outline"
type="text"
variant="outlined"
/>
>
</v-text-field>
<v-alert
v-if="error"
@@ -73,7 +77,7 @@
>
取消
</v-btn>
<v-spacer />
<v-spacer/>
<v-btn
:disabled="!form.namespace || authenticating"
:loading="authenticating"
+3 -1
View File
@@ -139,6 +139,8 @@
</div>
</div>
</v-card>
</div>
<!-- 步骤 3: 询问使用场景 -->
@@ -551,7 +553,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
+1 -1
View File
@@ -21,7 +21,7 @@
</v-alert>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-spacer/>
<v-btn
v-if="showCancel"
variant="text"
+9 -24
View File
@@ -1,26 +1,15 @@
<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">
@@ -32,19 +21,15 @@
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
@@ -85,7 +70,7 @@
rounded="lg"
title="返回上一页"
variant="text"
@click="$router.back()"
@click="this.$router.back()"
>
<v-overlay
contained
+6 -24
View File
@@ -11,19 +11,13 @@
<span class="text-truncate">{{ exam?.examName || "加载中..." }}</span>
</v-card-title>
<v-card-text
class="flex-grow-1 pa-4 overflow-y-auto"
:style="contentStyle"
>
<div
v-if="loading"
class="d-flex justify-center align-center py-4"
>
<v-card-text class="flex-grow-1 pa-4 overflow-y-auto" :style="contentStyle">
<div v-if="loading" class="d-flex justify-center align-center py-4">
<v-progress-circular
indeterminate
size="24"
color="primary"
/>
></v-progress-circular>
</div>
<template v-else-if="exam">
@@ -32,11 +26,7 @@
</div>-->
<div class="d-flex flex-column">
<div
v-for="(group, gIndex) in groupedExamInfos"
:key="gIndex"
class="mb-3"
>
<div v-for="(group, gIndex) in groupedExamInfos" :key="gIndex" class="mb-3">
<div class="text-subtitle-2 font-weight-bold text-primary mb-1">
<RelativeTimeDisplay :time="group.date" />
</div>
@@ -49,10 +39,7 @@
'text-grey': isPast(info.end),
}"
>
<div
class="font-weight-bold mr-2"
style="font-size: 1.1em"
>
<div class="font-weight-bold mr-2" style="font-size: 1.1em">
{{ info.name }}
</div>
<div
@@ -67,12 +54,7 @@
</div>
</template>
<div
v-else
class="text-center text-caption text-grey py-2"
>
无法加载
</div>
<div v-else class="text-center text-caption text-grey py-2">无法加载</div>
</v-card-text>
</v-card>
</template>
+18 -70
View File
@@ -1,35 +1,15 @@
<template>
<v-card
class="fill-height d-flex flex-column rounded-xl"
elevation="2"
>
<v-card class="fill-height d-flex flex-column rounded-xl" elevation="2">
<v-card-title class="d-flex align-center py-3 px-4 bg-primary text-white">
<v-icon class="mr-2">
mdi-calendar-clock
</v-icon>
<v-icon class="mr-2">mdi-calendar-clock</v-icon>
<span class="text-truncate">{{ exam?.examName || '加载中...' }}</span>
<v-spacer />
<v-btn
icon="mdi-close"
variant="text"
density="comfortable"
@click="$emit('close')"
/>
<v-spacer></v-spacer>
<v-btn icon="mdi-close" variant="text" density="comfortable" @click="$emit('close')"></v-btn>
</v-card-title>
<v-card-text
class="flex-grow-1 pa-4 overflow-y-auto"
:style="contentStyle"
>
<div
v-if="loading"
class="d-flex justify-center align-center fill-height"
style="min-height: 200px;"
>
<v-progress-circular
indeterminate
color="primary"
/>
<v-card-text class="flex-grow-1 pa-4 overflow-y-auto" :style="contentStyle">
<div v-if="loading" class="d-flex justify-center align-center fill-height" style="min-height: 200px;">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</div>
<template v-else-if="exam">
@@ -44,24 +24,12 @@
{{ exam.message }}
</v-alert>
<v-list
density="comfortable"
class="pa-0 bg-transparent"
>
<v-list-item
v-for="(info, index) in exam.examInfos"
:key="index"
class="px-0 mb-3"
>
<v-list density="comfortable" class="pa-0 bg-transparent">
<v-list-item v-for="(info, index) in exam.examInfos" :key="index" class="px-0 mb-3">
<template #prepend>
<v-avatar
color="primary"
variant="tonal"
size="large"
class="mr-3 font-weight-bold elevation-1"
>
{{ info.name.charAt(0) }}
</v-avatar>
<v-avatar color="primary" variant="tonal" size="large" class="mr-3 font-weight-bold elevation-1">
{{ info.name.charAt(0) }}
</v-avatar>
</template>
<v-list-item-title class="font-weight-bold text-h6 mb-1">
@@ -70,40 +38,20 @@
<v-list-item-subtitle class="text-body-1">
<div class="d-flex align-center mb-1">
<v-icon
size="small"
color="success"
class="mr-2"
>
mdi-clock-start
</v-icon>
<span class="font-weight-medium">{{ formatTime(info.start) }}</span>
<v-icon size="small" color="success" class="mr-2">mdi-clock-start</v-icon>
<span class="font-weight-medium">{{ formatTime(info.start) }}</span>
</div>
<div class="d-flex align-center">
<v-icon
size="small"
color="error"
class="mr-2"
>
mdi-clock-end
</v-icon>
<span class="font-weight-medium">{{ formatTime(info.end) }}</span>
<v-icon size="small" color="error" class="mr-2">mdi-clock-end</v-icon>
<span class="font-weight-medium">{{ formatTime(info.end) }}</span>
</div>
</v-list-item-subtitle>
</v-list-item>
</v-list>
</template>
<div
v-else
class="d-flex flex-column align-center justify-center fill-height text-grey mt-4"
>
<v-icon
size="large"
class="mb-2"
>
mdi-alert-circle-outline
</v-icon>
<div v-else class="d-flex flex-column align-center justify-center fill-height text-grey mt-4">
<v-icon size="large" class="mb-2">mdi-alert-circle-outline</v-icon>
无法加载考试信息
</div>
</v-card-text>
+2 -11
View File
@@ -10,12 +10,7 @@
>
上传
</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
@@ -91,11 +86,7 @@
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">
+29 -111
View File
@@ -1,8 +1,5 @@
<template>
<div
ref="gridContainer"
class="grid-masonry"
>
<div ref="gridContainer" class="grid-masonry">
<TransitionGroup name="grid">
<div
v-for="item in sortedItems"
@@ -15,18 +12,12 @@
class="grid-item"
>
<!-- 一言卡片 -->
<div
v-if="item.type === 'hitokoto'"
style="height: 100%"
>
<div v-if="item.type === 'hitokoto'" style="height: 100%">
<hitokoto-card />
</div>
<!-- 考试卡片 -->
<div
v-else-if="item.type === 'exam'"
style="height: 100%"
>
<div v-else-if="item.type === 'exam'" style="height: 100%">
<concise-exam-card
:exam-id="item.data.examId"
:content-style="contentStyle"
@@ -46,11 +37,7 @@
@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>
@@ -59,78 +46,36 @@
<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>
@@ -139,8 +84,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"
>
@@ -161,11 +106,7 @@
@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">
@@ -203,10 +144,7 @@
<!-- 单独显示空科目 -->
<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"
@@ -215,37 +153,24 @@
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"
@@ -258,15 +183,8 @@
{{ 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>
+30 -118
View File
@@ -1,46 +1,25 @@
<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
@@ -78,11 +57,9 @@
</v-btn>
</div>
<v-divider class="mb-4 w-100" />
<v-divider class="mb-4 w-100"></v-divider>
<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"
@@ -121,7 +98,7 @@
新一代,开源,编程社区
</v-list-item-subtitle>
</v-list-item>
<v-divider class="ma-1" />
<v-divider class="ma-1"></v-divider>
<v-list-item
append-icon="mdi-link"
href="https://github.com/HUSX100/IslandCaller"
@@ -164,14 +141,12 @@
fullscreen
transition="dialog-bottom-transition"
>
<v-card>
<v-card
>
<v-toolbar>
<v-btn
icon="mdi-close"
@click="showDeps = false"
/>
<v-btn icon="mdi-close" @click="showDeps = false"></v-btn>
<v-toolbar-title>使用的第三方库</v-toolbar-title>
<v-spacer />
<v-spacer></v-spacer>
</v-toolbar>
<v-card-text>
<v-list>
@@ -195,108 +170,45 @@
</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 icon="mdi-close" @click="showReportDialog = false"></v-btn>
<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"
: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>
<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>
</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-btn
variant="text"
@click="showReportDialog = false"
>
关闭
</v-btn>
<v-spacer></v-spacer>
<v-btn variant="text" @click="showReportDialog = false">关闭</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -1,33 +1,17 @@
<template>
<v-dialog
v-model="dialog"
max-width="600"
scrollable
>
<v-dialog v-model="dialog" max-width="600" scrollable>
<v-card>
<v-card-title>迁移到云端</v-card-title>
<v-card-text style="height: 400px;">
<div
v-if="loading"
class="d-flex justify-center align-center fill-height"
>
<v-progress-circular
indeterminate
color="primary"
/>
<div v-if="loading" class="d-flex justify-center align-center fill-height">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</div>
<div
v-else-if="keys.length === 0"
class="d-flex justify-center align-center fill-height"
>
<div v-else-if="keys.length === 0" class="d-flex justify-center align-center fill-height">
没有找到本地数据
</div>
<div v-else>
<!-- Category Selection -->
<v-list
select-strategy="classic"
class="mb-4"
>
<v-list select-strategy="classic" class="mb-4">
<v-list-subheader>选择数据类型</v-list-subheader>
<v-list-item
@@ -35,38 +19,32 @@
:key="category.id"
@click="toggleCategory(category)"
>
<template #prepend>
<template v-slot:prepend>
<v-checkbox-btn
:model-value="getCategoryState(category)"
:indeterminate="getCategoryIndeterminate(category)"
@click.stop="toggleCategory(category)"
/>
></v-checkbox-btn>
</template>
<v-list-item-title>{{ category.label }}</v-list-item-title>
<v-list-item-subtitle>{{ category.description }} ({{ getCategoryCount(category) }} 项)</v-list-item-subtitle>
</v-list-item>
</v-list>
<v-divider class="mb-4" />
<v-divider class="mb-4"></v-divider>
<!-- Individual Keys Expansion -->
<v-expansion-panels>
<v-expansion-panel title="详细数据列表">
<v-expansion-panel-text>
<v-list
select-strategy="classic"
density="compact"
>
<v-list select-strategy="classic" density="compact">
<v-list-item
v-for="key in keys"
:key="key"
:value="key"
>
<template #prepend>
<v-checkbox-btn
v-model="selectedKeys"
:value="key"
/>
<template v-slot:prepend>
<v-checkbox-btn v-model="selectedKeys" :value="key"></v-checkbox-btn>
</template>
<v-list-item-title>{{ key }}</v-list-item-title>
</v-list-item>
@@ -76,24 +54,14 @@
</v-expansion-panels>
</div>
</v-card-text>
<v-divider />
<v-divider></v-divider>
<v-card-actions>
<div class="text-caption ml-4 text-medium-emphasis">
已选择 {{ selectedKeys.length }} 项
</div>
<v-spacer />
<v-btn
variant="text"
@click="dialog = false"
>
取消
</v-btn>
<v-btn
color="primary"
:loading="migrating"
:disabled="selectedKeys.length === 0"
@click="migrate"
>
<v-spacer></v-spacer>
<v-btn variant="text" @click="dialog = false">取消</v-btn>
<v-btn color="primary" @click="migrate" :loading="migrating" :disabled="selectedKeys.length === 0">
开始迁移
</v-btn>
</v-card-actions>
@@ -101,10 +69,7 @@
</v-dialog>
<!-- Result Dialog -->
<v-dialog
v-model="resultDialog"
max-width="500"
>
<v-dialog v-model="resultDialog" max-width="500">
<v-card>
<v-card-title>迁移结果</v-card-title>
<v-card-text>
@@ -114,19 +79,12 @@
<p>失败: {{ result.summary.failed }}</p>
</div>
<div v-else-if="error">
<p class="text-error">
{{ error }}
</p>
<p class="text-error">{{ error }}</p>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
@click="resultDialog = false"
>
关闭
</v-btn>
<v-spacer></v-spacer>
<v-btn color="primary" @click="resultDialog = false">关闭</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
+4 -14
View File
@@ -1,17 +1,7 @@
<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>
@@ -28,7 +18,7 @@
</v-card-text>
<v-card-actions v-if="$slots.actions">
<slot name="actions" />
<slot name="actions"></slot>
</v-card-actions>
</v-card>
</template>
+14 -38
View File
@@ -1,10 +1,7 @@
<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">
@@ -17,10 +14,7 @@
<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'"
@@ -46,10 +40,7 @@
@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"
@@ -85,7 +76,7 @@
</div>
<v-menu location="bottom">
<template #activator="{ props }">
<template v-slot:activator="{ props }">
<v-btn
:disabled="disabled"
class="ml-2"
@@ -97,36 +88,24 @@
</template>
<v-list density="compact">
<v-list-item @click="copySettingId">
<template #prepend>
<v-icon
icon="mdi-key"
size="small"
/>
<template v-slot: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 #prepend>
<v-icon
icon="mdi-content-copy"
size="small"
/>
<template v-slot: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 #prepend>
<v-icon
icon="mdi-restore"
size="small"
/>
<v-list-item :disabled="isDefaultValue" @click="resetToDefault">
<template v-slot:prepend>
<v-icon icon="mdi-restore" size="small"/>
</template>
<v-list-item-title>重置为默认值</v-list-item-title>
</v-list-item>
@@ -137,10 +116,7 @@
</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"
+12 -28
View File
@@ -1,46 +1,30 @@
<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>
+10 -41
View File
@@ -6,20 +6,11 @@
>
<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"
@@ -48,13 +39,7 @@
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>
@@ -62,11 +47,7 @@
<!-- 普通编辑模式 -->
<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"
@@ -107,10 +88,7 @@
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"
@@ -122,10 +100,7 @@
</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"
@@ -133,7 +108,7 @@
>
置顶
</v-list-item>
<v-divider />
<v-divider/>
<v-list-item
:disabled="index === 0"
prepend-icon="mdi-arrow-up"
@@ -197,10 +172,7 @@
</div>
<!-- 高级编辑模式 -->
<div
v-else
class="pt-2"
>
<div v-else class="pt-2">
<v-textarea
v-model="modelValue.text"
hint="使用文本编辑模式批量编辑学生名单,保存时会自动去除空行"
@@ -215,10 +187,7 @@
</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,15 +1,7 @@
<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>
@@ -183,7 +175,7 @@
</v-card-text>
<v-card-actions>
<v-spacer />
<v-spacer/>
<v-btn
:loading="loading"
color="primary"
@@ -221,7 +213,7 @@
<p>您确定要重新初始化云端存储吗?</p>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-spacer/>
<v-btn
variant="text"
@click="showReinitDialog = false"
@@ -1,22 +1,16 @@
<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>
@@ -28,94 +22,64 @@
测试连接
</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-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-btn
color="grey"
variant="text"
@click="confirmDialog = false"
<v-spacer></v-spacer>
<v-btn color="grey" variant="text" @click="confirmDialog = false"
>取消
</v-btn
>
取消
</v-btn>
<v-btn
color="error"
variant="tonal"
@click="handleConfirm"
<v-btn color="error" variant="tonal" @click="handleConfirm"
>确认
</v-btn
>
确认
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -1,39 +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.showAntiScreenBurnCard'"/>
<v-divider class="my-2" />
<setting-item :setting-key="'display.showExamScheduleButton'" />
<v-divider class="my-2"/>
<setting-item :setting-key="'display.showExamScheduleButton'"/>
<v-divider class="my-2"/>
<setting-item :setting-key="'display.forceDesktopMode'"/>
<v-divider class="my-2" />
<setting-item :setting-key="'display.forceDesktopMode'" />
</v-list>
</settings-card>
</template>
@@ -6,22 +6,13 @@
@click="handleClick"
>
<v-card-text>
<div
ref="typewriter"
class="typewriter-text"
/>
<div
ref="sourceWriter"
class="source-text"
/>
<div ref="typewriter" class="typewriter-text"></div>
<div ref="sourceWriter" class="source-text"></div>
</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>
@@ -58,10 +49,6 @@ 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);
@@ -106,6 +93,10 @@ export default {
console.error("复制失败:", err);
}
}
},
beforeUnmount() {
[this.typewriter, this.sourceWriter].forEach(writer => writer?.stop());
}
};
</script>
@@ -1,30 +1,28 @@
<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,15 +48,8 @@
</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
@@ -70,14 +63,8 @@
/>
</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]"
@@ -87,7 +74,7 @@
variant="plain"
@blur="updateSubject(subject)"
/>
<v-spacer />
<v-spacer/>
<v-btn
color="error"
icon="mdi-delete"
@@ -108,24 +95,17 @@
@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 #prepend>
<v-icon
class="mr-2"
icon="mdi-book-open-variant"
/>
<template v-slot:prepend>
<v-icon class="mr-2" icon="mdi-book-open-variant"/>
</template>
<template #append>
<template v-slot:append>
<v-chip
class="mr-2"
color="info"
@@ -150,15 +130,8 @@
</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
@@ -173,24 +146,17 @@
</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 #prepend>
<v-icon
class="mr-2"
icon="mdi-book-multiple"
/>
<template v-slot:prepend>
<v-icon class="mr-2" icon="mdi-book-multiple"/>
</template>
<template #append>
<template v-slot:append>
<v-chip
class="mr-2"
color="info"
@@ -210,7 +176,7 @@
</v-list>
</v-list-item>
<v-divider class="my-2" />
<v-divider class="my-2"/>
<v-list-item>
<v-text-field
@@ -225,18 +191,14 @@
</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 #append>
<template v-slot:append>
<v-btn
color="error"
icon="mdi-delete"
@@ -254,10 +216,7 @@
</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 }}
@@ -276,43 +235,22 @@
/>
</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 #prepend>
<v-icon
class="mr-2"
icon="mdi-checkbox-blank-circle-outline"
size="small"
/>
<template v-slot:prepend>
<v-icon class="mr-2" icon="mdi-checkbox-blank-circle-outline" size="small"/>
</template>
<v-text-field
v-model="dialog.editedItem.tasks[index]"
@@ -320,7 +258,7 @@
hide-details
variant="plain"
/>
<template #append>
<template v-slot:append>
<v-btn
color="error"
icon="mdi-delete"
@@ -349,7 +287,7 @@
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer />
<v-spacer></v-spacer>
<v-btn
color="primary"
variant="elevated"
+55 -199
View File
@@ -1,67 +1,40 @@
<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 @click="showMigrationDialog = true">
<v-icon
class="mr-1"
icon="mdi-cloud-upload"
/>
<v-icon class="mr-1" icon="mdi-cloud-upload"/>
从本地迁移
</v-btn>
</v-btn-group>
@@ -70,18 +43,11 @@
</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
@@ -107,10 +73,7 @@
</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"
@@ -143,23 +106,13 @@
</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">
@@ -179,21 +132,12 @@
</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>
@@ -201,23 +145,13 @@
</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">
@@ -237,11 +171,8 @@
</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
@@ -258,23 +189,13 @@
</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>
@@ -294,18 +215,15 @@
:error-messages="isValidNewJson ? [] : ['请输入有效的JSON格式']"
class="font-monospace"
label="数据内容 (JSON格式)"
placeholder="请输入JSON数据,如:{&quot;name&quot;: &quot;value&quot;}"
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
@@ -322,23 +240,13 @@
</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">
@@ -346,43 +254,19 @@
</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>
@@ -399,16 +283,10 @@
@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>
@@ -429,10 +307,7 @@
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>
@@ -441,11 +316,8 @@
</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
@@ -454,10 +326,7 @@
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>
@@ -465,37 +334,24 @@
</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,23 +1,22 @@
<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" />
<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-divider class="my-2" />
<setting-item setting-key="randomPicker.animation" />
</v-list>
</settings-card>
</template>
@@ -1,21 +1,13 @@
<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,16 +58,10 @@
</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 || '科目名称不能为空']"
@@ -91,7 +85,7 @@
v-for="(subject, index) in subjects"
:key="subject.order"
>
<template #prepend>
<template v-slot:prepend>
<div class="d-flex flex-column align-center mr-2">
<v-btn
:disabled="index === 0"
@@ -120,7 +114,7 @@
/>
</v-list-item-title>
<template #append>
<template v-slot:append>
<v-btn
color="error"
icon="mdi-delete"
@@ -1,15 +1,9 @@
<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>
@@ -20,17 +14,11 @@
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>
@@ -49,11 +37,6 @@ export default {
name: 'ThemeSettingsCard',
components: {SettingsCard},
setup() {
const theme = useTheme();
return {theme};
},
data() {
return {
localTheme: getSetting('theme.mode')
@@ -67,6 +50,11 @@ export default {
}
},
setup() {
const theme = useTheme();
return {theme};
},
methods: {
updateTheme(mode) {
this.theme.global.name.value = mode;