chore(release): 1.3.3 发布并退役旧联动
This commit is contained in:
1 parent
b7cdc7c9ea
commit
c0ebf88138
184 files changed
+5450
-11705
No files matched your search
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { confirmAction } from '../../composables/confirm.js';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useAuthStore } from '../../stores/auth.js';
|
||||
import { useRealtimeStore } from '../../stores/realtime.js';
|
||||
|
||||
@@ -13,7 +14,7 @@ const auth = useAuthStore();
|
||||
const realtime = useRealtimeStore();
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
await ElMessageBox.confirm('确认退出当前账号?', '退出登录', { type: 'warning' });
|
||||
if (!(await confirmAction('确认退出当前账号?', '退出登录', { type: 'warning' }))) return;
|
||||
realtime.disconnect();
|
||||
await auth.logout();
|
||||
ElMessage.success('已退出登录');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { confirmAction } from '../../composables/confirm.js';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { formatDate, relativeTime, type UpdateInfo } from '@classhelper/shared';
|
||||
import type { ClientAgentStatus, DesktopAppInfo } from '../../../types/desktop.js';
|
||||
import { useAppStore } from '../../stores/app.js';
|
||||
@@ -198,9 +199,12 @@ const cacheRows = computed(() =>
|
||||
);
|
||||
|
||||
async function clearCache(): Promise<void> {
|
||||
await ElMessageBox.confirm('清空后离线时将无法查看历史数据,确认继续?', '清空离线缓存', {
|
||||
type: 'warning',
|
||||
});
|
||||
if (
|
||||
!(await confirmAction('清空后离线时将无法查看历史数据,确认继续?', '清空离线缓存', {
|
||||
type: 'warning',
|
||||
}))
|
||||
)
|
||||
return;
|
||||
await appStore.clearCache();
|
||||
ElMessage.success('离线缓存已清空');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { confirmAction } from '../../composables/confirm.js';
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {
|
||||
HOMEWORK_PHRASE_DEFAULTS,
|
||||
HOMEWORK_PHRASE_MAX_COUNT,
|
||||
@@ -93,9 +94,12 @@ async function manualSync(): Promise<void> {
|
||||
}
|
||||
|
||||
async function clearCache(): Promise<void> {
|
||||
await ElMessageBox.confirm('清空后离线时将无法查看历史数据,确认继续?', '清空离线缓存', {
|
||||
type: 'warning',
|
||||
});
|
||||
if (
|
||||
!(await confirmAction('清空后离线时将无法查看历史数据,确认继续?', '清空离线缓存', {
|
||||
type: 'warning',
|
||||
}))
|
||||
)
|
||||
return;
|
||||
await appStore.clearCache();
|
||||
ElMessage.success('离线缓存已清空');
|
||||
}
|
||||
@@ -151,7 +155,7 @@ async function removePhrase(value: string): Promise<void> {
|
||||
}
|
||||
|
||||
async function resetPhrases(): Promise<void> {
|
||||
await ElMessageBox.confirm('恢复为默认快捷短语?', '恢复默认', { type: 'warning' });
|
||||
if (!(await confirmAction('恢复为默认快捷短语?', '恢复默认', { type: 'warning' }))) return;
|
||||
phrases.value = [...HOMEWORK_PHRASE_DEFAULTS];
|
||||
await savePhrases();
|
||||
ElMessage.success('已恢复默认');
|
||||
@@ -192,7 +196,7 @@ const updateTargetText = computed(() => {
|
||||
const pendingUpdateText = computed(() => {
|
||||
const pending = agent.value?.pendingUpdateVersion ?? '';
|
||||
if (!pending) return '无';
|
||||
return `${pending}(已下载,空闲时自动安装)`;
|
||||
return `${pending}(已下载,等待按服务端指定的时机安装)`;
|
||||
});
|
||||
|
||||
const lastErrorText = computed(() => agent.value?.lastError?.trim() || '无');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { confirmAction } from '../../composables/confirm.js';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {
|
||||
DEFAULT_ISLAND_APPEARANCE,
|
||||
ISLAND_APPEARANCE_RANGES as RANGES,
|
||||
@@ -65,7 +66,7 @@ async function saveIslandAppearance(): Promise<void> {
|
||||
}
|
||||
|
||||
async function resetIslandAppearance(): Promise<void> {
|
||||
await ElMessageBox.confirm('恢复灵动岛的默认外观?', '恢复默认', { type: 'warning' });
|
||||
if (!(await confirmAction('恢复灵动岛的默认外观?', '恢复默认', { type: 'warning' }))) return;
|
||||
applyIslandAppearance({ ...DEFAULT_ISLAND_APPEARANCE });
|
||||
await saveIslandAppearance();
|
||||
}
|
||||
|
||||
@@ -1,42 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {
|
||||
CLASSISLAND_NOTIFICATION_CHANNEL_HINTS,
|
||||
CLASSISLAND_NOTIFICATION_CHANNEL_LABELS,
|
||||
CLASSISLAND_NOTIFICATION_CHANNELS,
|
||||
formatDate,
|
||||
type ClassIslandNotificationChannel,
|
||||
} from '@classhelper/shared';
|
||||
import { classGradeQueryApi, classIslandStatusApi } from '../../api/index.js';
|
||||
import { useAppStore } from '../../stores/app.js';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { classGradeQueryApi } from '../../api/index.js';
|
||||
import { useAuthStore } from '../../stores/auth.js';
|
||||
|
||||
/**
|
||||
* 提醒设置:这台教室机器把提醒弹在哪个端(客户端 / ClassIsland / 两端都弹)。
|
||||
* 值同时落本地配置与班级记录(服务端据此决定是否推送 ClassIsland)。
|
||||
*/
|
||||
const appStore = useAppStore();
|
||||
const auth = useAuthStore();
|
||||
|
||||
const channelSaving = ref(false);
|
||||
/**
|
||||
* 本班有没有接入 ClassIsland 设备。
|
||||
*
|
||||
* 只用来禁用「只在 ClassIsland 上弹」——没有设备时选它等于把提醒静默丢掉;
|
||||
* 注意**不能**顺手把「两端都弹」也禁掉:那样教室一旦选过"只在客户端",没设备时就再也切不回来了。
|
||||
*/
|
||||
const classIslandConnected = ref(false);
|
||||
/** 本班 ClassIsland 设备的展示信息(设备名 / 最后上报),来自 /classes/:id/classisland-status */
|
||||
const classIslandStatus = ref<{
|
||||
connected: boolean;
|
||||
deviceName: string | null;
|
||||
deviceCount: number;
|
||||
lastSeenAt: string | null;
|
||||
pluginVersion: string | null;
|
||||
classIslandVersion: string | null;
|
||||
} | null>(null);
|
||||
|
||||
/* ------------------------------------------------------------ 班级端成绩查询状态(只读)
|
||||
*
|
||||
* 「是否允许班级端查看成绩」是**服务端口径**:管理员在 Web 端「班级管理」里开关,
|
||||
@@ -56,43 +22,7 @@ async function loadGradeQuery(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const classIslandStatusText = computed(() => {
|
||||
const status = classIslandStatus.value;
|
||||
if (!status || !status.connected) return '未接入';
|
||||
const seen = status.lastSeenAt ? `最后上报 ${formatDate(status.lastSeenAt, true)}` : '尚未上报过';
|
||||
return `已接入 · ${status.deviceName ?? 'ClassIsland 设备'} · ${seen}`;
|
||||
});
|
||||
|
||||
const channelOptions = computed(() =>
|
||||
CLASSISLAND_NOTIFICATION_CHANNELS.map((value) => ({
|
||||
value,
|
||||
label: CLASSISLAND_NOTIFICATION_CHANNEL_LABELS[value],
|
||||
disabled: value === 'classisland' && !classIslandConnected.value,
|
||||
})),
|
||||
);
|
||||
|
||||
async function applyChannel(value: ClassIslandNotificationChannel): Promise<void> {
|
||||
channelSaving.value = true;
|
||||
try {
|
||||
const saved = await appStore.setNotificationChannel(auth.classId, value);
|
||||
if (saved) ElMessage.success('提醒显示位置已更新');
|
||||
else ElMessage.warning('已保存在本机,但同步到服务器失败(离线或权限不足),联网后请再点一次');
|
||||
} finally {
|
||||
channelSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await appStore.loadNotificationChannel(auth.classId);
|
||||
// 本班 ClassIsland 联动状态:没接设备就别让选「只在 ClassIsland 上弹」
|
||||
if (auth.classId) {
|
||||
const status = await classIslandStatusApi.get(auth.classId).catch(() => null);
|
||||
classIslandStatus.value = status;
|
||||
classIslandConnected.value = status?.connected === true;
|
||||
}
|
||||
// 班级端成绩查询开关(存在班级上,与 Web 端共享同一个值)
|
||||
await loadGradeQuery();
|
||||
});
|
||||
onMounted(loadGradeQuery);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -100,44 +30,14 @@ onMounted(async () => {
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2 class="page-title">提醒</h2>
|
||||
<p class="page-subtitle">老师发通知 / 叫人时,提醒弹在哪个端</p>
|
||||
<p class="page-subtitle">通知提醒与成绩查询状态</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header><span>ClassIsland 联动</span></template>
|
||||
<el-descriptions :column="1" border size="small" class="mb-12">
|
||||
<el-descriptions-item label="联动状态">{{ classIslandStatusText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="插件 / ClassIsland">
|
||||
{{ classIslandStatus?.pluginVersion ?? '—' }} /
|
||||
{{ classIslandStatus?.classIslandVersion ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #header>通知提醒</template>
|
||||
<p class="text-muted">
|
||||
<strong>通知模式</strong>
|
||||
:老师在 ClassHelper 上发布通知 / 叫人时,提醒弹在灵动岛还是 ClassIsland,
|
||||
由这台教室机器决定。选择会同步到班级,服务端据此决定是否推送到教室的 ClassIsland。
|
||||
</p>
|
||||
<el-radio-group
|
||||
:model-value="appStore.notificationChannel"
|
||||
:disabled="channelSaving"
|
||||
class="channel-group"
|
||||
@change="
|
||||
(value: string | number | boolean | undefined) =>
|
||||
applyChannel(value as ClassIslandNotificationChannel)
|
||||
"
|
||||
>
|
||||
<el-radio v-for="item in channelOptions" :key="item.value" :value="item.value" :disabled="item.disabled">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<p class="text-muted channel-hint">
|
||||
{{ CLASSISLAND_NOTIFICATION_CHANNEL_HINTS[appStore.notificationChannel] }}
|
||||
<template v-if="!classIslandConnected">
|
||||
<br />
|
||||
提示:本班还没有接入 ClassIsland 设备(在 Web 端「ClassHelper 联动」里接入后才能选"只在
|
||||
ClassIsland 上弹")。
|
||||
</template>
|
||||
通知与叫人通过 ClassHelper 客户端和灵动岛提醒。普通通知在上课期间暂存,下课后补弹;紧急通知立即提醒。
|
||||
</p>
|
||||
</el-card>
|
||||
|
||||
@@ -150,9 +50,7 @@ onMounted(async () => {
|
||||
打开后,可以在「成绩」页看到本班成绩与排名。学生本身没有账号、不能登录,
|
||||
查成绩就是由这台教室机器代做。关闭后本机查成绩会被服务端拒绝,教师端不受影响。
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
本开关由管理员在服务端「班级管理」和「客户端设备」里统一设置,教室机器不提供本地开关。
|
||||
</p>
|
||||
<p class="text-muted">本开关由管理员在服务端「班级管理」里统一设置,教室机器不提供本地开关。</p>
|
||||
<el-tag :type="gradeQueryEnabled ? 'success' : 'info'" effect="plain">
|
||||
{{ gradeQueryEnabled ? '当前:已允许查看成绩与排名' : '当前:已由管理员关闭' }}
|
||||
</el-tag>
|
||||
|
||||
Reference in new issue
Block a user