code lint
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -30,6 +30,7 @@ declare module 'vue' {
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
||||
NPagination: typeof import('naive-ui')['NPagination']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSplit: typeof import('naive-ui')['NSplit']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>前端开发学习平台</title>
|
||||
<link rel="stylesheet" href="/normalize.min.css" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<script>
|
||||
window.localStorage.setItem("maxkbMaskTip", true)
|
||||
|
||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.3",
|
||||
"@fsegurai/codemirror-theme-github-light": "^6.1.2",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"axios": "^1.8.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"github-markdown-css": "^5.8.1",
|
||||
@@ -22,7 +22,6 @@
|
||||
"marked-code-preview": "^1.3.7",
|
||||
"marked-highlight": "^2.2.1",
|
||||
"naive-ui": "^2.41.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-router": "^4.5.0"
|
||||
@@ -743,9 +742,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
@@ -874,14 +873,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "12.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.7.0.tgz",
|
||||
"integrity": "sha512-jtK5B7YjZXmkGNHjviyGO4s3ZtEhbzSgrbX+s5o+Lr8i2nYqNyHuPVOeTdM1/hZ5Tkxg/KktAuAVDDiHMraMVA==",
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz",
|
||||
"integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.20",
|
||||
"@vueuse/metadata": "12.7.0",
|
||||
"@vueuse/shared": "12.7.0",
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "12.8.2",
|
||||
"@vueuse/shared": "12.8.2",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"funding": {
|
||||
@@ -889,18 +888,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "12.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.7.0.tgz",
|
||||
"integrity": "sha512-4VvTH9mrjXqFN5LYa5YfqHVRI6j7R00Vy4995Rw7PQxyCL3z0Lli86iN4UemWqixxEvYfRjG+hF9wL8oLOn+3g==",
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz",
|
||||
"integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "12.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.7.0.tgz",
|
||||
"integrity": "sha512-coLlUw2HHKsm7rPN6WqHJQr18WymN4wkA/3ThFaJ4v4gWGWAQQGK+MJxLuJTBs4mojQiazlVWAKNJNpUWGRkNw==",
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz",
|
||||
"integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
@@ -2317,12 +2316,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize.css": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.3",
|
||||
"@fsegurai/codemirror-theme-github-light": "^6.1.2",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"axios": "^1.8.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"github-markdown-css": "^5.8.1",
|
||||
@@ -23,7 +23,6 @@
|
||||
"marked-code-preview": "^1.3.7",
|
||||
"marked-highlight": "^2.2.1",
|
||||
"naive-ui": "^2.41.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-router": "^4.5.0"
|
||||
|
||||
48
src/api.ts
48
src/api.ts
@@ -35,62 +35,62 @@ http.interceptors.response.use(
|
||||
},
|
||||
)
|
||||
|
||||
export class Account {
|
||||
static async login(username: string, password: string) {
|
||||
export const Account = {
|
||||
async login(username: string, password: string) {
|
||||
const res = await http.post("/account/login", { username, password })
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async logout() {
|
||||
async logout() {
|
||||
const res = await http.post("/account/logout")
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async getMyProfile() {
|
||||
async getMyProfile() {
|
||||
const res = await http.get("/account/profile")
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async list(query: { username: string; page: number }) {
|
||||
async list(query: { username: string; page: number }) {
|
||||
const res = await http.get("/account/list", {
|
||||
params: query,
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async toggleActive(id: number) {
|
||||
async toggleActive(id: number) {
|
||||
const res = await http.put(`/account/active/${id}`)
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export class Tutorial {
|
||||
static async list() {
|
||||
export const Tutorial = {
|
||||
async list() {
|
||||
const res = await http.get("/tutorial/list")
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async createOrUpdate(payload: TutorialIn) {
|
||||
async createOrUpdate(payload: TutorialIn) {
|
||||
const res = await http.post("/tutorial/", payload)
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async togglePublic(display: number) {
|
||||
async togglePublic(display: number) {
|
||||
const res = await http.put(`/tutorial/public/${display}`)
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async remove(display: number) {
|
||||
async remove(display: number) {
|
||||
await http.delete(`/tutorial/${display}`)
|
||||
}
|
||||
},
|
||||
|
||||
static async get(display: number) {
|
||||
async get(display: number) {
|
||||
const res = await http.get(`/tutorial/${display}`)
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
|
||||
static async listDisplay() {
|
||||
async listDisplay() {
|
||||
const res = await http.get("/tutorial/display")
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -32,13 +32,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
const code = defineModel<string>("value")
|
||||
|
||||
const lang = computed(() => {
|
||||
if (props.language === "html") {
|
||||
return html()
|
||||
} else if (props.language === "css") {
|
||||
return css()
|
||||
} else {
|
||||
if (props.language === "html") return html()
|
||||
if (props.language === "css") return css()
|
||||
return javascript()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Icon icon="noto:eyes" :width="20"></Icon>
|
||||
<n-text class="titleText">预览</n-text>
|
||||
</n-flex>
|
||||
<n-button quaternary @click="download">下载</n-button>
|
||||
<n-button quaternary @click="download" :disabled="!showDL">下载</n-button>
|
||||
</n-flex>
|
||||
<iframe class="iframe" ref="iframe"></iframe>
|
||||
</template>
|
||||
@@ -12,10 +12,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { watchDebounced } from "@vueuse/core"
|
||||
import { html, css, js } from "../store/editors"
|
||||
import { onMounted, useTemplateRef } from "vue"
|
||||
import { computed, onMounted, useTemplateRef } from "vue"
|
||||
import { Icon } from "@iconify/vue"
|
||||
|
||||
const iframe = useTemplateRef<HTMLIFrameElement>("iframe")
|
||||
const showDL = computed(() => html.value || css.value || js.value)
|
||||
|
||||
function getContent() {
|
||||
return `<!DOCTYPE html>
|
||||
@@ -37,11 +38,13 @@ function getContent() {
|
||||
|
||||
function preview() {
|
||||
if (!iframe.value) return
|
||||
const doc = iframe.value.contentDocument!
|
||||
const doc = iframe.value.contentDocument
|
||||
if (doc) {
|
||||
doc.open()
|
||||
doc.write(getContent())
|
||||
doc.close()
|
||||
}
|
||||
}
|
||||
|
||||
function download() {
|
||||
const content = getContent()
|
||||
|
||||
@@ -75,8 +75,8 @@ async function getContent() {
|
||||
function addButton() {
|
||||
const action = document.createElement("div")
|
||||
action.className = "codeblock-action"
|
||||
const pres = $content.value!.querySelectorAll("pre")
|
||||
pres.forEach((pre) => {
|
||||
const pres = $content.value?.querySelectorAll("pre") ?? []
|
||||
for (const pre of pres) {
|
||||
let timer = 0
|
||||
const copy = action.cloneNode() as HTMLDivElement
|
||||
pre.insertBefore(copy, pre.children[0])
|
||||
@@ -88,7 +88,7 @@ function addButton() {
|
||||
const btn = copy.children[1] as HTMLDivElement
|
||||
btn.onclick = () => {
|
||||
tab.value = lang
|
||||
const content = pre.children[1].textContent!
|
||||
const content = pre.children[1].textContent
|
||||
if (lang === "html") html.value = content
|
||||
if (lang === "css") css.value = content
|
||||
if (lang === "js") js.value = content
|
||||
@@ -99,12 +99,14 @@ function addButton() {
|
||||
btn.innerHTML = "替换"
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function modifyLink() {
|
||||
const links = $content.value!.querySelectorAll("a")
|
||||
links.forEach((link) => (link.target = "_blank"))
|
||||
const links = $content.value?.querySelectorAll("a") ?? []
|
||||
for (const link of links) {
|
||||
link.target = "_blank"
|
||||
}
|
||||
}
|
||||
|
||||
async function render() {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<n-flex>
|
||||
<n-button
|
||||
secondary
|
||||
v-if="user.role !== Role.Super"
|
||||
@click="edit"
|
||||
>
|
||||
<n-button secondary v-if="user.role !== Role.Super" @click="edit">
|
||||
修改
|
||||
</n-button>
|
||||
<n-button
|
||||
|
||||
@@ -2,8 +2,6 @@ import { createApp } from "vue"
|
||||
import { create } from "naive-ui"
|
||||
import App from "./App.vue"
|
||||
|
||||
//@ts-ignore
|
||||
import "normalize.css"
|
||||
//@ts-ignore
|
||||
import "github-markdown-css/github-markdown-light.css"
|
||||
|
||||
|
||||
@@ -2,11 +2,21 @@
|
||||
<n-flex vertical class="container">
|
||||
<n-flex>
|
||||
<div>
|
||||
<n-input v-model:value="query.username" clearable />
|
||||
<n-input
|
||||
style="width: 200px"
|
||||
v-model:value="query.username"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<n-select
|
||||
style="width: 120px"
|
||||
v-model:value="query.role"
|
||||
:options="roles"
|
||||
/>
|
||||
</div>
|
||||
<n-button @click="init">搜索</n-button>
|
||||
<n-button @click="goDjangoUserAdd">新建一个</n-button>
|
||||
<n-button>批量新建</n-button>
|
||||
<n-button @click="showBatch = true">批量新建</n-button>
|
||||
<n-pagination
|
||||
v-model:page="query.page"
|
||||
:page-size="20"
|
||||
@@ -15,6 +25,20 @@
|
||||
/>
|
||||
</n-flex>
|
||||
<n-data-table :columns="columns" :data="users"></n-data-table>
|
||||
<n-modal
|
||||
style="width: 300px"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
title="批量新建用户"
|
||||
v-model:show="showBatch"
|
||||
>
|
||||
<n-flex vertical>
|
||||
<b>前缀:web</b>
|
||||
<n-input placeholder="班级" />
|
||||
<n-input rows="20" type="textarea" />
|
||||
<n-button type="primary">提交</n-button>
|
||||
</n-flex>
|
||||
</n-modal>
|
||||
</n-flex>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -22,10 +46,11 @@ import { onMounted, reactive, ref, h, watch } from "vue"
|
||||
import { Account } from "../api"
|
||||
import { parseTime } from "../utils/helper"
|
||||
import type { DataTableColumn } from "naive-ui"
|
||||
import { getRole, type User } from "../utils/type"
|
||||
import { getRole, Role, type User } from "../utils/type"
|
||||
import { ADMIN_URL } from "../utils/const"
|
||||
import UserActions from "../components/dashboard/UserActions.vue"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
import { watchDebounced } from "@vueuse/core"
|
||||
|
||||
const users = ref([])
|
||||
const count = ref(0)
|
||||
@@ -34,7 +59,16 @@ const router = useRouter()
|
||||
const query = reactive({
|
||||
username: "",
|
||||
page: Number(route.params.page),
|
||||
role: "",
|
||||
})
|
||||
const showBatch = ref(true)
|
||||
|
||||
const roles = [
|
||||
{ label: "全部权限", value: "" },
|
||||
{ label: "普通用户", value: Role.Normal },
|
||||
{ label: "管理员", value: Role.Admin },
|
||||
{ label: "超级管理员", value: Role.Super },
|
||||
]
|
||||
|
||||
const columns: DataTableColumn<User>[] = [
|
||||
{
|
||||
@@ -80,7 +114,8 @@ async function init() {
|
||||
count.value = data.count
|
||||
}
|
||||
|
||||
watch(query, init)
|
||||
watch(() => [query.page, query.role], init)
|
||||
watchDebounced(() => query.username, init, { debounce: 500, maxWait: 1000 })
|
||||
watch(
|
||||
() => query.page,
|
||||
(v) => router.push({ params: { page: v } }),
|
||||
|
||||
Reference in New Issue
Block a user