feat(app): migrate schedules and screenshots (stage 4)
This commit is contained in:
42
app-frontend/src/api/schedules.js
Normal file
42
app-frontend/src/api/schedules.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { publicApi } from './http'
|
||||
|
||||
export async function fetchSchedules() {
|
||||
const { data } = await publicApi.get('/schedules')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function createSchedule(payload) {
|
||||
const { data } = await publicApi.post('/schedules', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function updateSchedule(scheduleId, payload) {
|
||||
const { data } = await publicApi.put(`/schedules/${scheduleId}`, payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function deleteSchedule(scheduleId) {
|
||||
const { data } = await publicApi.delete(`/schedules/${scheduleId}`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function toggleSchedule(scheduleId, payload) {
|
||||
const { data } = await publicApi.post(`/schedules/${scheduleId}/toggle`, payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function runScheduleNow(scheduleId) {
|
||||
const { data } = await publicApi.post(`/schedules/${scheduleId}/run`, {})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchScheduleLogs(scheduleId, params = {}) {
|
||||
const { data } = await publicApi.get(`/schedules/${scheduleId}/logs`, { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export async function clearScheduleLogs(scheduleId) {
|
||||
const { data } = await publicApi.delete(`/schedules/${scheduleId}/logs`)
|
||||
return data
|
||||
}
|
||||
|
||||
17
app-frontend/src/api/screenshots.js
Normal file
17
app-frontend/src/api/screenshots.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { publicApi } from './http'
|
||||
|
||||
export async function fetchScreenshots() {
|
||||
const { data } = await publicApi.get('/screenshots')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function deleteScreenshot(filename) {
|
||||
const { data } = await publicApi.delete(`/screenshots/${encodeURIComponent(filename)}`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function clearScreenshots() {
|
||||
const { data } = await publicApi.post('/screenshots/clear', {})
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -1,20 +1,598 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
import { fetchAccounts } from '../api/accounts'
|
||||
import {
|
||||
clearScheduleLogs,
|
||||
createSchedule,
|
||||
deleteSchedule,
|
||||
fetchScheduleLogs,
|
||||
fetchSchedules,
|
||||
runScheduleNow,
|
||||
toggleSchedule,
|
||||
updateSchedule,
|
||||
} from '../api/schedules'
|
||||
import { useUserStore } from '../stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const schedules = ref([])
|
||||
|
||||
const accountsLoading = ref(false)
|
||||
const accountOptions = ref([])
|
||||
|
||||
const editorOpen = ref(false)
|
||||
const editorSaving = ref(false)
|
||||
const editingId = ref(null)
|
||||
|
||||
const logsOpen = ref(false)
|
||||
const logsLoading = ref(false)
|
||||
const logs = ref([])
|
||||
const logsSchedule = ref(null)
|
||||
|
||||
const vipModalOpen = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
schedule_time: '08:00',
|
||||
weekdays: ['1', '2', '3', '4', '5'],
|
||||
browse_type: '应读',
|
||||
enable_screenshot: true,
|
||||
account_ids: [],
|
||||
})
|
||||
|
||||
const browseTypeOptions = [
|
||||
{ label: '应读', value: '应读' },
|
||||
{ label: '未读', value: '未读' },
|
||||
{ label: '注册前未读', value: '注册前未读' },
|
||||
]
|
||||
|
||||
const weekdayOptions = [
|
||||
{ label: '周一', value: '1' },
|
||||
{ label: '周二', value: '2' },
|
||||
{ label: '周三', value: '3' },
|
||||
{ label: '周四', value: '4' },
|
||||
{ label: '周五', value: '5' },
|
||||
{ label: '周六', value: '6' },
|
||||
{ label: '周日', value: '7' },
|
||||
]
|
||||
|
||||
const canUseSchedule = computed(() => userStore.isVip)
|
||||
|
||||
function normalizeTime(value) {
|
||||
const match = String(value || '').match(/^(\d{1,2}):(\d{2})$/)
|
||||
if (!match) return null
|
||||
const hour = Number(match[1])
|
||||
const minute = Number(match[2])
|
||||
if (Number.isNaN(hour) || Number.isNaN(minute)) return null
|
||||
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null
|
||||
return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function weekdaysText(textOrArray) {
|
||||
const raw = Array.isArray(textOrArray) ? textOrArray : String(textOrArray || '').split(',').filter(Boolean)
|
||||
const map = Object.fromEntries(weekdayOptions.map((w) => [w.value, w.label]))
|
||||
return raw.map((d) => map[String(d)] || String(d)).join(' ')
|
||||
}
|
||||
|
||||
async function loadAccounts() {
|
||||
accountsLoading.value = true
|
||||
try {
|
||||
const list = await fetchAccounts({ refresh: false })
|
||||
accountOptions.value = (list || []).map((acc) => ({ label: acc.username, value: acc.id }))
|
||||
} catch {
|
||||
accountOptions.value = []
|
||||
} finally {
|
||||
accountsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSchedules() {
|
||||
loading.value = true
|
||||
try {
|
||||
schedules.value = await fetchSchedules()
|
||||
} catch (e) {
|
||||
if (e?.response?.status === 401) window.location.href = '/login'
|
||||
schedules.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editingId.value = null
|
||||
form.name = ''
|
||||
form.schedule_time = '08:00'
|
||||
form.weekdays = ['1', '2', '3', '4', '5']
|
||||
form.browse_type = '应读'
|
||||
form.enable_screenshot = true
|
||||
form.account_ids = []
|
||||
editorOpen.value = true
|
||||
}
|
||||
|
||||
function openEdit(schedule) {
|
||||
editingId.value = schedule.id
|
||||
form.name = schedule.name || ''
|
||||
form.schedule_time = normalizeTime(schedule.schedule_time) || '08:00'
|
||||
form.weekdays = String(schedule.weekdays || '')
|
||||
.split(',')
|
||||
.filter(Boolean)
|
||||
.map((v) => String(v))
|
||||
if (form.weekdays.length === 0) form.weekdays = ['1', '2', '3', '4', '5']
|
||||
form.browse_type = schedule.browse_type || '应读'
|
||||
form.enable_screenshot = Number(schedule.enable_screenshot ?? 1) !== 0
|
||||
form.account_ids = Array.isArray(schedule.account_ids) ? schedule.account_ids.slice() : []
|
||||
editorOpen.value = true
|
||||
}
|
||||
|
||||
async function saveSchedule() {
|
||||
if (!canUseSchedule.value) {
|
||||
vipModalOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedTime = normalizeTime(form.schedule_time)
|
||||
if (!normalizedTime) {
|
||||
ElMessage.error('时间格式错误,请使用 HH:MM')
|
||||
return
|
||||
}
|
||||
if (!form.weekdays || form.weekdays.length === 0) {
|
||||
ElMessage.warning('请选择至少一个执行日期')
|
||||
return
|
||||
}
|
||||
|
||||
editorSaving.value = true
|
||||
try {
|
||||
const payload = {
|
||||
name: form.name.trim() || '我的定时任务',
|
||||
schedule_time: normalizedTime,
|
||||
weekdays: form.weekdays.join(','),
|
||||
browse_type: form.browse_type,
|
||||
enable_screenshot: form.enable_screenshot ? 1 : 0,
|
||||
account_ids: form.account_ids,
|
||||
}
|
||||
|
||||
if (editingId.value) {
|
||||
await updateSchedule(editingId.value, payload)
|
||||
ElMessage.success('保存成功')
|
||||
} else {
|
||||
await createSchedule(payload)
|
||||
ElMessage.success('创建成功')
|
||||
}
|
||||
editorOpen.value = false
|
||||
await loadSchedules()
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '保存失败')
|
||||
} finally {
|
||||
editorSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete(schedule) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要删除定时任务「${schedule.name || '未命名任务'}」吗?`, '删除任务', {
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await deleteSchedule(schedule.id)
|
||||
if (res?.success) {
|
||||
ElMessage.success('已删除')
|
||||
await loadSchedules()
|
||||
} else {
|
||||
ElMessage.error(res?.error || '删除失败')
|
||||
}
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function onToggle(schedule, enabled) {
|
||||
if (!canUseSchedule.value) {
|
||||
vipModalOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await toggleSchedule(schedule.id, { enabled })
|
||||
if (res?.success) {
|
||||
schedule.enabled = enabled ? 1 : 0
|
||||
ElMessage.success(enabled ? '已启用' : '已禁用')
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function onRunNow(schedule) {
|
||||
if (!canUseSchedule.value) {
|
||||
vipModalOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await runScheduleNow(schedule.id)
|
||||
if (res?.success) ElMessage.success(res?.message || '已开始执行')
|
||||
else ElMessage.error(res?.error || '执行失败')
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '执行失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function openLogs(schedule) {
|
||||
logsSchedule.value = schedule
|
||||
logsOpen.value = true
|
||||
logsLoading.value = true
|
||||
try {
|
||||
logs.value = await fetchScheduleLogs(schedule.id, { limit: 20 })
|
||||
} catch {
|
||||
logs.value = []
|
||||
} finally {
|
||||
logsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function clearLogs() {
|
||||
const schedule = logsSchedule.value
|
||||
if (!schedule) return
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要清空该任务的所有执行日志吗?', '清空日志', {
|
||||
confirmButtonText: '清空',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await clearScheduleLogs(schedule.id)
|
||||
if (res?.success) {
|
||||
ElMessage.success(`已清空 ${res?.deleted || 0} 条日志`)
|
||||
logs.value = []
|
||||
} else {
|
||||
ElMessage.error(res?.error || '操作失败')
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
function statusTagType(status) {
|
||||
const text = String(status || '')
|
||||
if (text === 'success' || text === 'completed') return 'success'
|
||||
if (text === 'failed') return 'danger'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
function formatDuration(seconds) {
|
||||
const value = Number(seconds || 0)
|
||||
const mins = Math.floor(value / 60)
|
||||
const secs = value % 60
|
||||
if (mins <= 0) return `${secs} 秒`
|
||||
return `${mins} 分 ${secs} 秒`
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!userStore.vipInfo) {
|
||||
userStore.refreshVipInfo().catch(() => {
|
||||
window.location.href = '/login'
|
||||
})
|
||||
}
|
||||
|
||||
await Promise.all([loadAccounts(), loadSchedules()])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never" :body-style="{ padding: '16px' }" class="card">
|
||||
<h2 class="title">定时任务</h2>
|
||||
<div class="app-muted">阶段1:页面壳子已就绪,功能将在后续阶段迁移。</div>
|
||||
</el-card>
|
||||
<div class="page">
|
||||
<el-alert
|
||||
v-if="!canUseSchedule"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
title="定时任务为 VIP 专属功能,升级后可使用。"
|
||||
class="vip-alert"
|
||||
>
|
||||
<template #default>
|
||||
<div class="vip-actions">
|
||||
<el-button type="primary" plain @click="vipModalOpen = true">了解VIP特权</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-card shadow="never" class="panel" :body-style="{ padding: '14px' }">
|
||||
<div class="panel-head">
|
||||
<div class="panel-title">定时任务</div>
|
||||
<div class="panel-actions">
|
||||
<el-button :loading="loading" @click="loadSchedules">刷新</el-button>
|
||||
<el-button type="primary" :disabled="!canUseSchedule" @click="openCreate">新建任务</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-skeleton v-if="loading" :rows="6" animated />
|
||||
<template v-else>
|
||||
<el-empty v-if="schedules.length === 0" description="暂无定时任务" />
|
||||
<div v-else class="grid">
|
||||
<el-card v-for="s in schedules" :key="s.id" shadow="never" class="schedule-card" :body-style="{ padding: '14px' }">
|
||||
<div class="schedule-top">
|
||||
<div class="schedule-main">
|
||||
<div class="schedule-title">
|
||||
<span class="schedule-name">{{ s.name || '未命名任务' }}</span>
|
||||
<el-tag size="small" effect="light" :type="Number(s.enabled) ? 'success' : 'info'">
|
||||
{{ Number(s.enabled) ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="schedule-meta app-muted">
|
||||
<span>⏰ {{ normalizeTime(s.schedule_time) || s.schedule_time }}</span>
|
||||
<span>📅 {{ weekdaysText(s.weekdays) }}</span>
|
||||
</div>
|
||||
<div class="schedule-meta app-muted">
|
||||
<span>📋 {{ s.browse_type || '应读' }}</span>
|
||||
<span>👥 {{ (s.account_ids || []).length }} 个账号</span>
|
||||
<span>{{ Number(s.enable_screenshot ?? 1) !== 0 ? '📸 截图' : '📷 不截图' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="schedule-switch">
|
||||
<el-switch
|
||||
:model-value="Boolean(Number(s.enabled))"
|
||||
:disabled="!canUseSchedule"
|
||||
inline-prompt
|
||||
active-text="启用"
|
||||
inactive-text="停用"
|
||||
@change="(val) => onToggle(s, val)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="schedule-actions">
|
||||
<el-button size="small" type="primary" :disabled="!canUseSchedule" @click="onRunNow(s)">立即执行</el-button>
|
||||
<el-button size="small" @click="openLogs(s)">日志</el-button>
|
||||
<el-button size="small" :disabled="!canUseSchedule" @click="openEdit(s)">编辑</el-button>
|
||||
<el-button size="small" type="danger" text :disabled="!canUseSchedule" @click="onDelete(s)">删除</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="editorOpen" :title="editingId ? '编辑定时任务' : '新建定时任务'" width="min(720px, 92vw)">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="任务名称">
|
||||
<el-input v-model="form.name" placeholder="我的定时任务" :disabled="!canUseSchedule" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行时间(HH:MM)">
|
||||
<el-input v-model="form.schedule_time" placeholder="08:00" :disabled="!canUseSchedule" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行日期">
|
||||
<el-checkbox-group v-model="form.weekdays" :disabled="!canUseSchedule">
|
||||
<el-checkbox v-for="w in weekdayOptions" :key="w.value" :label="w.value">{{ w.label }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="浏览类型">
|
||||
<el-select v-model="form.browse_type" style="width: 160px" :disabled="!canUseSchedule">
|
||||
<el-option v-for="opt in browseTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="截图">
|
||||
<el-switch v-model="form.enable_screenshot" :disabled="!canUseSchedule" inline-prompt active-text="截图" inactive-text="不截图" />
|
||||
</el-form-item>
|
||||
<el-form-item label="参与账号">
|
||||
<el-select
|
||||
v-model="form.account_ids"
|
||||
multiple
|
||||
filterable
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
placeholder="选择账号(可多选)"
|
||||
style="width: 100%"
|
||||
:loading="accountsLoading"
|
||||
:disabled="!canUseSchedule"
|
||||
>
|
||||
<el-option v-for="opt in accountOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="editorOpen = false">取消</el-button>
|
||||
<el-button type="primary" :loading="editorSaving" :disabled="!canUseSchedule" @click="saveSchedule">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="logsOpen" :title="logsSchedule ? `【${logsSchedule.name || '未命名任务'}】执行日志` : '执行日志'" width="min(760px, 92vw)">
|
||||
<el-skeleton v-if="logsLoading" :rows="6" animated />
|
||||
<template v-else>
|
||||
<el-empty v-if="logs.length === 0" description="暂无执行日志" />
|
||||
<div v-else class="logs">
|
||||
<el-card v-for="log in logs" :key="log.id" shadow="never" class="log-card" :body-style="{ padding: '12px' }">
|
||||
<div class="log-head">
|
||||
<el-tag size="small" effect="light" :type="statusTagType(log.status)">
|
||||
{{ log.status === 'failed' ? '失败' : log.status === 'running' ? '进行中' : '成功' }}
|
||||
</el-tag>
|
||||
<span class="app-muted">{{ log.created_at || '' }}</span>
|
||||
</div>
|
||||
<div class="log-body">
|
||||
<div>账号数:{{ log.total_accounts || 0 }} 个</div>
|
||||
<div>成功:{{ log.success_count || 0 }} 个 · 失败:{{ log.failed_count || 0 }} 个</div>
|
||||
<div>耗时:{{ formatDuration(log.duration || 0) }}</div>
|
||||
<div v-if="log.error_message" class="log-error">错误:{{ log.error_message }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="logsOpen = false">关闭</el-button>
|
||||
<el-button type="danger" plain :disabled="logs.length === 0" @click="clearLogs">清空日志</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="vipModalOpen" title="VIP 特权" width="min(560px, 92vw)">
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
title="升级 VIP 后可解锁:无限账号、优先排队、定时任务、批量操作。"
|
||||
show-icon
|
||||
/>
|
||||
<div class="vip-body">
|
||||
<div class="vip-tip app-muted">升级方式:请通过“反馈”联系管理员开通。</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="vipModalOpen = false">我知道了</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.vip-alert {
|
||||
border-radius: var(--app-radius);
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 6px;
|
||||
.vip-actions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: var(--app-radius);
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.panel-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.schedule-card {
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.schedule-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.schedule-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.schedule-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.schedule-name {
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.schedule-meta {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.schedule-actions {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.logs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.log-card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.log-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.log-body {
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.log-error {
|
||||
margin-top: 6px;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.vip-body {
|
||||
padding: 12px 0 0;
|
||||
}
|
||||
|
||||
.vip-tip {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,20 +1,253 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
import { clearScreenshots, deleteScreenshot, fetchScreenshots } from '../api/screenshots'
|
||||
|
||||
const loading = ref(false)
|
||||
const screenshots = ref([])
|
||||
|
||||
const previewOpen = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewTitle = ref('')
|
||||
|
||||
function buildUrl(filename) {
|
||||
return `/screenshots/${encodeURIComponent(filename)}`
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await fetchScreenshots()
|
||||
screenshots.value = Array.isArray(data) ? data : []
|
||||
} catch (e) {
|
||||
if (e?.response?.status === 401) window.location.href = '/login'
|
||||
screenshots.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openPreview(item) {
|
||||
previewTitle.value = item.display_name || item.filename || '截图预览'
|
||||
previewUrl.value = buildUrl(item.filename)
|
||||
previewOpen.value = true
|
||||
}
|
||||
|
||||
async function onClearAll() {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要清空全部截图吗?', '清空截图', {
|
||||
confirmButtonText: '清空',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await clearScreenshots()
|
||||
if (res?.success) {
|
||||
ElMessage.success(`已清空(删除 ${res?.deleted || 0} 张)`)
|
||||
screenshots.value = []
|
||||
previewOpen.value = false
|
||||
return
|
||||
}
|
||||
ElMessage.error(res?.error || '操作失败')
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete(item) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要删除截图「${item.display_name || item.filename}」吗?`, '删除截图', {
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await deleteScreenshot(item.filename)
|
||||
if (res?.success) {
|
||||
screenshots.value = screenshots.value.filter((s) => s.filename !== item.filename)
|
||||
if (previewUrl.value.includes(encodeURIComponent(item.filename))) previewOpen.value = false
|
||||
ElMessage.success('已删除')
|
||||
return
|
||||
}
|
||||
ElMessage.error(res?.error || '删除失败')
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function copyLink(item) {
|
||||
const url = `${window.location.origin}${buildUrl(item.filename)}`
|
||||
try {
|
||||
await navigator.clipboard.writeText(url)
|
||||
ElMessage.success('链接已复制')
|
||||
} catch {
|
||||
ElMessage.warning('复制失败,请手动复制链接')
|
||||
}
|
||||
}
|
||||
|
||||
async function copyImage(item) {
|
||||
const url = buildUrl(item.filename)
|
||||
try {
|
||||
const resp = await fetch(url, { credentials: 'include' })
|
||||
const blob = await resp.blob()
|
||||
await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })])
|
||||
ElMessage.success('图片已复制')
|
||||
} catch {
|
||||
await copyLink(item)
|
||||
}
|
||||
}
|
||||
|
||||
function download(item) {
|
||||
const link = document.createElement('a')
|
||||
link.href = buildUrl(item.filename)
|
||||
link.download = item.display_name || item.filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never" :body-style="{ padding: '16px' }" class="card">
|
||||
<h2 class="title">截图管理</h2>
|
||||
<div class="app-muted">阶段1:页面壳子已就绪,功能将在后续阶段迁移。</div>
|
||||
<el-card shadow="never" class="panel" :body-style="{ padding: '14px' }">
|
||||
<div class="panel-head">
|
||||
<div class="panel-title">截图管理</div>
|
||||
<div class="panel-actions">
|
||||
<el-button :loading="loading" @click="load">刷新</el-button>
|
||||
<el-button type="danger" plain :disabled="screenshots.length === 0" @click="onClearAll">清空全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-skeleton v-if="loading" :rows="6" animated />
|
||||
<template v-else>
|
||||
<el-empty v-if="screenshots.length === 0" description="暂无截图" />
|
||||
|
||||
<div v-else class="grid">
|
||||
<el-card v-for="item in screenshots" :key="item.filename" shadow="never" class="shot-card" :body-style="{ padding: '0' }">
|
||||
<img class="shot-img" :src="buildUrl(item.filename)" :alt="item.display_name || item.filename" loading="lazy" @click="openPreview(item)" />
|
||||
<div class="shot-body">
|
||||
<div class="shot-name" :title="item.display_name || item.filename">{{ item.display_name || item.filename }}</div>
|
||||
<div class="shot-meta app-muted">{{ item.created || '' }}</div>
|
||||
<div class="shot-actions">
|
||||
<el-button size="small" text type="primary" @click="copyImage(item)">复制图片</el-button>
|
||||
<el-button size="small" text @click="download(item)">下载</el-button>
|
||||
<el-button size="small" text type="danger" @click="onDelete(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-dialog v-model="previewOpen" :title="previewTitle" width="min(920px, 94vw)">
|
||||
<div class="preview">
|
||||
<img :src="previewUrl" :alt="previewTitle" class="preview-img" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="previewOpen = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
.panel {
|
||||
border-radius: var(--app-radius);
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 6px;
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.panel-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.shot-card {
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--app-border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.shot-img {
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.shot-body {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.shot-name {
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.shot-meta {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.shot-actions {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.preview-img {
|
||||
max-width: 100%;
|
||||
max-height: 78vh;
|
||||
object-fit: contain;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--app-border);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
{
|
||||
"_auth-yhlOdREj.js": {
|
||||
"file": "assets/auth-yhlOdREj.js",
|
||||
"_accounts-DI7tHfNj.js": {
|
||||
"file": "assets/accounts-DI7tHfNj.js",
|
||||
"name": "accounts",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_auth-CgGRYkq1.js": {
|
||||
"file": "assets/auth-CgGRYkq1.js",
|
||||
"name": "auth",
|
||||
"imports": [
|
||||
"index.html"
|
||||
@@ -11,7 +18,7 @@
|
||||
"name": "password"
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-DvbGwVAp.js",
|
||||
"file": "assets/index-BstQMnWL.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
@@ -29,11 +36,12 @@
|
||||
]
|
||||
},
|
||||
"src/pages/AccountsPage.vue": {
|
||||
"file": "assets/AccountsPage-C2BSK5Ns.js",
|
||||
"file": "assets/AccountsPage-Bcis23qR.js",
|
||||
"name": "AccountsPage",
|
||||
"src": "src/pages/AccountsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DI7tHfNj.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -41,13 +49,13 @@
|
||||
]
|
||||
},
|
||||
"src/pages/LoginPage.vue": {
|
||||
"file": "assets/LoginPage-DYohZsxn.js",
|
||||
"file": "assets/LoginPage-BNb9NBzk.js",
|
||||
"name": "LoginPage",
|
||||
"src": "src/pages/LoginPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-yhlOdREj.js",
|
||||
"_auth-CgGRYkq1.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
@@ -55,26 +63,26 @@
|
||||
]
|
||||
},
|
||||
"src/pages/RegisterPage.vue": {
|
||||
"file": "assets/RegisterPage-CGBzvBqd.js",
|
||||
"file": "assets/RegisterPage-CFiuiL-s.js",
|
||||
"name": "RegisterPage",
|
||||
"src": "src/pages/RegisterPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-yhlOdREj.js"
|
||||
"_auth-CgGRYkq1.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/RegisterPage-CVjBOq6i.css"
|
||||
]
|
||||
},
|
||||
"src/pages/ResetPasswordPage.vue": {
|
||||
"file": "assets/ResetPasswordPage-ClLk6uyu.js",
|
||||
"file": "assets/ResetPasswordPage-DeWXyaHc.js",
|
||||
"name": "ResetPasswordPage",
|
||||
"src": "src/pages/ResetPasswordPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-yhlOdREj.js",
|
||||
"_auth-CgGRYkq1.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
@@ -82,19 +90,20 @@
|
||||
]
|
||||
},
|
||||
"src/pages/SchedulesPage.vue": {
|
||||
"file": "assets/SchedulesPage-DHlqgLCv.js",
|
||||
"file": "assets/SchedulesPage-BXyRqadY.js",
|
||||
"name": "SchedulesPage",
|
||||
"src": "src/pages/SchedulesPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DI7tHfNj.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/SchedulesPage-BAj1X6GW.css"
|
||||
"assets/SchedulesPage-Gu_OsDUd.css"
|
||||
]
|
||||
},
|
||||
"src/pages/ScreenshotsPage.vue": {
|
||||
"file": "assets/ScreenshotsPage-jZuEr5af.js",
|
||||
"file": "assets/ScreenshotsPage-BwyU04c1.js",
|
||||
"name": "ScreenshotsPage",
|
||||
"src": "src/pages/ScreenshotsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -102,11 +111,11 @@
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/ScreenshotsPage-CmPGicmh.css"
|
||||
"assets/ScreenshotsPage-B66M6Olm.css"
|
||||
]
|
||||
},
|
||||
"src/pages/VerifyResultPage.vue": {
|
||||
"file": "assets/VerifyResultPage-B_i4AM-j.js",
|
||||
"file": "assets/VerifyResultPage-DgCNTQ4L.js",
|
||||
"name": "VerifyResultPage",
|
||||
"src": "src/pages/VerifyResultPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
|
||||
1
static/app/assets/AccountsPage-Bcis23qR.js
Normal file
1
static/app/assets/AccountsPage-Bcis23qR.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{_ as M,r as j,a as p,c as B,o as A,b as S,d as t,w as o,e as m,u as H,f as g,g as n,h as U,i as x,j as N,t as q,k as E,E as d}from"./index-DvbGwVAp.js";import{g as z,f as F,c as G}from"./auth-yhlOdREj.js";const J={class:"auth-wrap"},O={class:"hint app-muted"},Q={class:"captcha-row"},W=["src"],X={class:"actions"},Y={__name:"RegisterPage",setup(Z){const T=H(),a=j({username:"",password:"",confirm_password:"",email:"",captcha:""}),v=p(!1),f=p(""),b=p(""),h=p(!1),l=p(""),_=p(""),V=p(""),K=B(()=>v.value?"邮箱 *":"邮箱(可选)"),P=B(()=>v.value?"必填,用于账号验证":"选填,用于接收审核通知");async function w(){try{const u=await z();b.value=u?.session_id||"",f.value=u?.captcha_image||"",a.captcha=""}catch{b.value="",f.value=""}}async function R(){try{const u=await F();v.value=!!u?.register_verify_enabled}catch{v.value=!1}}function D(){l.value="",_.value="",V.value=""}async function k(){D();const u=a.username.trim(),e=a.password,y=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){l.value="用户名至少3个字符",d.error(l.value);return}if(e.length<6){l.value="密码至少6个字符",d.error(l.value);return}if(e!==y){l.value="两次输入的密码不一致",d.error(l.value);return}if(v.value&&!s){l.value="请填写邮箱地址用于账号验证",d.error(l.value);return}if(s&&!s.includes("@")){l.value="邮箱格式不正确",d.error(l.value);return}if(!i){l.value="请输入验证码",d.error(l.value);return}h.value=!0;try{const c=await G({username:u,password:e,email:s,captcha_session:b.value,captcha:i});_.value=c?.message||"注册成功",V.value=c?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",d.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(c){const C=c?.response?.data;l.value=C?.error||"注册失败",d.error(l.value),await w()}finally{h.value=!1}}function I(){T.push("/login")}return A(async()=>{await w(),await R()}),(u,e)=>{const y=m("el-alert"),s=m("el-input"),i=m("el-form-item"),c=m("el-button"),C=m("el-form"),L=m("el-card");return g(),S("div",J,[t(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),l.value?(g(),U(y,{key:0,type:"error",closable:!1,title:l.value,"show-icon":"",class:"alert"},null,8,["title"])):x("",!0),_.value?(g(),U(y,{key:1,type:"success",closable:!1,title:_.value,description:V.value,"show-icon":"",class:"alert"},null,8,["title","description"])):x("",!0),t(C,{"label-position":"top"},{default:o(()=>[t(i,{label:"用户名 *"},{default:o(()=>[t(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),t(i,{label:"密码 *"},{default:o(()=>[t(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少6个字符",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少6个字符",-1))]),_:1}),t(i,{label:"确认密码 *"},{default:o(()=>[t(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:N(k,["enter"])},null,8,["modelValue"])]),_:1}),t(i,{label:K.value},{default:o(()=>[t(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",O,q(P.value),1)]),_:1},8,["label"]),t(i,{label:"验证码 *"},{default:o(()=>[n("div",Q,[t(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:N(k,["enter"])},null,8,["modelValue"]),f.value?(g(),S("img",{key:0,class:"captcha-img",src:f.value,alt:"验证码",title:"点击刷新",onClick:w},null,8,W)):x("",!0),t(c,{onClick:w},{default:o(()=>[...e[7]||(e[7]=[E("刷新",-1)])]),_:1})])]),_:1})]),_:1}),t(c,{type:"primary",class:"submit-btn",loading:h.value,onClick:k},{default:o(()=>[...e[8]||(e[8]=[E("注册",-1)])]),_:1},8,["loading"]),n("div",X,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),t(c,{link:"",type:"primary",onClick:I},{default:o(()=>[...e[9]||(e[9]=[E("立即登录",-1)])]),_:1})])]),_:1})])}}},ae=M(Y,[["__scopeId","data-v-32684b4d"]]);export{ae as default};
|
||||
import{_ as M,r as j,a as p,c as B,o as A,b as S,d as t,w as o,e as m,u as H,f as g,g as n,h as U,i as x,j as N,t as q,k as E,E as d}from"./index-BstQMnWL.js";import{g as z,f as F,c as G}from"./auth-CgGRYkq1.js";const J={class:"auth-wrap"},O={class:"hint app-muted"},Q={class:"captcha-row"},W=["src"],X={class:"actions"},Y={__name:"RegisterPage",setup(Z){const T=H(),a=j({username:"",password:"",confirm_password:"",email:"",captcha:""}),v=p(!1),f=p(""),b=p(""),h=p(!1),l=p(""),_=p(""),V=p(""),K=B(()=>v.value?"邮箱 *":"邮箱(可选)"),P=B(()=>v.value?"必填,用于账号验证":"选填,用于接收审核通知");async function w(){try{const u=await z();b.value=u?.session_id||"",f.value=u?.captcha_image||"",a.captcha=""}catch{b.value="",f.value=""}}async function R(){try{const u=await F();v.value=!!u?.register_verify_enabled}catch{v.value=!1}}function D(){l.value="",_.value="",V.value=""}async function k(){D();const u=a.username.trim(),e=a.password,y=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){l.value="用户名至少3个字符",d.error(l.value);return}if(e.length<6){l.value="密码至少6个字符",d.error(l.value);return}if(e!==y){l.value="两次输入的密码不一致",d.error(l.value);return}if(v.value&&!s){l.value="请填写邮箱地址用于账号验证",d.error(l.value);return}if(s&&!s.includes("@")){l.value="邮箱格式不正确",d.error(l.value);return}if(!i){l.value="请输入验证码",d.error(l.value);return}h.value=!0;try{const c=await G({username:u,password:e,email:s,captcha_session:b.value,captcha:i});_.value=c?.message||"注册成功",V.value=c?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",d.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(c){const C=c?.response?.data;l.value=C?.error||"注册失败",d.error(l.value),await w()}finally{h.value=!1}}function I(){T.push("/login")}return A(async()=>{await w(),await R()}),(u,e)=>{const y=m("el-alert"),s=m("el-input"),i=m("el-form-item"),c=m("el-button"),C=m("el-form"),L=m("el-card");return g(),S("div",J,[t(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),l.value?(g(),U(y,{key:0,type:"error",closable:!1,title:l.value,"show-icon":"",class:"alert"},null,8,["title"])):x("",!0),_.value?(g(),U(y,{key:1,type:"success",closable:!1,title:_.value,description:V.value,"show-icon":"",class:"alert"},null,8,["title","description"])):x("",!0),t(C,{"label-position":"top"},{default:o(()=>[t(i,{label:"用户名 *"},{default:o(()=>[t(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),t(i,{label:"密码 *"},{default:o(()=>[t(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少6个字符",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少6个字符",-1))]),_:1}),t(i,{label:"确认密码 *"},{default:o(()=>[t(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:N(k,["enter"])},null,8,["modelValue"])]),_:1}),t(i,{label:K.value},{default:o(()=>[t(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",O,q(P.value),1)]),_:1},8,["label"]),t(i,{label:"验证码 *"},{default:o(()=>[n("div",Q,[t(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:N(k,["enter"])},null,8,["modelValue"]),f.value?(g(),S("img",{key:0,class:"captcha-img",src:f.value,alt:"验证码",title:"点击刷新",onClick:w},null,8,W)):x("",!0),t(c,{onClick:w},{default:o(()=>[...e[7]||(e[7]=[E("刷新",-1)])]),_:1})])]),_:1})]),_:1}),t(c,{type:"primary",class:"submit-btn",loading:h.value,onClick:k},{default:o(()=>[...e[8]||(e[8]=[E("注册",-1)])]),_:1},8,["loading"]),n("div",X,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),t(c,{link:"",type:"primary",onClick:I},{default:o(()=>[...e[9]||(e[9]=[E("立即登录",-1)])]),_:1})])]),_:1})])}}},ae=M(Y,[["__scopeId","data-v-32684b4d"]]);export{ae as default};
|
||||
@@ -1 +1 @@
|
||||
import{_ as L,a as n,l as M,r as U,c as j,o as F,m as K,b as v,d as s,w as a,e as l,u as D,f as m,g as w,F as T,k,h as q,i as x,j as z,t as G,E as y}from"./index-DvbGwVAp.js";import{d as H}from"./auth-yhlOdREj.js";import{v as J}from"./password-7ryi82gE.js";const O={class:"auth-wrap"},Q={class:"actions"},W={class:"actions"},X={key:0,class:"app-muted"},Y={__name:"ResetPasswordPage",setup(Z){const B=M(),A=D(),r=n(String(B.params.token||"")),i=n(!0),b=n(""),t=U({newPassword:"",confirmPassword:""}),g=n(!1),f=n(""),d=n(0);let u=null;function C(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=j(()=>!!(i.value&&r.value&&!f.value));function S(){A.push("/login")}function N(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=J(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}g.value=!0;try{await H({token:r.value,new_password:o}),f.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),N()}catch(p){const _=p?.response?.data;y.error(_?.error||"重置失败")}finally{g.value=!1}}return F(()=>{const o=C();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),K(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),_=l("el-input"),h=l("el-form-item"),R=l("el-form"),E=l("el-card");return m(),v("div",O,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=w("div",{class:"brand"},[w("div",{class:"brand-title"},"知识管理平台"),w("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(m(),v(T,{key:1},[f.value?(m(),q(c,{key:0,type:"success",closable:!1,title:"重置成功",description:f.value,"show-icon":"",class:"alert"},null,8,["description"])):x("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(h,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(_,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=P=>t.newPassword=P),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(h,{label:"确认密码"},{default:a(()=>[s(_,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=P=>t.confirmPassword=P),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:z(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:g.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),w("div",W,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(m(),v("span",X,G(d.value)+" 秒后自动跳转…",1)):x("",!0)])],64)):(m(),v(T,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),w("div",Q,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},se=L(Y,[["__scopeId","data-v-0bbb511c"]]);export{se as default};
|
||||
import{_ as L,a as n,l as M,r as U,c as j,o as F,m as K,b as v,d as s,w as a,e as l,u as D,f as m,g as w,F as T,k,h as q,i as x,j as z,t as G,E as y}from"./index-BstQMnWL.js";import{d as H}from"./auth-CgGRYkq1.js";import{v as J}from"./password-7ryi82gE.js";const O={class:"auth-wrap"},Q={class:"actions"},W={class:"actions"},X={key:0,class:"app-muted"},Y={__name:"ResetPasswordPage",setup(Z){const B=M(),A=D(),r=n(String(B.params.token||"")),i=n(!0),b=n(""),t=U({newPassword:"",confirmPassword:""}),g=n(!1),f=n(""),d=n(0);let u=null;function C(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=j(()=>!!(i.value&&r.value&&!f.value));function S(){A.push("/login")}function N(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=J(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}g.value=!0;try{await H({token:r.value,new_password:o}),f.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),N()}catch(p){const _=p?.response?.data;y.error(_?.error||"重置失败")}finally{g.value=!1}}return F(()=>{const o=C();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),K(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),_=l("el-input"),h=l("el-form-item"),R=l("el-form"),E=l("el-card");return m(),v("div",O,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=w("div",{class:"brand"},[w("div",{class:"brand-title"},"知识管理平台"),w("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(m(),v(T,{key:1},[f.value?(m(),q(c,{key:0,type:"success",closable:!1,title:"重置成功",description:f.value,"show-icon":"",class:"alert"},null,8,["description"])):x("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(h,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(_,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=P=>t.newPassword=P),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(h,{label:"确认密码"},{default:a(()=>[s(_,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=P=>t.confirmPassword=P),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:z(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:g.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),w("div",W,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(m(),v("span",X,G(d.value)+" 秒后自动跳转…",1)):x("",!0)])],64)):(m(),v(T,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),w("div",Q,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},se=L(Y,[["__scopeId","data-v-0bbb511c"]]);export{se as default};
|
||||
@@ -1 +0,0 @@
|
||||
.card[data-v-b4b9e229]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.title[data-v-b4b9e229]{margin:0 0 6px;font-size:16px;font-weight:800}
|
||||
1
static/app/assets/SchedulesPage-BXyRqadY.js
Normal file
1
static/app/assets/SchedulesPage-BXyRqadY.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{_ as t,h as o,w as c,e as d,f as r,g as s}from"./index-DvbGwVAp.js";const n={};function l(_,e){const a=d("el-card");return r(),o(a,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:c(()=>[...e[0]||(e[0]=[s("h2",{class:"title"},"定时任务",-1),s("div",{class:"app-muted"},"阶段1:页面壳子已就绪,功能将在后续阶段迁移。",-1)])]),_:1})}const f=t(n,[["render",l],["__scopeId","data-v-b4b9e229"]]);export{f as default};
|
||||
1
static/app/assets/SchedulesPage-Gu_OsDUd.css
Normal file
1
static/app/assets/SchedulesPage-Gu_OsDUd.css
Normal file
@@ -0,0 +1 @@
|
||||
.page[data-v-ee36bae1]{display:flex;flex-direction:column;gap:12px}.vip-alert[data-v-ee36bae1]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.vip-actions[data-v-ee36bae1]{margin-top:10px}.panel[data-v-ee36bae1]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.panel-head[data-v-ee36bae1]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:10px}.panel-title[data-v-ee36bae1]{font-size:16px;font-weight:900}.panel-actions[data-v-ee36bae1]{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.grid[data-v-ee36bae1]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px}.schedule-card[data-v-ee36bae1]{border-radius:14px;border:1px solid var(--app-border)}.schedule-top[data-v-ee36bae1]{display:flex;justify-content:space-between;gap:12px}.schedule-main[data-v-ee36bae1]{min-width:0;flex:1}.schedule-title[data-v-ee36bae1]{display:flex;align-items:center;justify-content:space-between;gap:10px}.schedule-name[data-v-ee36bae1]{font-size:14px;font-weight:900;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.schedule-meta[data-v-ee36bae1]{margin-top:6px;display:flex;gap:10px;flex-wrap:wrap;font-size:12px}.schedule-actions[data-v-ee36bae1]{margin-top:12px;display:flex;gap:8px;flex-wrap:wrap}.logs[data-v-ee36bae1]{display:flex;flex-direction:column;gap:10px}.log-card[data-v-ee36bae1]{border-radius:12px;border:1px solid var(--app-border)}.log-head[data-v-ee36bae1]{display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:12px}.log-body[data-v-ee36bae1]{margin-top:8px;font-size:13px;line-height:1.6}.log-error[data-v-ee36bae1]{margin-top:6px;color:#b91c1c}.vip-body[data-v-ee36bae1]{padding:12px 0 0}.vip-tip[data-v-ee36bae1]{margin-top:10px;font-size:13px;line-height:1.6}@media(max-width:480px){.grid[data-v-ee36bae1]{grid-template-columns:1fr}}
|
||||
1
static/app/assets/ScreenshotsPage-B66M6Olm.css
Normal file
1
static/app/assets/ScreenshotsPage-B66M6Olm.css
Normal file
@@ -0,0 +1 @@
|
||||
.panel[data-v-ff08abf8]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.panel-head[data-v-ff08abf8]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:10px}.panel-title[data-v-ff08abf8]{font-size:16px;font-weight:900}.panel-actions[data-v-ff08abf8]{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.grid[data-v-ff08abf8]{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.shot-card[data-v-ff08abf8]{border-radius:14px;border:1px solid var(--app-border);overflow:hidden}.shot-img[data-v-ff08abf8]{width:100%;aspect-ratio:16/9;object-fit:cover;cursor:pointer;display:block}.shot-body[data-v-ff08abf8]{padding:12px}.shot-name[data-v-ff08abf8]{font-size:13px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shot-meta[data-v-ff08abf8]{margin-top:4px;font-size:12px}.shot-actions[data-v-ff08abf8]{margin-top:10px;display:flex;flex-wrap:wrap;gap:6px}.preview[data-v-ff08abf8]{display:flex;justify-content:center}.preview-img[data-v-ff08abf8]{max-width:100%;max-height:78vh;object-fit:contain;border-radius:10px;border:1px solid var(--app-border);background:#fff}@media(max-width:480px){.grid[data-v-ff08abf8]{grid-template-columns:1fr}}
|
||||
1
static/app/assets/ScreenshotsPage-BwyU04c1.js
Normal file
1
static/app/assets/ScreenshotsPage-BwyU04c1.js
Normal file
@@ -0,0 +1 @@
|
||||
import{p as C,_ as P,a as y,o as R,h as w,w as s,e as _,f as c,g as o,b,d as i,k as f,F as B,v as D,t as T,x as I,E as l}from"./index-BstQMnWL.js";async function F(){const{data:d}=await C.get("/screenshots");return d}async function L(d){const{data:u}=await C.delete(`/screenshots/${encodeURIComponent(d)}`);return u}async function O(){const{data:d}=await C.post("/screenshots/clear",{});return d}const j={class:"panel-head"},q={class:"panel-actions"},G={key:1,class:"grid"},H=["src","alt","onClick"],J={class:"shot-body"},K=["title"],Q={class:"shot-meta app-muted"},W={class:"shot-actions"},X={class:"preview"},Y=["src","alt"],Z={__name:"ScreenshotsPage",setup(d){const u=y(!1),r=y([]),p=y(!1),g=y(""),h=y("");function v(a){return`/screenshots/${encodeURIComponent(a)}`}async function x(){u.value=!0;try{const a=await F();r.value=Array.isArray(a)?a:[]}catch(a){a?.response?.status===401&&(window.location.href="/login"),r.value=[]}finally{u.value=!1}}function S(a){h.value=a.display_name||a.filename||"截图预览",g.value=v(a.filename),p.value=!0}async function U(){try{await I.confirm("确定要清空全部截图吗?","清空截图",{confirmButtonText:"清空",cancelButtonText:"取消",type:"warning"})}catch{return}try{const a=await O();if(a?.success){l.success(`已清空(删除 ${a?.deleted||0} 张)`),r.value=[],p.value=!1;return}l.error(a?.error||"操作失败")}catch(a){const e=a?.response?.data;l.error(e?.error||"操作失败")}}async function V(a){try{await I.confirm(`确定要删除截图「${a.display_name||a.filename}」吗?`,"删除截图",{confirmButtonText:"删除",cancelButtonText:"取消",type:"warning"})}catch{return}try{const e=await L(a.filename);if(e?.success){r.value=r.value.filter(n=>n.filename!==a.filename),g.value.includes(encodeURIComponent(a.filename))&&(p.value=!1),l.success("已删除");return}l.error(e?.error||"删除失败")}catch(e){const n=e?.response?.data;l.error(n?.error||"删除失败")}}async function E(a){const e=`${window.location.origin}${v(a.filename)}`;try{await navigator.clipboard.writeText(e),l.success("链接已复制")}catch{l.warning("复制失败,请手动复制链接")}}async function z(a){const e=v(a.filename);try{const m=await(await fetch(e,{credentials:"include"})).blob();await navigator.clipboard.write([new ClipboardItem({[m.type]:m})]),l.success("图片已复制")}catch{await E(a)}}function A(a){const e=document.createElement("a");e.href=v(a.filename),e.download=a.display_name||a.filename,document.body.appendChild(e),e.click(),e.remove()}return R(x),(a,e)=>{const n=_("el-button"),m=_("el-skeleton"),M=_("el-empty"),$=_("el-card"),N=_("el-dialog");return c(),w($,{shadow:"never",class:"panel","body-style":{padding:"14px"}},{default:s(()=>[o("div",j,[e[4]||(e[4]=o("div",{class:"panel-title"},"截图管理",-1)),o("div",q,[i(n,{loading:u.value,onClick:x},{default:s(()=>[...e[2]||(e[2]=[f("刷新",-1)])]),_:1},8,["loading"]),i(n,{type:"danger",plain:"",disabled:r.value.length===0,onClick:U},{default:s(()=>[...e[3]||(e[3]=[f("清空全部",-1)])]),_:1},8,["disabled"])])]),u.value?(c(),w(m,{key:0,rows:6,animated:""})):(c(),b(B,{key:1},[r.value.length===0?(c(),w(M,{key:0,description:"暂无截图"})):(c(),b("div",G,[(c(!0),b(B,null,D(r.value,t=>(c(),w($,{key:t.filename,shadow:"never",class:"shot-card","body-style":{padding:"0"}},{default:s(()=>[o("img",{class:"shot-img",src:v(t.filename),alt:t.display_name||t.filename,loading:"lazy",onClick:k=>S(t)},null,8,H),o("div",J,[o("div",{class:"shot-name",title:t.display_name||t.filename},T(t.display_name||t.filename),9,K),o("div",Q,T(t.created||""),1),o("div",W,[i(n,{size:"small",text:"",type:"primary",onClick:k=>z(t)},{default:s(()=>[...e[5]||(e[5]=[f("复制图片",-1)])]),_:1},8,["onClick"]),i(n,{size:"small",text:"",onClick:k=>A(t)},{default:s(()=>[...e[6]||(e[6]=[f("下载",-1)])]),_:1},8,["onClick"]),i(n,{size:"small",text:"",type:"danger",onClick:k=>V(t)},{default:s(()=>[...e[7]||(e[7]=[f("删除",-1)])]),_:1},8,["onClick"])])])]),_:2},1024))),128))]))],64)),i(N,{modelValue:p.value,"onUpdate:modelValue":e[1]||(e[1]=t=>p.value=t),title:h.value,width:"min(920px, 94vw)"},{footer:s(()=>[i(n,{onClick:e[0]||(e[0]=t=>p.value=!1)},{default:s(()=>[...e[8]||(e[8]=[f("关闭",-1)])]),_:1})]),default:s(()=>[o("div",X,[o("img",{src:g.value,alt:h.value,class:"preview-img"},null,8,Y)])]),_:1},8,["modelValue","title"])]),_:1})}}},ae=P(Z,[["__scopeId","data-v-ff08abf8"]]);export{ae as default};
|
||||
@@ -1 +0,0 @@
|
||||
.card[data-v-08f8d2d3]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.title[data-v-08f8d2d3]{margin:0 0 6px;font-size:16px;font-weight:800}
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as t,h as o,w as c,e as d,f as r,g as s}from"./index-DvbGwVAp.js";const n={};function _(l,e){const a=d("el-card");return r(),o(a,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:c(()=>[...e[0]||(e[0]=[s("h2",{class:"title"},"截图管理",-1),s("div",{class:"app-muted"},"阶段1:页面壳子已就绪,功能将在后续阶段迁移。",-1)])]),_:1})}const f=t(n,[["render",_],["__scopeId","data-v-08f8d2d3"]]);export{f as default};
|
||||
@@ -1 +1 @@
|
||||
import{_ as U,a as o,c as I,o as E,m as R,b as k,d as i,w as s,e as d,u as W,f as _,g as l,i as B,h as $,k as T,t as v}from"./index-DvbGwVAp.js";const j={class:"auth-wrap"},z={class:"actions"},D={key:0,class:"countdown app-muted"},M={__name:"VerifyResultPage",setup(q){const x=W(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function C(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function N(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const A=I(()=>!!(r.value&&u.value)),b=I(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await x.push(e)}}function P(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return E(()=>{const e=C();N(e),P()}),R(()=>{a&&window.clearInterval(a)}),(e,t)=>{const h=d("el-button"),V=d("el-result"),L=d("el-card");return _(),k("div",j,[i(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(V,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",z,[i(h,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[T(v(w.value),1)]),_:1}),A.value?(_(),$(h,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[T(v(r.value),1)]),_:1})):B("",!0)]),b.value?(_(),k("div",D,v(n.value)+" 秒后自动跳转... ",1)):B("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=U(M,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
||||
import{_ as U,a as o,c as I,o as E,m as R,b as k,d as i,w as s,e as d,u as W,f as _,g as l,i as B,h as $,k as T,t as v}from"./index-BstQMnWL.js";const j={class:"auth-wrap"},z={class:"actions"},D={key:0,class:"countdown app-muted"},M={__name:"VerifyResultPage",setup(q){const x=W(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function C(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function N(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const A=I(()=>!!(r.value&&u.value)),b=I(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await x.push(e)}}function P(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return E(()=>{const e=C();N(e),P()}),R(()=>{a&&window.clearInterval(a)}),(e,t)=>{const h=d("el-button"),V=d("el-result"),L=d("el-card");return _(),k("div",j,[i(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(V,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",z,[i(h,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[T(v(w.value),1)]),_:1}),A.value?(_(),$(h,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[T(v(r.value),1)]),_:1})):B("",!0)]),b.value?(_(),k("div",D,v(n.value)+" 秒后自动跳转... ",1)):B("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=U(M,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
||||
1
static/app/assets/accounts-DI7tHfNj.js
Normal file
1
static/app/assets/accounts-DI7tHfNj.js
Normal file
@@ -0,0 +1 @@
|
||||
import{p as c}from"./index-BstQMnWL.js";async function o(t={}){const{data:a}=await c.get("/accounts",{params:t});return a}async function u(t){const{data:a}=await c.post("/accounts",t);return a}async function r(t,a){const{data:n}=await c.put(`/accounts/${t}`,a);return n}async function e(t){const{data:a}=await c.delete(`/accounts/${t}`);return a}async function i(t,a){const{data:n}=await c.put(`/accounts/${t}/remark`,a);return n}async function p(t,a){const{data:n}=await c.post(`/accounts/${t}/start`,a);return n}async function d(t){const{data:a}=await c.post(`/accounts/${t}/stop`,{});return a}async function f(t){const{data:a}=await c.post("/accounts/batch/start",t);return a}async function w(t){const{data:a}=await c.post("/accounts/batch/stop",t);return a}async function y(){const{data:t}=await c.post("/accounts/clear",{});return t}async function A(t,a={}){const{data:n}=await c.post(`/accounts/${t}/screenshot`,a);return n}export{w as a,f as b,y as c,d,e,o as f,u as g,i as h,p as s,A as t,r as u};
|
||||
@@ -1 +1 @@
|
||||
import{p as s}from"./index-DvbGwVAp.js";async function r(){const{data:t}=await s.get("/email/verify-status");return t}async function e(){const{data:t}=await s.post("/generate_captcha",{});return t}async function o(t){const{data:a}=await s.post("/login",t);return a}async function i(t){const{data:a}=await s.post("/register",t);return a}async function c(t){const{data:a}=await s.post("/resend-verify-email",t);return a}async function u(t){const{data:a}=await s.post("/forgot-password",t);return a}async function f(t){const{data:a}=await s.post("/reset_password_request",t);return a}async function d(t){const{data:a}=await s.post("/reset-password-confirm",t);return a}export{u as a,c as b,i as c,d,r as f,e as g,o as l,f as r};
|
||||
import{p as s}from"./index-BstQMnWL.js";async function r(){const{data:t}=await s.get("/email/verify-status");return t}async function e(){const{data:t}=await s.post("/generate_captcha",{});return t}async function o(t){const{data:a}=await s.post("/login",t);return a}async function i(t){const{data:a}=await s.post("/register",t);return a}async function c(t){const{data:a}=await s.post("/resend-verify-email",t);return a}async function u(t){const{data:a}=await s.post("/forgot-password",t);return a}async function f(t){const{data:a}=await s.post("/reset_password_request",t);return a}async function d(t){const{data:a}=await s.post("/reset-password-confirm",t);return a}export{u as a,c as b,i as c,d,r as f,e as g,o as l,f as r};
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>知识管理平台</title>
|
||||
<script type="module" crossorigin src="./assets/index-DvbGwVAp.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-BstQMnWL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-Baiuy_-z.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user