style: set print width to 120

This commit is contained in:
AkagiYui 2024-05-26 20:09:55 +08:00
parent 4721e0b576
commit a36855bfd3
40 changed files with 185 additions and 752 deletions

View File

@ -1,6 +1,6 @@
/** @type {import("prettier").Config} */
const config = {
printWidth: 80, // 每行字符数
printWidth: 120, // 每行字符数
tabWidth: 2, // 缩进空格数
useTabs: false, // 使用tab缩进
semi: false, // 在语句末尾使用分号

View File

@ -40,11 +40,7 @@ onMounted(() => {
</script>
<template>
<n-config-provider
:locale="customizedLocale"
:date-locale="dateZhCN"
:theme="isDarkMode ? darkTheme : null"
>
<n-config-provider :locale="customizedLocale" :date-locale="dateZhCN" :theme="isDarkMode ? darkTheme : null">
<FeedbackProvider>
<div class="container">
<div v-show="banner" class="banner">

View File

@ -26,10 +26,7 @@ export function getAnnouncements(
/**
*
*/
export function updateAnnouncementStatus(
id: string,
disabled: boolean,
): RequestResponse<undefined> {
export function updateAnnouncementStatus(id: string, disabled: boolean): RequestResponse<undefined> {
return Request.put(
`/announcement/${id}/status`,
{
@ -53,11 +50,7 @@ export function deleteAnnouncement(id: string): RequestResponse<undefined> {
/**
*
*/
export function updateAnnouncement(
id: string,
title?: string,
content?: string,
): RequestResponse<undefined> {
export function updateAnnouncement(id: string, title?: string, content?: string): RequestResponse<undefined> {
const requestData: { title?: string; content?: string } = {}
if (hasText(title)) {
requestData.title = title
@ -74,9 +67,6 @@ export function updateAnnouncement(
* @param data
* @returns ID
*/
export function addAnnouncement(data: {
title: string
content: string
}): RequestResponse<string> {
export function addAnnouncement(data: { title: string; content: string }): RequestResponse<string> {
return Request.post("/announcement", data)
}

View File

@ -2,9 +2,7 @@ import Request, { config } from "./request"
import type { AxiosProgressEvent } from "axios"
/** 获取目录内容 */
export function getFolderContent(
id: string | null = null,
): RequestResponse<FolderContent> {
export function getFolderContent(id: string | null = null): RequestResponse<FolderContent> {
return Request.get("/file", { params: { folder: id } })
}
@ -22,11 +20,7 @@ export function getFileTemporaryToken(id: string): RequestResponse<string> {
* @param single 线
* @param name
*/
export function getFileTemporaryUrl(
id: string,
single: boolean = false,
name?: string,
): Promise<string> {
export function getFileTemporaryUrl(id: string, single: boolean = false, name?: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
getFileTemporaryToken(id)
.then((response) => {
@ -90,10 +84,7 @@ export function deleteFile(id: string): RequestResponse<null> {
* @param id ID
* @param folderId ID
*/
export function moveFile(
id: string,
folderId: string | undefined,
): RequestResponse<null> {
export function moveFile(id: string, folderId: string | undefined): RequestResponse<null> {
return Request.put(
`/file/${id}/move`,
{},

View File

@ -1,10 +1,7 @@
import Request from "./request"
/** 创建文件夹 */
export function createFolder(
name: string | undefined,
parent?: string,
): RequestResponse<undefined> {
export function createFolder(name: string | undefined, parent?: string): RequestResponse<undefined> {
return Request.post("/folder", { name: name, parent: parent || null })
}
@ -13,10 +10,7 @@ export function createFolder(
* @param id ID
* @param parent ID
*/
export function moveFolder(
id: string,
parent: string | undefined,
): RequestResponse<undefined> {
export function moveFolder(id: string, parent: string | undefined): RequestResponse<undefined> {
return Request.put(
`/folder/${id}/move`,
{},

View File

@ -1,11 +1,7 @@
import Request from "./request"
/** 获取所有角色 */
export function getRoles(
index: number = 0,
size: number = 10,
expression?: string,
): RequestResponse<Page<Role>> {
export function getRoles(index: number = 0, size: number = 10, expression?: string): RequestResponse<Page<Role>> {
return Request.get("/role", {
params: {
index: index,
@ -31,18 +27,12 @@ export function deleteRole(id: string): RequestResponse<null> {
}
/** 更新角色信息 */
export function updateRole(
id: string,
data: UpdateRoleData,
): RequestResponse<null> {
export function updateRole(id: string, data: UpdateRoleData): RequestResponse<null> {
return Request.put(`/role/${id}`, data)
}
/** 更新角色状态 */
export function updateRoleStatus(
id: string,
disabled?: boolean,
): RequestResponse<null> {
export function updateRoleStatus(id: string, disabled?: boolean): RequestResponse<null> {
const requestData: any = {}
if (disabled !== undefined) {
requestData.disabled = disabled
@ -60,18 +50,12 @@ export function getRoleUsers(id: string): RequestResponse<string[]> {
}
/** 分配角色用户 */
export function assignRoleUsers(
id: string,
userIds: string[],
): RequestResponse<null> {
export function assignRoleUsers(id: string, userIds: string[]): RequestResponse<null> {
return Request.put(`/role/${id}/users`, userIds)
}
/** 取消分配角色用户 */
export function unassignRoleUsers(
id: string,
userIds: string[],
): RequestResponse<null> {
export function unassignRoleUsers(id: string, userIds: string[]): RequestResponse<null> {
return Request.delete(`/role/${id}/users`, {
data: userIds,
})

View File

@ -11,10 +11,7 @@ export function getConfig(): RequestResponse<Object> {
}
/** 更新设置 */
export function updateSetting(
key: string,
value: string | number | boolean,
): RequestResponse<undefined> {
export function updateSetting(key: string, value: string | number | boolean): RequestResponse<undefined> {
return Request.put(
`/system/setting/${key}`,
{ value },

View File

@ -18,12 +18,7 @@ export function deleteUser(id: string) {
}
/** 新增用户 */
export function addUser(data: {
username: string
password: string
nickname?: string
email?: string
}) {
export function addUser(data: { username: string; password: string; nickname?: string; email?: string }) {
// 检查undefined不发送
const requestData: any = {}
if (hasText(data.nickname)) {
@ -40,10 +35,7 @@ export function addUser(data: {
}
/** 更新用户信息 */
export function updateUserInfo(
id: string,
data: { password?: string; nickname?: string; email?: string },
) {
export function updateUserInfo(id: string, data: { password?: string; nickname?: string; email?: string }) {
// 检查undefined不发送
const requestData: any = {}
if (hasText(data.nickname)) {
@ -107,10 +99,7 @@ export function uploadUserAvatar(file: File) {
/**
*
*/
export function updateUserDisabled(
id: string,
disabled: boolean,
): Promise<any> {
export function updateUserDisabled(id: string, disabled: boolean): Promise<any> {
return Request.put(
`/user/${id}/disable`,
{
@ -127,10 +116,7 @@ export function updateUserDisabled(
/**
* ()
*/
export function updateUserPassword(
id: string,
newPassword: string,
): Promise<any> {
export function updateUserPassword(id: string, newPassword: string): Promise<any> {
return Request.put(`/user/${id}/password`, {
newPassword: newPassword,
})
@ -146,32 +132,21 @@ export function getUserRoles(id: string): RequestResponse<string[]> {
/**
* ()
*/
export function assignRoles(
id: string,
roles: string[],
): RequestResponse<null> {
export function assignRoles(id: string, roles: string[]): RequestResponse<null> {
return Request.put(`/user/${id}/role`, roles)
}
/**
* ()
*/
export function removeRoles(
id: string,
roles: string[],
): RequestResponse<null> {
export function removeRoles(id: string, roles: string[]): RequestResponse<null> {
return Request.delete(`/user/${id}/role`, { data: roles })
}
/**
*
*/
export function sendRegisterEmailCode(
username: string,
password: string,
email: string,
captcha: GeetestSuccessInfo,
) {
export function sendRegisterEmailCode(username: string, password: string, email: string, captcha: GeetestSuccessInfo) {
return Request.post(
"/user/register/email",
{

View File

@ -4,24 +4,17 @@ const props = defineProps<{
config: GeetestConfig
}>()
defineExpose({
validate: (
onFail: (w: GeetestFailInfo) => void = () => {},
): Promise<GeetestSuccessInfo> =>
new Promise<GeetestSuccessInfo>(
(
resolve: (value: GeetestSuccessInfo) => void = () => {},
reject = () => {},
) => {
if (!geetest) {
reject(new Error("geetest not ready"))
return
}
geetest.onSuccess(() => resolve(geetest.getValidate()))
geetest.onFail(onFail)
geetest.onError(reject)
geetest.showCaptcha()
},
),
validate: (onFail: (w: GeetestFailInfo) => void = () => {}): Promise<GeetestSuccessInfo> =>
new Promise<GeetestSuccessInfo>((resolve: (value: GeetestSuccessInfo) => void = () => {}, reject = () => {}) => {
if (!geetest) {
reject(new Error("geetest not ready"))
return
}
geetest.onSuccess(() => resolve(geetest.getValidate()))
geetest.onFail(onFail)
geetest.onError(reject)
geetest.showCaptcha()
}),
})
onBeforeMount(() => {

View File

@ -30,8 +30,7 @@ import {
import { useAppConfig } from "@/stores/app-config"
import { renderIcon } from "@/utils"
const { expandedMenuKeys, isMenuCollapsed, isDebugMode } =
storeToRefs(useAppConfig())
const { expandedMenuKeys, isMenuCollapsed, isDebugMode } = storeToRefs(useAppConfig())
const menuOptions = ref([
{
@ -92,6 +91,7 @@ const menuOptions = ref([
key: "recycle",
icon: renderIcon(TrashOutline),
disabled: true,
show: false,
},
{
label: "设置",
@ -108,6 +108,7 @@ const menuOptions = ref([
label: "系统状态",
key: "system-status",
icon: renderIcon(SpeedometerOutline),
show: false,
children: [
{
label: "运行信息",

View File

@ -1,20 +1,13 @@
<script setup lang="ts">
import { useRouter } from "vue-router/auto"
import { storeToRefs } from "pinia"
import {
ArrowUpOutline,
LogOutOutline,
MoonOutline,
PersonCircleOutline,
SunnyOutline,
} from "@vicons/ionicons5"
import { ArrowUpOutline, LogOutOutline, MoonOutline, PersonCircleOutline, SunnyOutline } from "@vicons/ionicons5"
import { useAppConfig } from "@/stores/app-config"
import { useUserInfo } from "@/stores/user-info"
import { renderIcon } from "@/utils"
import QrCode from "@/components/QrCode.vue"
const { isDarkMode, isUploadDrawerShow, isDebugMode, uploadItemCount } =
storeToRefs(useAppConfig())
const { isDarkMode, isUploadDrawerShow, isDebugMode, uploadItemCount } = storeToRefs(useAppConfig())
const { nickname, isLoggedIn, avatarUrl } = storeToRefs(useUserInfo())
const { removeInfo } = useUserInfo()
const router = useRouter()
@ -54,23 +47,8 @@ const onSelect = (key: string) => {
</script>
<template>
<n-layout-header
style="
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
"
bordered
>
<n-space
style="
margin-left: 36px;
display: flex;
align-items: center;
height: 36px;
"
>
<n-layout-header style="height: 64px; display: flex; align-items: center; justify-content: space-between" bordered>
<n-space style="margin-left: 36px; display: flex; align-items: center; height: 36px">
<n-popover trigger="hover" title="网站二维码" :disabled="!isDebugMode">
<template #header>
<n-text depth="1" strong> 网站二维码扫码立即体验</n-text>
@ -93,24 +71,13 @@ const onSelect = (key: string) => {
<QrCode :value="host" :size="200" />
</n-popover>
</n-space>
<n-space
style="
margin-right: 24px;
display: flex;
align-items: center;
height: 64px;
"
>
<n-space style="margin-right: 24px; display: flex; align-items: center; height: 64px">
<n-button circle quaternary strong @click="isDarkMode = !isDarkMode">
<template #icon>
<n-icon :component="isDarkMode ? MoonOutline : SunnyOutline" />
</template>
</n-button>
<n-badge
v-if="isLoggedIn && !isPlayer"
:max="999"
:value="uploadItemCount"
>
<n-badge v-if="isLoggedIn && !isPlayer" :max="999" :value="uploadItemCount">
<n-button circle secondary strong @click="isUploadDrawerShow = true">
<template #icon>
<n-icon :component="ArrowUpOutline" />

View File

@ -1,11 +1,5 @@
<script setup lang="ts">
import {
useLoadingBar,
useDialog,
useMessage,
useNotification,
useModal,
} from "naive-ui"
import { useLoadingBar, useDialog, useMessage, useNotification, useModal } from "naive-ui"
window.$loadingbar = useLoadingBar()
window.$dialog = useDialog()

View File

@ -19,9 +19,7 @@ export class SHA256Calculator {
throw new Error("Data must be a string or ArrayBuffer")
}
const combinedData = new Uint8Array(
this.digestBuffer.length + newData.length,
)
const combinedData = new Uint8Array(this.digestBuffer.length + newData.length)
combinedData.set(this.digestBuffer, 0)
combinedData.set(newData, this.digestBuffer.length)
this.digestBuffer = combinedData

View File

@ -25,11 +25,7 @@ const displayMap = reactive(new Map<number, UploadDisplayInfo>())
watch(displayMap, () => {
let notDoneCount = 0
displayMap.forEach((value) => {
if (
value.status !== "mirrored" &&
value.status !== "merged" &&
value.status !== "canceled"
) {
if (value.status !== "mirrored" && value.status !== "merged" && value.status !== "canceled") {
notDoneCount++
}
})
@ -156,12 +152,7 @@ function onPauseButtonClick(taskId: number) {
function onRemoveButtonClick(taskId: number) {
const info = displayMap.get(taskId)
if (!info) return
if (
info.status === "mirrored" ||
info.status === "merged" ||
info.status === "canceled" ||
info.status === "error"
) {
if (info.status === "mirrored" || info.status === "merged" || info.status === "canceled" || info.status === "error") {
displayMap.delete(taskId)
} else {
uploader.postMessage({
@ -174,20 +165,8 @@ function onRemoveButtonClick(taskId: number) {
<template>
<input v-show="false" ref="fileInputRef" MULTIPLE type="file" />
<input
v-show="false"
ref="folderInputRef"
mozdirectory
odirectory
type="file"
webkitdirectory
/>
<n-drawer
v-model:show="isUploadDrawerShow"
:placement="'right'"
:width="502"
:auto-focus="false"
>
<input v-show="false" ref="folderInputRef" mozdirectory odirectory type="file" webkitdirectory />
<n-drawer v-model:show="isUploadDrawerShow" :placement="'right'" :width="502" :auto-focus="false">
<n-drawer-content
id="content"
:native-scrollbar="false"

View File

@ -1,12 +1,5 @@
<script lang="ts" setup>
import {
ArchiveOutline,
CheckmarkOutline,
CloseOutline,
HelpOutline,
PauseOutline,
PlayOutline,
} from "@vicons/ionicons5"
import { CheckmarkOutline, CloseOutline, HelpOutline, PauseOutline, PlayOutline } from "@vicons/ionicons5"
import { useThemeVars } from "naive-ui"
import { type2Icon } from "@/utils"
@ -24,18 +17,18 @@ const info = computed<UploadDisplayInfo>(() => props.data)
const themeVars = useThemeVars()
const progress = computed(() => info.value.progress)
const progressText = computed(() => `${progress.value.toFixed(2)}%`)
const finished = computed(() => info.value.status === 'mirrored' || info.value.status === 'uploaded')
const finished = computed(() => info.value.status === "mirrored" || info.value.status === "merged")
const progressColor = computed(() => {
switch (info.value.status) {
case 'uploading':
case 'waiting':
case "uploading":
case "waiting":
return `${themeVars.value.primaryColor}50`
case 'paused':
case "paused":
return `${themeVars.value.warningColor}50`
case 'error':
case "error":
return `${themeVars.value.errorColor}50`
}
return `transparent`
return "transparent"
})
//
@ -64,7 +57,7 @@ const onRemoveButtonClick = (() => {
const text = computed(() => {
switch (info.value.status) {
case "uploading":
return `${Math.min(info.value.progress, 100).toFixed(0)}%`
return `${Math.min(progress.value, 100).toFixed(0)}%`
case "paused":
return "已暂停"
case "waiting":
@ -100,8 +93,14 @@ const buttonIcon = computed(() => {
<n-button v-show="false" circle quaternary size="small" :disabled="finished" @click="emit('onPauseButtonClick')">
<n-icon :component="info.status == 'paused' ? PlayOutline : PauseOutline" />
</n-button>
<n-button style="margin-right: 24px" :quaternary="!isConfirming" circle size="small"
:type="finished ? 'success' : 'error'" @click="onRemoveButtonClick">
<n-button
style="margin-right: 24px"
:quaternary="!isConfirming"
circle
size="small"
:type="finished ? 'success' : 'error'"
@click="onRemoveButtonClick"
>
<n-icon :component="buttonIcon" />
</n-button>
</n-flex>
@ -111,7 +110,7 @@ const buttonIcon = computed(() => {
.container {
width: 100%;
height: 50px;
position: relative
position: relative;
}
.container::before {
@ -124,6 +123,8 @@ const buttonIcon = computed(() => {
transform: scaleX(v-bind(progressText));
transform-origin: left;
background-color: v-bind(progressColor);
transition: transform 0.2s linear, background-color 0.5s;
transition:
transform 0.2s linear,
background-color 0.5s;
}
</style>

View File

@ -248,12 +248,9 @@ async function uploadByTask(task: UploadTask, fileHash: string): Promise<void> {
log("upload finished")
// 轮询检查合并状态
const checkMergeStatus = async () => {
const response = await fetch(
`${config.createTaskUrl}/${taskId}`,
{
headers: requestHeader,
},
)
const response = await fetch(`${config.createTaskUrl}/${taskId}`, {
headers: requestHeader,
})
const responseJson = await response.json()
log("checkMergeStatus", responseJson)
const merged = responseJson["data"]["merged"]
@ -268,9 +265,7 @@ async function uploadByTask(task: UploadTask, fileHash: string): Promise<void> {
}
} else {
log("upload failed", xhr.responseText)
reject(
new Error(`Failed to upload chunk ${index}: ${xhr.statusText}`),
)
reject(new Error(`Failed to upload chunk ${index}: ${xhr.statusText}`))
}
}
xhr.onerror = function () {
@ -290,8 +285,7 @@ async function uploadByTask(task: UploadTask, fileHash: string): Promise<void> {
function onProgress(event, index) {
log(`Chunk ${index} progress: ${event.loaded}/${event.total}`)
progressList[index] = event.loaded
const overallProgress =
progressList.reduce((acc, cur) => acc + cur, 0) / file.size
const overallProgress = progressList.reduce((acc, cur) => acc + cur, 0) / file.size
log(`Overall progress: ${overallProgress * 100}%`)
postMessage({
event: "progress",

View File

@ -5,10 +5,7 @@ import { useGlobal } from "./vue"
* 使线
* @returns 线
*/
export function useBusEvent<T extends BusEvent>(
event: T,
callback: (...args: EventParams[T]) => void,
) {
export function useBusEvent<T extends BusEvent>(event: T, callback: (...args: EventParams[T]) => void) {
const { $bus } = useGlobal()
$bus.on(event, callback as (...args: any[]) => void)
onUnmounted(() => $bus.off(event, callback as (...args: any[]) => void))
@ -17,10 +14,7 @@ export function useBusEvent<T extends BusEvent>(
/**
*
*/
export function emitBusEvent<T extends BusEvent>(
event: T,
...args: EventParams[T]
) {
export function emitBusEvent<T extends BusEvent>(event: T, ...args: EventParams[T]) {
const { $bus } = useGlobal()
console.debug(`[Bus] Emit event: ${event}`, ...args)
$bus.emit(event, ...args)

View File

@ -8,11 +8,7 @@ export * from "./vue"
* @param event
* @param callback
*/
export function useEventListener(
target: EventTarget,
event: string,
callback: EventListenerOrEventListenerObject,
) {
export function useEventListener(target: EventTarget, event: string, callback: EventListenerOrEventListenerObject) {
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}

View File

@ -29,9 +29,7 @@ export const useGlobal = (() => {
* @param fn
* @returns
*/
export function useComputedFn<T>(
fn: (...args: unknown[]) => T,
): (...args: unknown[]) => ComputedRef<T> {
export function useComputedFn<T>(fn: (...args: unknown[]) => T): (...args: unknown[]) => ComputedRef<T> {
const cache: Map<string, ComputedRef<T>> = new Map()
return (...args: unknown[]) => {
const cacheKey = JSON.stringify(args)

View File

@ -72,21 +72,16 @@ function createGeetest(): GeetestComponent {
document.head.appendChild(script)
return {
validate: (onFail: (w: GeetestFailInfo) => void = () => {}) =>
new Promise<GeetestSuccessInfo>(
(
resolve: (value: GeetestSuccessInfo) => void = () => {},
reject = () => {},
) => {
if (!geetest) {
reject(new Error("geetest not ready"))
return
}
geetest.onSuccess(() => resolve(geetest.getValidate()))
geetest.onFail(onFail)
geetest.onError(reject)
geetest.showCaptcha()
},
),
new Promise<GeetestSuccessInfo>((resolve: (value: GeetestSuccessInfo) => void = () => {}, reject = () => {}) => {
if (!geetest) {
reject(new Error("geetest not ready"))
return
}
geetest.onSuccess(() => resolve(geetest.getValidate()))
geetest.onFail(onFail)
geetest.onError(reject)
geetest.showCaptcha()
}),
}
}

View File

@ -15,13 +15,7 @@ const router = useRouter()
<template>
<n-card class="page">
<n-result
class="center"
status="error"
title="404 页面不存在"
size="huge"
description="需要帮助请联系管理员"
>
<n-result class="center" status="error" title="404 页面不存在" size="huge" description="需要帮助请联系管理员">
<template #footer>
<n-space justify="center">
<n-button @click="router.replace('/')"> 回到首页 </n-button>

View File

@ -79,11 +79,7 @@ onBeforeMount(async () => {
Kenko Drive Vue 前端
<n-popover trigger="hover">
<template #trigger>
<n-tag
style="cursor: pointer"
type="info"
@click="showRoadMap = !showRoadMap"
>
<n-tag style="cursor: pointer" type="info" @click="showRoadMap = !showRoadMap">
{{ frontendVersion }}
</n-tag>
</template>
@ -105,20 +101,8 @@ onBeforeMount(async () => {
<p>MIT License</p>
<p>
<n-space>
<n-button
:href="`${frontendRepoUrl}/issues`"
tag="a"
target="_blank"
>
问题反馈
</n-button>
<n-button
:href="`${frontendRepoUrl}/releases`"
tag="a"
target="_blank"
>
发布日志
</n-button>
<n-button :href="`${frontendRepoUrl}/issues`" tag="a" target="_blank"> 问题反馈 </n-button>
<n-button :href="`${frontendRepoUrl}/releases`" tag="a" target="_blank"> 发布日志 </n-button>
</n-space>
</p>
</div>
@ -147,20 +131,8 @@ onBeforeMount(async () => {
<p>MIT License</p>
<p>
<n-space>
<n-button
:href="`${backendRepoUrl}/issues`"
tag="a"
target="_blank"
>
问题反馈
</n-button>
<n-button
:href="`${backendRepoUrl}/releases`"
tag="a"
target="_blank"
>
发布日志
</n-button>
<n-button :href="`${backendRepoUrl}/issues`" tag="a" target="_blank"> 问题反馈 </n-button>
<n-button :href="`${backendRepoUrl}/releases`" tag="a" target="_blank"> 发布日志 </n-button>
</n-space>
</p>
</div>

View File

@ -10,14 +10,7 @@
<script setup lang="ts">
import { AddOutline, RefreshOutline, SearchOutline } from "@vicons/ionicons5"
import type { PaginationProps } from "naive-ui"
import {
type FormInst,
NButton,
NInput,
NSpace,
NText,
NTooltip,
} from "naive-ui"
import { type FormInst, NButton, NInput, NSpace, NText, NTooltip } from "naive-ui"
import {
addAnnouncement,
deleteAnnouncement,
@ -272,11 +265,7 @@ function onModalPositiveButtonClick() {
if (!selectRow.value.id) {
throw new Error("未获取到用户ID")
}
updateAnnouncement(
selectRow.value.id,
modalData.value.title,
modalData.value.content,
)
updateAnnouncement(selectRow.value.id, modalData.value.title, modalData.value.content)
.then(() => {
getData()
window.$message.success("修改成功")
@ -313,11 +302,7 @@ function onEditButtonClick(row: Announcement) {
/** 获取表格数据 */
function getData() {
isLoading.value = true
getAnnouncements(
pagination.page - 1,
pagination.pageSize,
searchExpression.value,
)
getAnnouncements(pagination.page - 1, pagination.pageSize, searchExpression.value)
.then((res) => {
let data: Page<any> = res.data
requestData.value = data.list
@ -334,10 +319,7 @@ function getData() {
<template>
<div style="padding: 12px">
<!-- 确认删除模态框 -->
<ConfirmModal
v-model:show="showDeleteConfirmModal"
@positive-click="onDeleteConfirm"
/>
<ConfirmModal v-model:show="showDeleteConfirmModal" @positive-click="onDeleteConfirm" />
<!-- 新增删除模态框 -->
<n-modal
@ -350,30 +332,17 @@ function getData() {
@after-leave="onAfterEditModalLeave"
>
<n-space vertical>
<n-form
ref="modalFormRef"
:model="modalData"
:rules="rules"
label-placement="left"
label-width="auto"
>
<n-form ref="modalFormRef" :model="modalData" :rules="rules" label-placement="left" label-width="auto">
<n-form-item label="标题" path="title">
<n-input v-model:value="modalData.title" placeholder="输入标题" />
</n-form-item>
<n-form-item label="内容" path="content">
<n-input
v-model:value="modalData.content"
placeholder="输入内容"
type="textarea"
/>
<n-input v-model:value="modalData.content" placeholder="输入内容" type="textarea" />
</n-form-item>
</n-form>
</n-space>
<n-space justify="end" style="width: 100%">
<n-button
:type="isEdit ? 'warning' : 'success'"
@click="onModalPositiveButtonClick"
>
<n-button :type="isEdit ? 'warning' : 'success'" @click="onModalPositiveButtonClick">
{{ isEdit ? "修改" : "确定" }}
</n-button>
</n-space>
@ -408,13 +377,7 @@ function getData() {
<n-icon :component="SearchOutline" />
</template>
</n-input>
<n-button
:disabled="isLoading || searchExpression.length === 0"
ghost
@click="getData"
>
搜索
</n-button>
<n-button :disabled="isLoading || searchExpression.length === 0" ghost @click="getData"> 搜索 </n-button>
</n-input-group>
</n-space>

View File

@ -49,11 +49,7 @@ const columns = [
secondary: true,
size: "small",
disabled: loading.value,
type: loading.value
? "tertiary"
: isUserInRole(row)
? "error"
: "primary",
type: loading.value ? "tertiary" : isUserInRole(row) ? "error" : "primary",
onClick: () => onActionClick(row),
},
{ default: () => (isUserInRole(row) ? "移除角色" : "分配角色") },
@ -116,9 +112,7 @@ const onActionClick = (user: User) => {
unassignRoleUsers(props.role.id, [user.id])
.then(() => {
window.$message.success("移除成功")
selectedUserIds.value = selectedUserIds.value.filter(
(id) => id !== user.id,
)
selectedUserIds.value = selectedUserIds.value.filter((id) => id !== user.id)
})
.catch((e) => {
window.$message.error("移除失败")

View File

@ -11,14 +11,7 @@
import { AddOutline, RefreshOutline, SearchOutline } from "@vicons/ionicons5"
import type { FormInst, PaginationProps } from "naive-ui"
import { NButton, NInput, NSpace, NSwitch } from "naive-ui"
import {
addRole,
deleteRole,
getPermissions,
getRoles,
updateRole,
updateRoleStatus,
} from "@/api/role"
import { addRole, deleteRole, getPermissions, getRoles, updateRole, updateRoleStatus } from "@/api/role"
import ConfirmModal from "@/components/ConfirmModal.vue"
import UserTable from "./UserTable.vue"
@ -213,9 +206,7 @@ const onModalConfirm = () => {
.then(() => {
showEditModal.value = false
window.$message.success("修改成功")
const index = tableData.value.findIndex(
(r) => r.id === modalData.value.id,
)
const index = tableData.value.findIndex((r) => r.id === modalData.value.id)
if (index !== -1) {
tableData.value[index] = {
...tableData.value[index],
@ -298,9 +289,7 @@ const onDeleteConfirm = () => {
deleteRole(selectedRow.value.id)
.then(() => {
window.$message.success("删除成功")
tableData.value = tableData.value.filter(
(r) => r.id !== selectedRow.value?.id,
)
tableData.value = tableData.value.filter((r) => r.id !== selectedRow.value?.id)
})
.catch(() => {
window.$message.error("删除失败")
@ -349,10 +338,7 @@ const getData = () => {
<template>
<div style="padding: 12px">
<!-- 确认删除模态框 -->
<ConfirmModal
v-model:show="showDeleteConfirmModal"
@positive-click="onDeleteConfirm"
/>
<ConfirmModal v-model:show="showDeleteConfirmModal" @positive-click="onDeleteConfirm" />
<!-- 新增编辑模态框 -->
<n-modal
@ -367,26 +353,12 @@ const getData = () => {
@after-leave="onModalClosed"
>
<n-flex vertical>
<n-form
ref="modalFormRef"
:model="modalData"
:rules="rules"
label-placement="left"
label-width="auto"
>
<n-form ref="modalFormRef" :model="modalData" :rules="rules" label-placement="left" label-width="auto">
<n-form-item path="name" label="角色名">
<n-input
v-model:value="modalData.name"
clearable
placeholder="输入角色名"
/>
<n-input v-model:value="modalData.name" clearable placeholder="输入角色名" />
</n-form-item>
<n-form-item path="description" label="描述">
<n-input
v-model:value="modalData.description"
clearable
placeholder="可空"
/>
<n-input v-model:value="modalData.description" clearable placeholder="可空" />
</n-form-item>
<n-form-item path="default" label="设为默认">
<n-switch v-model:value="modalData.default" />
@ -421,12 +393,7 @@ const getData = () => {
<n-form :show-label="false" inline :show-feedback="false">
<n-formItem>
<n-space>
<n-button
tertiary
type="info"
:disabled="isLoading"
@click="getData"
>
<n-button tertiary type="info" :disabled="isLoading" @click="getData">
<template #icon>
<n-icon>
<RefreshOutline />
@ -445,17 +412,12 @@ const getData = () => {
</n-button>
<n-input-group>
<n-input
v-model:value="searchExpression"
placeholder="角色名、描述"
>
<n-input v-model:value="searchExpression" placeholder="角色名、描述">
<template #prefix>
<n-icon :component="SearchOutline" />
</template>
</n-input>
<n-button ghost :disabled="isLoading" @click="getData">
搜索
</n-button>
<n-button ghost :disabled="isLoading" @click="getData"> 搜索 </n-button>
</n-input-group>
</n-space>
</n-formItem>

View File

@ -84,18 +84,10 @@ const updateSettingsDebounced = useDebounceFn(() => {
<template>
<div style="padding: 20px; width: 500px; margin: 0 auto">
<n-flex vertical>
<n-form
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging"
:show-feedback="true"
>
<n-form label-placement="left" label-width="auto" require-mark-placement="right-hanging" :show-feedback="true">
<n-h3>用户注册</n-h3>
<n-form-item label="开放注册">
<n-switch
v-model:value="settings.registerEnabled"
:disabled="isLoading"
/>
<n-switch v-model:value="settings.registerEnabled" :disabled="isLoading" />
</n-form-item>
<n-form-item label="注册需要邮箱验证">
<n-switch :disabled="true" :value="true" />
@ -153,8 +145,7 @@ const updateSettingsDebounced = useDebounceFn(() => {
:disabled="true"
:options="[
{
label:
'Everybody\'s Got Something to Hide Except Me and My Monkey',
label: 'Everybody\'s Got Something to Hide Except Me and My Monkey',
value: 'song0',
disabled: true,
},

View File

@ -50,11 +50,7 @@ const columns = [
secondary: true,
size: "small",
disabled: loading.value,
type: loading.value
? "tertiary"
: isRoleInUser(row)
? "error"
: "primary",
type: loading.value ? "tertiary" : isRoleInUser(row) ? "error" : "primary",
onClick: () => onActionClick(row),
},
{ default: () => (isRoleInUser(row) ? "移除角色" : "分配角色") },
@ -118,9 +114,7 @@ const onActionClick = (role: Role) => {
removeRoles(props.user.id, [role.id])
.then(() => {
window.$message.success("移除成功")
selectedRoleIds.value = selectedRoleIds.value.filter(
(id) => id !== role.id,
)
selectedRoleIds.value = selectedRoleIds.value.filter((id) => id !== role.id)
})
.catch((e) => {
window.$message.error("移除失败")

View File

@ -10,24 +10,9 @@
<script setup lang="ts">
import { changeColor } from "seemly"
import { AddOutline, RefreshOutline, SearchOutline } from "@vicons/ionicons5"
import {
NButton,
NInput,
NProgress,
NSpace,
NText,
NTooltip,
useThemeVars,
} from "naive-ui"
import { NButton, NInput, NProgress, NSpace, NText, NTooltip, useThemeVars } from "naive-ui"
import type { FormInst, PaginationProps } from "naive-ui"
import {
addUser,
deleteUser,
getUsers,
updateUserDisabled,
updateUserInfo,
updateUserPassword,
} from "@/api/user"
import { addUser, deleteUser, getUsers, updateUserDisabled, updateUserInfo, updateUserPassword } from "@/api/user"
import RoleTable from "./RoleTable.vue"
import ConfirmModal from "@/components/ConfirmModal.vue"
import { renderTooltip } from "@/utils"
@ -446,10 +431,7 @@ const getData = () => {
<template>
<div style="padding: 12px">
<!-- 确认删除模态框 -->
<ConfirmModal
v-model:show="showDeleteConfirmModal"
@positive-click="onDeleteUserConfirm"
/>
<ConfirmModal v-model:show="showDeleteConfirmModal" @positive-click="onDeleteUserConfirm" />
<!-- 重置密码模态框 -->
<n-modal
@ -473,12 +455,7 @@ const getData = () => {
label-width="auto"
>
<n-form-item path="password" label="新密码">
<n-input
v-model:value="resetPasswordData.password"
clearable
placeholder="输入新密码"
type="password"
/>
<n-input v-model:value="resetPasswordData.password" clearable placeholder="输入新密码" type="password" />
</n-form-item>
<n-form-item path="confirmPassword" label="确认密码">
<n-input
@ -490,15 +467,8 @@ const getData = () => {
</n-form-item>
</n-form>
<n-space justify="end">
<n-button
type="error"
@click="() => (showResetPasswordModal = false)"
>
取消
</n-button>
<n-button type="primary" @click="onResetPasswordClick">
确定
</n-button>
<n-button type="error" @click="() => (showResetPasswordModal = false)"> 取消 </n-button>
<n-button type="primary" @click="onResetPasswordClick"> 确定 </n-button>
</n-space>
</n-space>
</n-modal>
@ -516,13 +486,7 @@ const getData = () => {
@after-leave="onAfterEditModalLeave"
>
<n-space vertical>
<n-form
ref="modalFormRef"
:model="modalData"
:rules="rules"
label-placement="left"
label-width="auto"
>
<n-form ref="modalFormRef" :model="modalData" :rules="rules" label-placement="left" label-width="auto">
<n-form-item path="username" label="用户名">
<n-tooltip :disabled="!isEdit" trigger="hover" placement="top">
<template #trigger>
@ -547,34 +511,18 @@ const getData = () => {
/>
</n-form-item>
<n-form-item path="repeatPassword" label="确认密码">
<n-input
v-model:value="modalData.repeatPassword"
type="password"
clearable
placeholder="重复密码"
/>
<n-input v-model:value="modalData.repeatPassword" type="password" clearable placeholder="重复密码" />
</n-form-item>
<n-form-item path="nickname" label="昵称">
<n-input
v-model:value="modalData.nickname"
clearable
placeholder="输入昵称"
/>
<n-input v-model:value="modalData.nickname" clearable placeholder="输入昵称" />
</n-form-item>
<n-form-item path="email" label="邮箱">
<n-input
v-model:value="modalData.email"
clearable
placeholder="输入邮箱"
/>
<n-input v-model:value="modalData.email" clearable placeholder="输入邮箱" />
</n-form-item>
</n-form>
</n-space>
<n-space justify="end" style="width: 100%">
<n-button
:type="isEdit ? 'warning' : 'success'"
@click="onModalPositiveButtonClick"
>
<n-button :type="isEdit ? 'warning' : 'success'" @click="onModalPositiveButtonClick">
{{ isEdit ? "修改" : "确定" }}
</n-button>
</n-space>
@ -615,13 +563,7 @@ const getData = () => {
<n-icon :component="SearchOutline" />
</template>
</n-input>
<n-button
ghost
:disabled="isLoading || searchExpression.length === 0"
@click="getData"
>
搜索
</n-button>
<n-button ghost :disabled="isLoading || searchExpression.length === 0" @click="getData"> 搜索 </n-button>
</n-input-group>
</n-space>

View File

@ -20,10 +20,7 @@ const isLoading = ref(false)
const folderName = ref("")
const create = () => {
isLoading.value = true
createFolder(
hasText(folderName.value) ? folderName.value : undefined,
props.parent,
)
createFolder(hasText(folderName.value) ? folderName.value : undefined, props.parent)
.then(() => {
emit("success")
show.value = false
@ -47,21 +44,11 @@ const create = () => {
<n-flex vertical>
<n-form :show-label="false" :disabled="isLoading">
<n-form-item required>
<n-input
v-model:value="folderName"
placeholder="请输入文件夹名称"
@keyup.enter="create"
/>
<n-input v-model:value="folderName" placeholder="请输入文件夹名称" @keyup.enter="create" />
</n-form-item>
</n-form>
<n-flex justify="end" style="margin-top: -10px">
<n-button
type="primary"
:disabled="isLoading"
:loading="isLoading"
@click="create"
>确定</n-button
>
<n-button type="primary" :disabled="isLoading" :loading="isLoading" @click="create">确定</n-button>
</n-flex>
</n-flex>
</n-modal>

View File

@ -11,22 +11,10 @@
import { useRouter } from "vue-router/auto"
import { storeToRefs } from "pinia"
import type { HTMLAttributes } from "vue"
import {
NButton,
NDropdown,
NFlex,
NIcon,
NImage,
useThemeVars,
} from "naive-ui"
import { NButton, NDropdown, NFlex, NIcon, NImage, useThemeVars } from "naive-ui"
import type { DataTableColumns } from "naive-ui"
import { ArrowUp, Folder } from "@vicons/carbon"
import {
AddOutline,
ArrowUpOutline,
RefreshOutline,
TrashBinOutline,
} from "@vicons/ionicons5"
import { AddOutline, ArrowUpOutline, RefreshOutline, TrashBinOutline } from "@vicons/ionicons5"
import { FolderOpenOutlined } from "@vicons/material"
import {
ArrowDownload24Regular as DownloadIcon,
@ -37,12 +25,7 @@ import {
Share24Regular as ShareIcon,
} from "@vicons/fluent"
import { filesize } from "filesize"
import {
deleteFile as deleteFileEndpoint,
getFileTemporaryUrl,
getFolderContent,
moveFile,
} from "@/api/file"
import { deleteFile as deleteFileEndpoint, getFileTemporaryUrl, getFolderContent, moveFile } from "@/api/file"
import CreateFolderModal from "./CreateFolderModal.vue"
import { renderIcon, type2Icon } from "@/utils"
import { useAppConfig } from "@/stores/app-config"
@ -182,12 +165,7 @@ const columns: DataTableColumns<TableData> = [
NIcon,
{ size: 30, color: themeVars.value.primaryColor, depth: 2 },
{
default: () =>
h(
isFolder
? FolderOpenOutlined
: type2Icon(row.fileType, row.name),
),
default: () => h(isFolder ? FolderOpenOutlined : type2Icon(row.fileType, row.name)),
},
),
h(
@ -265,8 +243,7 @@ function onActionSelect(key: string, row: TableData) {
const { openConfirmModal } = useConfirmModal()
function deleteItem(row: TableData) {
openConfirmModal(() => {
const endpoint =
row.type === "folder" ? deleteFolderEndpoint : deleteFileEndpoint
const endpoint = row.type === "folder" ? deleteFolderEndpoint : deleteFileEndpoint
endpoint(row.id).then(() => {
window.$message.success("删除成功")
loadFolder(currentFolderId.value)
@ -333,10 +310,7 @@ function playFile(row: TableData) {
}
// docx
if (
fileType ===
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
) {
if (fileType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
getFileTemporaryUrl(row.id).then((res) => {
const route = router.resolve({
name: "docx-preview",
@ -347,10 +321,7 @@ function playFile(row: TableData) {
return
}
// xlsx
if (
fileType ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
) {
if (fileType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
getFileTemporaryUrl(row.id).then((res) => {
const route = router.resolve({
name: "xlsx-preview",
@ -572,34 +543,14 @@ function onBreadcrumbDrop(folderId: string | undefined, event: DragEvent) {
ref="imageRef"
:show-toolbar-tooltip="true"
:src="imagePreviewUrl"
style="
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
"
style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)"
@load="onImageLoaded"
/>
<n-modal
v-model:show="showMarkdownPreview"
preset="card"
style="width: 70%"
title="Markdown 预览"
>
<n-modal v-model:show="showMarkdownPreview" preset="card" style="width: 70%" title="Markdown 预览">
<MarkdownPreview :value="markdownPreviewValue" />
</n-modal>
<n-modal
v-model:show="showMonaco"
preset="card"
style="width: 80%; height: 80vh"
title="代码预览"
>
<MonacoEditor
:content="monacoPreviewValue"
:dark="isDarkMode"
:language="monacoLanguage"
/>
<n-modal v-model:show="showMonaco" preset="card" style="width: 80%; height: 80vh" title="代码预览">
<MonacoEditor :content="monacoPreviewValue" :dark="isDarkMode" :language="monacoLanguage" />
</n-modal>
<div style="padding-top: 10px">
<CreateFolderModal
@ -610,15 +561,8 @@ function onBreadcrumbDrop(folderId: string | undefined, event: DragEvent) {
<!-- 页面内容 -->
<n-flex vertical>
<!-- 操作按钮 -->
<n-flex
class="buttons-container"
:style="{ '--color': themeVars.dividerColor }"
>
<n-button
tertiary
type="info"
@click="() => loadFolder(currentFolderId)"
>
<n-flex class="buttons-container" :style="{ '--color': themeVars.dividerColor }">
<n-button tertiary type="info" @click="() => loadFolder(currentFolderId)">
<template #icon>
<n-icon :component="RefreshOutline" />
</template>

View File

@ -63,12 +63,8 @@ function fetchAnnouncements() {
// Date
// 2023-08-25T09:05:13.497+00:00
announcements.value.forEach((announcement) => {
announcement.createTime = new Date(
announcement.createTime,
).toLocaleString()
announcement.updateTime = new Date(
announcement.updateTime,
).toLocaleString()
announcement.createTime = new Date(announcement.createTime).toLocaleString()
announcement.updateTime = new Date(announcement.updateTime).toLocaleString()
})
lastFetchTime.value = Date.now()
})
@ -107,18 +103,13 @@ onActivated(() => {
</n-flex>
<n-list v-show="announcements.length > 0" bordered>
<n-list-item
v-for="announcement in announcements"
:key="announcement.title"
>
<n-list-item v-for="announcement in announcements" :key="announcement.title">
<n-thing
:title="announcement.title"
:title-extra="announcement.userNickname"
:description="announcement.updateTime"
>
<div v-for="line in announcement.content.split('\n')" :key="line">
{{ line }}<br />
</div>
<div v-for="line in announcement.content.split('\n')" :key="line">{{ line }}<br /></div>
</n-thing>
</n-list-item>
</n-list>

View File

@ -73,9 +73,7 @@ const uploadAvatar = (fileList: UploadFileInfo[]) => {
@update:file-list="uploadAvatar"
>
<n-space vertical>
<n-upload-dragger
style="margin: 0; padding: 0; height: 100px; width: 100px"
>
<n-upload-dragger style="margin: 0; padding: 0; height: 100px; width: 100px">
<n-image
object-fit="fill"
preview-disabled
@ -94,9 +92,7 @@ const uploadAvatar = (fileList: UploadFileInfo[]) => {
<n-space>
<n-space vertical>
<n-input-group>
<n-input-group-label class="info-label">
用户名
</n-input-group-label>
<n-input-group-label class="info-label"> 用户名 </n-input-group-label>
<n-tooltip trigger="hover" placement="top">
<template #trigger>
<n-input :value="username" disabled />
@ -105,15 +101,11 @@ const uploadAvatar = (fileList: UploadFileInfo[]) => {
</n-tooltip>
</n-input-group>
<n-input-group>
<n-input-group-label class="info-label">
昵称
</n-input-group-label>
<n-input-group-label class="info-label"> 昵称 </n-input-group-label>
<n-input v-model:value="personalInfo.nickname" />
</n-input-group>
<n-input-group>
<n-input-group-label class="info-label">
邮箱
</n-input-group-label>
<n-input-group-label class="info-label"> 邮箱 </n-input-group-label>
<n-input v-model:value="personalInfo.email" />
</n-input-group>
</n-space>
@ -127,10 +119,7 @@ const uploadAvatar = (fileList: UploadFileInfo[]) => {
<n-space style="display: flex">
<n-button-group>
<span style="align-self: center; margin-right: 10px">主题</span>
<n-button
:type="!isDarkMode ? 'primary' : 'default'"
@click="toggleDarkMode"
>
<n-button :type="!isDarkMode ? 'primary' : 'default'" @click="toggleDarkMode">
<template #icon>
<n-icon>
<WeatherSunny16Regular />
@ -138,10 +127,7 @@ const uploadAvatar = (fileList: UploadFileInfo[]) => {
</template>
亮色
</n-button>
<n-button
:type="isDarkMode ? 'primary' : 'default'"
@click="toggleDarkMode"
>
<n-button :type="isDarkMode ? 'primary' : 'default'" @click="toggleDarkMode">
<template #icon>
<n-icon>
<WeatherMoon16Regular />

View File

@ -14,15 +14,7 @@ import "vue-cropper/dist/index.css"
import { VueCropper } from "vue-cropper"
import VueWordCloud from "vuewordcloud"
// Chart.js
import {
BarElement,
CategoryScale,
Chart as ChartJS,
Legend,
LinearScale,
Title,
Tooltip,
} from "chart.js"
import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, Title, Tooltip } from "chart.js"
import { Bar } from "vue-chartjs"
import GeetestCaptcha from "@/components/GeetestCaptcha.vue"
import { useGlobal } from "@/hooks"
@ -523,9 +515,7 @@ const editorMounted = (editor) => {
</script>
<template>
<NDivider title-placement="right">
This is playground. 下面是开发者用来测试组件的区域
</NDivider>
<NDivider title-placement="right"> This is playground. 下面是开发者用来测试组件的区域 </NDivider>
<div style="padding: 24px">
<MonacoEditor
v-model:content="value1"
@ -541,11 +531,7 @@ const editorMounted = (editor) => {
/>
<NSpace vertical>
<n-modal
:show="false"
preset="card"
style="max-width: 60%; height: 80%; padding: 10px"
>
<n-modal :show="false" preset="card" style="max-width: 60%; height: 80%; padding: 10px">
<template #header>
<div>Markdown 预览</div>
</template>
@ -577,42 +563,22 @@ const editorMounted = (editor) => {
}"
/>
<NSpace>
<svg
fill="currentColor"
height="20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<svg fill="currentColor" height="20" width="20" xmlns="http://www.w3.org/2000/svg">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
<svg
fill="currentColor"
height="17"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<svg fill="currentColor" height="17" width="20" xmlns="http://www.w3.org/2000/svg">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
<svg
fill="currentColor"
height="20"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<svg fill="currentColor" height="20" width="18" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
<svg
fill="currentColor"
height="20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<svg fill="currentColor" height="20" width="20" xmlns="http://www.w3.org/2000/svg">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>

View File

@ -22,13 +22,7 @@ import {
} from "@vicons/ionicons5"
import { useAppConfig } from "@/stores/app-config"
import { useUserInfo } from "@/stores/user-info"
import {
confirmRegisterEmailCode,
confirmSmsCode,
getToken,
sendRegisterEmailCode,
sendSmsCode,
} from "@/api/user"
import { confirmRegisterEmailCode, confirmSmsCode, getToken, sendRegisterEmailCode, sendSmsCode } from "@/api/user"
import { getRegisterEnabled } from "@/api/server"
import { ResponseMessagesSimplifiedChinese } from "@/api/ResponseMessages"
import { hasText } from "@/utils"
@ -134,12 +128,7 @@ function onSendEmailCodeLogoClick() {
?.validate()
.then(() => {
geetest.validate().then((w) => {
sendRegisterEmailCode(
loginForm.value.username,
loginForm.value.password,
loginForm.value.email,
w,
)
sendRegisterEmailCode(loginForm.value.username, loginForm.value.password, loginForm.value.email, w)
.then(() => {
window.$message.success("验证码已发送,请查收")
isCooldown.value = true
@ -252,11 +241,7 @@ const onSendSmsCodeLogoClick = () => {
:show-require-mark="false"
>
<n-form-item-row label="手机号" path="phone">
<n-input
v-model:value="smsLoginForm.phone"
:input-props="{ autocomplete: 'phone' }"
placeholder="手机号"
>
<n-input v-model:value="smsLoginForm.phone" :input-props="{ autocomplete: 'phone' }" placeholder="手机号">
<template #prefix>
<n-icon :component="PhonePortraitOutline" />
</template>
@ -282,24 +267,10 @@ const onSendSmsCodeLogoClick = () => {
</n-input>
</n-form-item-row>
</n-form>
<n-button
block
secondary
strong
type="primary"
@click="onSmsLoginButtonClick"
>
登录
</n-button>
<n-button block secondary strong type="primary" @click="onSmsLoginButtonClick"> 登录 </n-button>
</n-tab-pane>
<n-tab-pane name="signin" tab="登录">
<n-form
ref="modalFormRef"
:show-require-mark="false"
:show-label="false"
:model="loginForm"
:rules="rules"
>
<n-form ref="modalFormRef" :show-require-mark="false" :show-label="false" :model="loginForm" :rules="rules">
<n-form-item-row path="username" label="账号">
<n-input
v-model:value="loginForm.username"
@ -326,23 +297,10 @@ const onSendSmsCodeLogoClick = () => {
</n-input>
</n-form-item-row>
</n-form>
<n-button
block
secondary
strong
type="primary"
@click="onLoginButtonClick"
>
登录
</n-button>
<n-button block secondary strong type="primary" @click="onLoginButtonClick"> 登录 </n-button>
</n-tab-pane>
<n-tab-pane v-if="isRegisterEnabled" name="signup" tab="注册">
<n-form
ref="registerFormRef"
:model="loginForm"
:rules="rules"
:show-require-mark="false"
>
<n-form ref="registerFormRef" :model="loginForm" :rules="rules" :show-require-mark="false">
<n-form-item-row path="username" label="用户名">
<n-input
v-model:value="loginForm.username"
@ -368,11 +326,7 @@ const onSendSmsCodeLogoClick = () => {
/>
</n-form-item-row>
<n-form-item-row label="邮箱" path="email">
<n-input
v-model:value="loginForm.email"
:disabled="isCooldown"
@keyup.enter="onSendEmailCodeLogoClick"
>
<n-input v-model:value="loginForm.email" :disabled="isCooldown" @keyup.enter="onSendEmailCodeLogoClick">
<template #suffix>
<n-icon
:component="isCooldown ? CheckmarkOutline : SendSharp"
@ -383,20 +337,10 @@ const onSendSmsCodeLogoClick = () => {
</n-input>
</n-form-item-row>
<n-form-item-row label="验证码" path="code">
<n-input
ref="codeInputRef"
v-model:value="loginForm.code"
:disabled="!sentEmailCode"
/>
<n-input ref="codeInputRef" v-model:value="loginForm.code" :disabled="!sentEmailCode" />
</n-form-item-row>
</n-form>
<n-button
:disabled="!sentEmailCode"
block
secondary
strong
type="primary"
@click="onRegisterButtonClick"
<n-button :disabled="!sentEmailCode" block secondary strong type="primary" @click="onRegisterButtonClick"
>注册
</n-button>
</n-tab-pane>

View File

@ -45,13 +45,7 @@ export const useAppConfig = defineStore(
{
persist: {
storage: localStorage,
paths: [
"isDarkMode",
"isMenuCollapsed",
"expandedMenuKeys",
"currentRouteName",
"isDebugMode",
],
paths: ["isDarkMode", "isMenuCollapsed", "expandedMenuKeys", "currentRouteName", "isDebugMode"],
},
},
)

View File

@ -56,9 +56,7 @@ export const useUserInfo = defineStore(
* @param imageData
*/
function setAvatar(imageData: ArrayBuffer) {
const imageUrl = URL.createObjectURL(
new Blob([imageData], { type: "imageType" }),
)
const imageUrl = URL.createObjectURL(new Blob([imageData], { type: "imageType" }))
if (hasText(avatarUrl.value)) {
URL.revokeObjectURL(avatarUrl.value)
}

View File

@ -1,8 +1,5 @@
declare interface Window {
initGeetest4: (
options: GeetestConfig,
callback: (captchaObj: Geetest) => void,
) => void
initGeetest4: (options: GeetestConfig, callback: (captchaObj: Geetest) => void) => void
}
/**

View File

@ -8,16 +8,7 @@ export function unmarshalDate(date: string | Date) {
return date
}
const parts: number[] = date
.split(/[-T:.+]/)
.map((part) => parseInt(part, 10))
const parts: number[] = date.split(/[-T:.+]/).map((part) => parseInt(part, 10))
// 注意JavaScript中的月份从0开始所以需要将月份减1
return new Date(
parts[0],
parts[1] - 1,
parts[2],
parts[3],
parts[4],
parts[5],
)
return new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5])
}

View File

@ -28,23 +28,16 @@ export function detectBrowserTypeByComputedStyle() {
*/
export function detectBrowserByFeature() {
// Opera 8.0+
const isOpera =
(!!window.opr && !!opr.addons) ||
!!window.opera ||
navigator.userAgent.indexOf(" OPR/") >= 0
const isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(" OPR/") >= 0
// Firefox 1.0+
const isFirefox = typeof InstallTrigger !== "undefined"
// Safari 3.0+
const isSafari =
/constructor/i.test(window.HTMLElement) ||
(
typeof safari !== "undefined" && window["safari"].pushNotification
).toString() === "[object SafariRemoteNotification]"
(typeof safari !== "undefined" && window["safari"].pushNotification).toString() ===
"[object SafariRemoteNotification]"
// Internet Explorer 6-11
const isIE =
!!window.ActiveXObject ||
"ActiveXObject" in window ||
!!document.documentMode
const isIE = !!window.ActiveXObject || "ActiveXObject" in window || !!document.documentMode
// 旧 Edge 20+
const isEdge = !isIE && !!window.StyleMedia
// Chrome 1 - 79

View File

@ -10,11 +10,7 @@ import {
SpaceDashboardOutlined,
TerminalOutlined,
} from "@vicons/material"
import {
DocumentPdf32Regular,
SlideLayout24Regular,
Gif24Regular,
} from "@vicons/fluent"
import { DocumentPdf32Regular, SlideLayout24Regular, Gif24Regular } from "@vicons/fluent"
import { Java, Markdown, Vuejs } from "@vicons/fa"
import {
ArchiveOutline,
@ -31,10 +27,7 @@ import {
* @param content
* @returns
*/
export function renderTooltip(
trigger: VNode<RendererNode, RendererElement, { [key: string]: any }>,
content: string,
) {
export function renderTooltip(trigger: VNode<RendererNode, RendererElement, { [key: string]: any }>, content: string) {
return h(NTooltip, null, {
trigger: () => trigger,
default: () => content,