feat(app): add announcements, feedback, settings (stage 5)
This commit is contained in:
12
app-frontend/src/api/announcements.js
Normal file
12
app-frontend/src/api/announcements.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { publicApi } from './http'
|
||||
|
||||
export async function fetchActiveAnnouncement() {
|
||||
const { data } = await publicApi.get('/announcements/active')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function dismissAnnouncement(announcementId) {
|
||||
const { data } = await publicApi.post(`/announcements/${announcementId}/dismiss`, {})
|
||||
return data
|
||||
}
|
||||
|
||||
12
app-frontend/src/api/feedback.js
Normal file
12
app-frontend/src/api/feedback.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { publicApi } from './http'
|
||||
|
||||
export async function submitFeedback(payload) {
|
||||
const { data } = await publicApi.post('/feedback', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchMyFeedbacks() {
|
||||
const { data } = await publicApi.get('/feedback')
|
||||
return data
|
||||
}
|
||||
|
||||
32
app-frontend/src/api/settings.js
Normal file
32
app-frontend/src/api/settings.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { publicApi } from './http'
|
||||
|
||||
export async function fetchUserEmail() {
|
||||
const { data } = await publicApi.get('/user/email')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function bindEmail(payload) {
|
||||
const { data } = await publicApi.post('/user/bind-email', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function unbindEmail() {
|
||||
const { data } = await publicApi.post('/user/unbind-email', {})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchEmailNotify() {
|
||||
const { data } = await publicApi.get('/user/email-notify')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function updateEmailNotify(payload) {
|
||||
const { data } = await publicApi.post('/user/email-notify', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function changePassword(payload) {
|
||||
const { data } = await publicApi.post('/user/password', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Calendar, Camera, User } from '@element-plus/icons-vue'
|
||||
|
||||
import { fetchActiveAnnouncement, dismissAnnouncement } from '../api/announcements'
|
||||
import { fetchMyFeedbacks, submitFeedback } from '../api/feedback'
|
||||
import {
|
||||
bindEmail,
|
||||
changePassword,
|
||||
fetchEmailNotify,
|
||||
fetchUserEmail,
|
||||
unbindEmail,
|
||||
updateEmailNotify,
|
||||
} from '../api/settings'
|
||||
import { useUserStore } from '../stores/user'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -14,6 +24,42 @@ const isMobile = ref(false)
|
||||
const drawerOpen = ref(false)
|
||||
let mediaQuery
|
||||
|
||||
const announcementOpen = ref(false)
|
||||
const announcement = ref(null)
|
||||
const announcementLoading = ref(false)
|
||||
|
||||
const feedbackOpen = ref(false)
|
||||
const feedbackTab = ref('new')
|
||||
const feedbackSubmitting = ref(false)
|
||||
const feedbackLoading = ref(false)
|
||||
const myFeedbacks = ref([])
|
||||
const feedbackForm = reactive({
|
||||
title: '',
|
||||
description: '',
|
||||
contact: '',
|
||||
})
|
||||
|
||||
const settingsOpen = ref(false)
|
||||
const settingsTab = ref('email')
|
||||
|
||||
const emailLoading = ref(false)
|
||||
const bindEmailLoading = ref(false)
|
||||
const emailInfo = reactive({
|
||||
email: '',
|
||||
email_verified: false,
|
||||
})
|
||||
const bindEmailValue = ref('')
|
||||
|
||||
const emailNotifyLoading = ref(false)
|
||||
const emailNotifyEnabled = ref(true)
|
||||
|
||||
const passwordLoading = ref(false)
|
||||
const passwordForm = reactive({
|
||||
current_password: '',
|
||||
new_password: '',
|
||||
confirm_password: '',
|
||||
})
|
||||
|
||||
function syncIsMobile() {
|
||||
isMobile.value = Boolean(mediaQuery?.matches)
|
||||
if (!isMobile.value) drawerOpen.value = false
|
||||
@@ -27,6 +73,8 @@ onMounted(() => {
|
||||
userStore.refreshVipInfo().catch(() => {
|
||||
window.location.href = '/login'
|
||||
})
|
||||
|
||||
loadAnnouncement()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -60,6 +108,258 @@ async function logout() {
|
||||
await userStore.logout()
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
function openFeedbackForm() {
|
||||
feedbackTab.value = 'new'
|
||||
feedbackForm.title = ''
|
||||
feedbackForm.description = ''
|
||||
feedbackForm.contact = ''
|
||||
feedbackOpen.value = true
|
||||
}
|
||||
|
||||
async function openMyFeedbacks() {
|
||||
feedbackTab.value = 'list'
|
||||
feedbackOpen.value = true
|
||||
await loadMyFeedbacks()
|
||||
}
|
||||
|
||||
async function loadMyFeedbacks() {
|
||||
feedbackLoading.value = true
|
||||
try {
|
||||
const list = await fetchMyFeedbacks()
|
||||
myFeedbacks.value = Array.isArray(list) ? list : []
|
||||
} catch {
|
||||
myFeedbacks.value = []
|
||||
} finally {
|
||||
feedbackLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function feedbackStatusLabel(status) {
|
||||
if (status === 'replied') return '已回复'
|
||||
if (status === 'closed') return '已关闭'
|
||||
return '待处理'
|
||||
}
|
||||
|
||||
function feedbackStatusTagType(status) {
|
||||
if (status === 'replied') return 'success'
|
||||
if (status === 'closed') return 'info'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
async function submitFeedbackForm() {
|
||||
const title = feedbackForm.title.trim()
|
||||
const description = feedbackForm.description.trim()
|
||||
const contact = feedbackForm.contact.trim()
|
||||
|
||||
if (!title || !description) {
|
||||
ElMessage.error('标题和描述不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
feedbackSubmitting.value = true
|
||||
try {
|
||||
const res = await submitFeedback({ title, description, contact })
|
||||
ElMessage.success(res?.message || '反馈提交成功')
|
||||
feedbackOpen.value = false
|
||||
feedbackForm.title = ''
|
||||
feedbackForm.description = ''
|
||||
feedbackForm.contact = ''
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '提交失败')
|
||||
} finally {
|
||||
feedbackSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function openSettings() {
|
||||
settingsOpen.value = true
|
||||
settingsTab.value = 'email'
|
||||
await loadSettings()
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
await Promise.all([loadEmailInfo(), loadEmailNotify()])
|
||||
}
|
||||
|
||||
async function loadEmailInfo() {
|
||||
emailLoading.value = true
|
||||
try {
|
||||
const data = await fetchUserEmail()
|
||||
emailInfo.email = data?.email || ''
|
||||
emailInfo.email_verified = Boolean(data?.email_verified)
|
||||
bindEmailValue.value = emailInfo.email || ''
|
||||
} catch {
|
||||
emailInfo.email = ''
|
||||
emailInfo.email_verified = false
|
||||
bindEmailValue.value = ''
|
||||
} finally {
|
||||
emailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadEmailNotify() {
|
||||
emailNotifyLoading.value = true
|
||||
try {
|
||||
const data = await fetchEmailNotify()
|
||||
emailNotifyEnabled.value = Boolean(data?.enabled)
|
||||
} catch {
|
||||
emailNotifyEnabled.value = true
|
||||
} finally {
|
||||
emailNotifyLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onBindEmail() {
|
||||
const email = bindEmailValue.value.trim().toLowerCase()
|
||||
if (!email) {
|
||||
ElMessage.error('请输入邮箱地址')
|
||||
return
|
||||
}
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(email)) {
|
||||
ElMessage.error('邮箱格式不正确')
|
||||
return
|
||||
}
|
||||
|
||||
bindEmailLoading.value = true
|
||||
try {
|
||||
const res = await bindEmail({ email })
|
||||
ElMessage.success(res?.message || '验证邮件已发送')
|
||||
emailInfo.email = email
|
||||
emailInfo.email_verified = false
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '绑定失败')
|
||||
} finally {
|
||||
bindEmailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onUnbindEmail() {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要解绑当前邮箱吗?', '解绑邮箱', {
|
||||
confirmButtonText: '解绑',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await unbindEmail()
|
||||
if (res?.success) {
|
||||
ElMessage.success(res?.message || '邮箱已解绑')
|
||||
await loadEmailInfo()
|
||||
return
|
||||
}
|
||||
ElMessage.error(res?.error || '解绑失败')
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '解绑失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function onToggleEmailNotify(value) {
|
||||
const previous = emailNotifyEnabled.value
|
||||
emailNotifyEnabled.value = Boolean(value)
|
||||
emailNotifyLoading.value = true
|
||||
try {
|
||||
const res = await updateEmailNotify({ enabled: Boolean(value) })
|
||||
if (res?.success) {
|
||||
ElMessage.success('已更新')
|
||||
return
|
||||
}
|
||||
emailNotifyEnabled.value = previous
|
||||
ElMessage.error(res?.error || '更新失败')
|
||||
} catch (e) {
|
||||
emailNotifyEnabled.value = previous
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '更新失败')
|
||||
} finally {
|
||||
emailNotifyLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onChangePassword() {
|
||||
const currentPassword = passwordForm.current_password
|
||||
const newPassword = passwordForm.new_password
|
||||
const confirmPassword = passwordForm.confirm_password
|
||||
|
||||
if (!currentPassword || !newPassword || !confirmPassword) {
|
||||
ElMessage.error('请填写完整信息')
|
||||
return
|
||||
}
|
||||
if (String(newPassword).length < 6) {
|
||||
ElMessage.error('新密码至少6位')
|
||||
return
|
||||
}
|
||||
if (newPassword !== confirmPassword) {
|
||||
ElMessage.error('两次输入的新密码不一致')
|
||||
return
|
||||
}
|
||||
|
||||
passwordLoading.value = true
|
||||
try {
|
||||
const res = await changePassword({ current_password: currentPassword, new_password: newPassword })
|
||||
if (res?.success) {
|
||||
ElMessage.success('密码修改成功')
|
||||
passwordForm.current_password = ''
|
||||
passwordForm.new_password = ''
|
||||
passwordForm.confirm_password = ''
|
||||
return
|
||||
}
|
||||
ElMessage.error(res?.error || '修改失败')
|
||||
} catch (e) {
|
||||
const data = e?.response?.data
|
||||
ElMessage.error(data?.error || '修改失败')
|
||||
} finally {
|
||||
passwordLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAnnouncement() {
|
||||
announcementLoading.value = true
|
||||
try {
|
||||
const data = await fetchActiveAnnouncement()
|
||||
const ann = data?.announcement
|
||||
if (!ann?.id) return
|
||||
|
||||
const sessionKey = `announcement_closed_${ann.id}`
|
||||
if (window.sessionStorage.getItem(sessionKey) === '1') return
|
||||
|
||||
announcement.value = ann
|
||||
announcementOpen.value = true
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
announcementLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function closeAnnouncementOnce() {
|
||||
const ann = announcement.value
|
||||
if (ann?.id) window.sessionStorage.setItem(`announcement_closed_${ann.id}`, '1')
|
||||
announcementOpen.value = false
|
||||
}
|
||||
|
||||
async function dismissAnnouncementPermanently() {
|
||||
const ann = announcement.value
|
||||
if (!ann?.id) {
|
||||
announcementOpen.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await dismissAnnouncement(ann.id)
|
||||
if (res?.success) ElMessage.success('已永久关闭')
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
announcementOpen.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -96,6 +396,8 @@ async function logout() {
|
||||
({{ userStore.vipDaysLeft }}天后到期)
|
||||
</span>
|
||||
</div>
|
||||
<el-button text type="primary" @click="openFeedbackForm">反馈</el-button>
|
||||
<el-button text @click="openSettings">设置</el-button>
|
||||
<el-button type="primary" plain @click="logout">退出</el-button>
|
||||
</div>
|
||||
</el-header>
|
||||
@@ -122,9 +424,191 @@ async function logout() {
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<div class="drawer-actions">
|
||||
<el-button text type="primary" style="width: 100%" @click="openFeedbackForm">问题反馈</el-button>
|
||||
<el-button text style="width: 100%" @click="openSettings">个人设置</el-button>
|
||||
<el-button type="primary" plain style="width: 100%" @click="logout">退出登录</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog v-model="announcementOpen" width="min(560px, 92vw)" :title="announcement?.title || '系统公告'">
|
||||
<div class="announcement-body" v-loading="announcementLoading">
|
||||
<div class="announcement-content">{{ announcement?.content || '' }}</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="closeAnnouncementOnce">当次关闭</el-button>
|
||||
<el-button type="primary" @click="dismissAnnouncementPermanently">永久关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="feedbackOpen" title="问题反馈" width="min(720px, 92vw)">
|
||||
<el-tabs v-model="feedbackTab" @tab-change="(name) => name === 'list' && loadMyFeedbacks()">
|
||||
<el-tab-pane label="提交反馈" name="new">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="标题">
|
||||
<el-input v-model="feedbackForm.title" placeholder="简要描述问题" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="详细描述">
|
||||
<el-input v-model="feedbackForm.description" type="textarea" :rows="5" placeholder="请详细描述您遇到的问题" maxlength="2000" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系方式(可选)">
|
||||
<el-input v-model="feedbackForm.contact" placeholder="方便我们联系您" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="我的反馈" name="list">
|
||||
<el-skeleton v-if="feedbackLoading" :rows="6" animated />
|
||||
<template v-else>
|
||||
<el-empty v-if="myFeedbacks.length === 0" description="暂无反馈" />
|
||||
<el-collapse v-else accordion>
|
||||
<el-collapse-item v-for="item in myFeedbacks" :key="item.id" :name="String(item.id)">
|
||||
<template #title>
|
||||
<div class="feedback-title">
|
||||
<span class="feedback-title-text">{{ item.title }}</span>
|
||||
<el-tag size="small" effect="light" :type="feedbackStatusTagType(item.status)">
|
||||
{{ feedbackStatusLabel(item.status) }}
|
||||
</el-tag>
|
||||
<span class="feedback-time app-muted">{{ item.created_at || '' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="feedback-body">
|
||||
<div class="feedback-section">
|
||||
<div class="feedback-label app-muted">描述</div>
|
||||
<div class="feedback-text">{{ item.description }}</div>
|
||||
</div>
|
||||
<div v-if="item.admin_reply" class="feedback-section">
|
||||
<div class="feedback-label app-muted">管理员回复</div>
|
||||
<div class="feedback-text">{{ item.admin_reply }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="feedbackOpen = false">关闭</el-button>
|
||||
<el-button v-if="feedbackTab === 'list'" @click="loadMyFeedbacks">刷新</el-button>
|
||||
<el-button v-if="feedbackTab === 'new'" type="primary" :loading="feedbackSubmitting" @click="submitFeedbackForm">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="settingsOpen" title="个人设置" width="min(720px, 92vw)">
|
||||
<el-tabs v-model="settingsTab">
|
||||
<el-tab-pane label="邮箱绑定" name="email">
|
||||
<div v-loading="emailLoading" class="settings-section">
|
||||
<el-alert
|
||||
v-if="emailInfo.email && emailInfo.email_verified"
|
||||
type="success"
|
||||
:closable="false"
|
||||
title="邮箱已绑定并验证"
|
||||
show-icon
|
||||
class="settings-alert"
|
||||
>
|
||||
<template #default>
|
||||
<div class="email-row">
|
||||
<div class="email-value">{{ emailInfo.email }}</div>
|
||||
<el-button type="danger" text @click="onUnbindEmail">解绑</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-alert
|
||||
v-else-if="emailInfo.email"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
title="邮箱待验证:请查收验证邮件(含垃圾箱)"
|
||||
show-icon
|
||||
class="settings-alert"
|
||||
/>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="邮箱地址">
|
||||
<el-input v-model="bindEmailValue" placeholder="name@example.com" />
|
||||
</el-form-item>
|
||||
<el-button type="primary" :loading="bindEmailLoading" @click="onBindEmail">发送验证邮件</el-button>
|
||||
</el-form>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="notify-row">
|
||||
<div>
|
||||
<div class="notify-title">任务完成通知</div>
|
||||
<div class="app-muted notify-desc">定时任务完成后发送邮件</div>
|
||||
</div>
|
||||
<el-switch
|
||||
:model-value="emailNotifyEnabled"
|
||||
:disabled="!emailInfo.email_verified || emailNotifyLoading"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="onToggleEmailNotify"
|
||||
/>
|
||||
</div>
|
||||
<el-alert
|
||||
v-if="!emailInfo.email_verified"
|
||||
type="info"
|
||||
:closable="false"
|
||||
title="绑定并验证邮箱后可开启邮件通知。"
|
||||
show-icon
|
||||
class="settings-hint"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="修改密码" name="password">
|
||||
<div class="settings-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="当前密码">
|
||||
<el-input v-model="passwordForm.current_password" type="password" show-password autocomplete="current-password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码(至少6位)">
|
||||
<el-input v-model="passwordForm.new_password" type="password" show-password autocomplete="new-password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码">
|
||||
<el-input
|
||||
v-model="passwordForm.confirm_password"
|
||||
type="password"
|
||||
show-password
|
||||
autocomplete="new-password"
|
||||
@keyup.enter="onChangePassword"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button type="primary" :loading="passwordLoading" @click="onChangePassword">确认修改</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="VIP信息" name="vip">
|
||||
<div class="settings-section">
|
||||
<el-alert
|
||||
:type="userStore.isVip ? 'success' : 'info'"
|
||||
:closable="false"
|
||||
:title="userStore.isVip ? '当前为 VIP 会员' : '当前为普通用户'"
|
||||
show-icon
|
||||
class="settings-alert"
|
||||
/>
|
||||
<div v-if="userStore.isVip" class="vip-info">
|
||||
<div class="vip-line">
|
||||
<span class="app-muted">到期时间</span>
|
||||
<span>{{ userStore.vipExpireTime || '未知' }}</span>
|
||||
</div>
|
||||
<div class="vip-line">
|
||||
<span class="app-muted">剩余天数</span>
|
||||
<span>{{ userStore.vipDaysLeft }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="vip-info">
|
||||
<div class="app-muted">升级方式:请通过“反馈”联系管理员开通。</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="settingsOpen = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
@@ -232,6 +716,113 @@ async function logout() {
|
||||
border-top: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.announcement-body {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.announcement-content {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.feedback-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.feedback-title-text {
|
||||
font-weight: 800;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.feedback-time {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.feedback-body {
|
||||
padding: 6px 0 2px;
|
||||
}
|
||||
|
||||
.feedback-section + .feedback-section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.feedback-label {
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.feedback-text {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
padding: 6px 2px 2px;
|
||||
}
|
||||
|
||||
.settings-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.email-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.email-value {
|
||||
font-weight: 800;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.notify-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.notify-title {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.notify-desc {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.settings-hint {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.vip-info {
|
||||
margin-top: 12px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.vip-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.layout-header {
|
||||
flex-wrap: wrap;
|
||||
|
||||
8
app.py
8
app.py
@@ -3697,9 +3697,13 @@ def change_user_password():
|
||||
if len(new_password) < 6:
|
||||
return jsonify({"error": "新密码至少6位"}), 400
|
||||
|
||||
# 验证当前密码
|
||||
# 验证当前密码(使用现有 bcrypt/SHA256 兼容逻辑)
|
||||
user = database.get_user_by_id(current_user.id)
|
||||
if not user or not check_password_hash(user['password_hash'], current_password):
|
||||
if not user:
|
||||
return jsonify({"error": "用户不存在"}), 404
|
||||
|
||||
username = user.get('username', '')
|
||||
if not username or not database.verify_user(username, current_password):
|
||||
return jsonify({"error": "当前密码错误"}), 400
|
||||
|
||||
# 更新密码(使用管理员重置密码的函数,因为已经验证过当前密码了)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"_accounts-DI7tHfNj.js": {
|
||||
"file": "assets/accounts-DI7tHfNj.js",
|
||||
"_accounts-L94-op7C.js": {
|
||||
"file": "assets/accounts-L94-op7C.js",
|
||||
"name": "accounts",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_auth-CgGRYkq1.js": {
|
||||
"file": "assets/auth-CgGRYkq1.js",
|
||||
"_auth-BTF1DcMg.js": {
|
||||
"file": "assets/auth-BTF1DcMg.js",
|
||||
"name": "auth",
|
||||
"imports": [
|
||||
"index.html"
|
||||
@@ -18,7 +18,7 @@
|
||||
"name": "password"
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-BstQMnWL.js",
|
||||
"file": "assets/index-iA-vJJPI.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
@@ -32,16 +32,16 @@
|
||||
"src/pages/ScreenshotsPage.vue"
|
||||
],
|
||||
"css": [
|
||||
"assets/index-Baiuy_-z.css"
|
||||
"assets/index-Cvi4RJz4.css"
|
||||
]
|
||||
},
|
||||
"src/pages/AccountsPage.vue": {
|
||||
"file": "assets/AccountsPage-Bcis23qR.js",
|
||||
"file": "assets/AccountsPage-C6vp-hsE.js",
|
||||
"name": "AccountsPage",
|
||||
"src": "src/pages/AccountsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DI7tHfNj.js",
|
||||
"_accounts-L94-op7C.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -49,13 +49,13 @@
|
||||
]
|
||||
},
|
||||
"src/pages/LoginPage.vue": {
|
||||
"file": "assets/LoginPage-BNb9NBzk.js",
|
||||
"file": "assets/LoginPage-B3UYJO5m.js",
|
||||
"name": "LoginPage",
|
||||
"src": "src/pages/LoginPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-CgGRYkq1.js",
|
||||
"_auth-BTF1DcMg.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
@@ -63,26 +63,26 @@
|
||||
]
|
||||
},
|
||||
"src/pages/RegisterPage.vue": {
|
||||
"file": "assets/RegisterPage-CFiuiL-s.js",
|
||||
"file": "assets/RegisterPage-DZG6_P91.js",
|
||||
"name": "RegisterPage",
|
||||
"src": "src/pages/RegisterPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-CgGRYkq1.js"
|
||||
"_auth-BTF1DcMg.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/RegisterPage-CVjBOq6i.css"
|
||||
]
|
||||
},
|
||||
"src/pages/ResetPasswordPage.vue": {
|
||||
"file": "assets/ResetPasswordPage-DeWXyaHc.js",
|
||||
"file": "assets/ResetPasswordPage-BSHAEmwh.js",
|
||||
"name": "ResetPasswordPage",
|
||||
"src": "src/pages/ResetPasswordPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-CgGRYkq1.js",
|
||||
"_auth-BTF1DcMg.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
@@ -90,12 +90,12 @@
|
||||
]
|
||||
},
|
||||
"src/pages/SchedulesPage.vue": {
|
||||
"file": "assets/SchedulesPage-BXyRqadY.js",
|
||||
"file": "assets/SchedulesPage-BEe8nXPf.js",
|
||||
"name": "SchedulesPage",
|
||||
"src": "src/pages/SchedulesPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DI7tHfNj.js",
|
||||
"_accounts-L94-op7C.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -103,7 +103,7 @@
|
||||
]
|
||||
},
|
||||
"src/pages/ScreenshotsPage.vue": {
|
||||
"file": "assets/ScreenshotsPage-BwyU04c1.js",
|
||||
"file": "assets/ScreenshotsPage-B2uEhK_5.js",
|
||||
"name": "ScreenshotsPage",
|
||||
"src": "src/pages/ScreenshotsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -115,7 +115,7 @@
|
||||
]
|
||||
},
|
||||
"src/pages/VerifyResultPage.vue": {
|
||||
"file": "assets/VerifyResultPage-DgCNTQ4L.js",
|
||||
"file": "assets/VerifyResultPage-s3b-_h4m.js",
|
||||
"name": "VerifyResultPage",
|
||||
"src": "src/pages/VerifyResultPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
|
||||
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-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};
|
||||
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-iA-vJJPI.js";import{g as z,f as F,c as G}from"./auth-BTF1DcMg.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-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};
|
||||
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-iA-vJJPI.js";import{d as H}from"./auth-BTF1DcMg.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};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +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};
|
||||
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-iA-vJJPI.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 +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-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};
|
||||
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-iA-vJJPI.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 +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};
|
||||
import{p as c}from"./index-iA-vJJPI.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-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};
|
||||
import{p as s}from"./index-iA-vJJPI.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
File diff suppressed because one or more lines are too long
1
static/app/assets/index-Cvi4RJz4.css
Normal file
1
static/app/assets/index-Cvi4RJz4.css
Normal file
File diff suppressed because one or more lines are too long
30
static/app/assets/index-iA-vJJPI.js
Normal file
30
static/app/assets/index-iA-vJJPI.js
Normal file
File diff suppressed because one or more lines are too long
@@ -4,8 +4,8 @@
|
||||
<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-BstQMnWL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-Baiuy_-z.css">
|
||||
<script type="module" crossorigin src="./assets/index-iA-vJJPI.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-Cvi4RJz4.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>该页面需要启用 JavaScript 才能使用。</noscript>
|
||||
|
||||
Reference in New Issue
Block a user