Skip to content

Commit eac0f73

Browse files
committed
fix: 修复诸多问题。
- 代码块中不应该有 heading - 非管理员不能上传 - 复制 - 跳转后关闭选择专业 - 登录过期时间 - 手机专业选择样式问题 - 手机端没有文档类型提示语
1 parent e944b74 commit eac0f73

14 files changed

Lines changed: 77 additions & 103 deletions

File tree

components/TopTabs.vue

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
<template>
22
<div class="d-flex">
3-
<v-menu v-if="schoolMajors" open-on-hover :open-on-click="$vuetify.display.mobile" offset-y :close-on-content-click="false" attach="body">
3+
<v-menu v-if="schoolMajors" v-model="openeMajors" open-on-hover :open-on-click="$vuetify.display.mobile" offset-y :close-on-content-click="false" attach="body">
44
<template #activator="{ props }">
55
<v-btn v-if="!$vuetify.display.mobile" v-bind="props" variant="text" append-icon="mdi-chevron-down">选择专业</v-btn>
66
<v-btn v-else v-bind="props" variant="text" icon="mdi-library-shelves" />
77
</template>
88
<v-card>
9-
<v-card-text class="overflow-auto-x" style="width: 60em">
9+
<v-card-text class="overflow-auto-x">
1010
<div class="top-tabs-searchbar d-md-none mb-1">
1111
<SearchBar />
1212
</div>
1313
<v-row>
1414
<v-col v-for="j in 3" :key="j" cols="12" md="4">
15-
<v-expansion-panels variant="accordion" flat width="20em">
15+
<v-expansion-panels variant="accordion" flat :style="{ width: !$vuetify.display.mobile ? '20em' : undefined }" >
1616
<v-expansion-panel
1717
v-for="i in Math.min(Math.floor((schoolMajors.length + 2) / 3), schoolMajors.length - Math.floor((schoolMajors.length + 2) / 3) * (j - 1))"
18-
:key="i" max-width="20em"
18+
:key="i" :max-width="!$vuetify.display.mobile ? '20em' : undefined"
1919
:title="schoolMajors[i - 1 + (j - 1) * Math.floor((schoolMajors.length + 2) / 3)][0]">
2020
<v-expansion-panel-text>
2121
<v-list-item
2222
v-for="m in schoolMajors[i - 1 + (j - 1) * Math.floor((schoolMajors.length + 2) / 3)][1]"
23-
:key="m.major_id" :title="m.name" density="compact" :to="`/docs/${m.major_id}`">
23+
:key="m.major_id" :title="m.name" density="compact" :to="`/docs/${m.major_id}`" >
2424
<template #prepend>
2525
<v-icon>mdi-book-education-outline</v-icon>
2626
</template>
@@ -37,7 +37,7 @@
3737
<v-btn v-if="!$vuetify.display.mobile" variant="text" class="ml-6" to="/docs/0/0" :active="false">简介</v-btn>
3838
<v-btn v-else variant="text" to="/docs/0/0" icon="mdi-book-play-outline" :active="false"/>
3939

40-
<div class="top-tabs-searchbar ml-6 d-none d-md-block">
40+
<div v-if="!$vuetify.display.mobile" class="top-tabs-searchbar ml-6">
4141
<SearchBar />
4242
</div>
4343
</div>
@@ -68,6 +68,11 @@ import SearchBar from '~/components/SearchBar.vue';
6868
6969
const { loggedIn, user, clear } = useUserSession();
7070
71+
const openeMajors = ref(false);
72+
const route = useRoute();
73+
74+
watch(() => route.path, () => { openeMajors.value = false});
75+
7176
const requestFetch = useRequestFetch();
7277
/**
7378
* 获得各个学院下的专业

nuxt.config.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ export default defineNuxtConfig({
44
devtools: { enabled: false },
55

66
modules: [
7-
"@nuxt/eslint",
8-
"@nuxt/scripts",
9-
"@nuxt/test-utils",
10-
"vuetify-nuxt-module",
11-
"nuxt-auth-utils",
12-
"nuxt-monaco-editor",
13-
"@nuxtjs/mdc",
7+
"@nuxt/eslint",
8+
"@nuxt/scripts",
9+
"@nuxt/test-utils",
10+
"vuetify-nuxt-module",
11+
"nuxt-auth-utils",
12+
"nuxt-monaco-editor",
13+
"@nuxtjs/mdc",
1414
],
1515

1616
runtimeConfig: {
@@ -28,6 +28,10 @@ export default defineNuxtConfig({
2828
footerNote: "",
2929
siteSubtitle: "",
3030
},
31+
session: {
32+
maxAge: 60 * 60 * 24 * 15,
33+
password: process.env.NUXT_SESSION_PASSWORD || '',
34+
},
3135
},
3236

3337
monacoEditor: {
@@ -41,11 +45,21 @@ export default defineNuxtConfig({
4145
rehypePlugins: {
4246
"rehype-mathjax": {},
4347
},
44-
highlight: {
48+
highlight: {
4549
theme: "github-light",
46-
langs: ["ts", "cpp", "markdown", "py", "rust", "c#", "js", "json", "c"],
47-
wrapperStyle: true
48-
},
50+
langs: [
51+
"ts",
52+
"cpp",
53+
"markdown",
54+
"py",
55+
"rust",
56+
"c#",
57+
"js",
58+
"json",
59+
"c",
60+
],
61+
wrapperStyle: true,
62+
}
4963
},
5064
vuetify: {
5165
vuetifyOptions: {
@@ -55,4 +69,4 @@ export default defineNuxtConfig({
5569
localeMessages: ["zhHans"],
5670
},
5771
},
58-
});
72+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@nuxtjs/mdc": "0.17.0",
1717
"@unhead/vue": "^2.0.11",
1818
"axios": "^0.27.2",
19+
"clipboard": "^2.0.11",
1920
"eslint": "^9.30.1",
2021
"formidable": "^3.5.4",
2122
"monaco-editor": "^0.52.2",
@@ -29,7 +30,6 @@
2930
"remark-math": "^6.0.0",
3031
"typescript": "^5.8.3",
3132
"vue": "^3.5.17",
32-
"vue-clipboard3": "^2.0.0",
3333
"vue-router": "^4.5.1",
3434
"vuetify": "^3.8.12",
3535
"vuetify-nuxt-module": "0.18.7",

pages/docs/[major]/[doc].vue

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@
5858
<template #prepend>
5959
<p class="text-h6 mt-4">目录</p>
6060
</template>
61-
<div v-if="toc.length" class="toc-container mt-4">
61+
<div v-if="ast?.toc?.links && ast.toc.links.length" class="toc-container mt-4">
6262
<ul class="toc-list">
63-
<li v-for="item in toc" :key="item.id" :class="{ 'active-toc': activeTocId === item.id }"
64-
:style="{ paddingLeft: (item.level - 1) * 16 + 'px' }">
63+
<li v-for="item in ast.toc.links" :key="item.id" :class="{ 'active-toc': activeTocId === item.id }"
64+
:style="{ paddingLeft: (item.depth - 1) * 16 + 'px' }">
6565
<a @click.prevent="scrollToAnchor(item.id)">{{ item.text }}</a>
6666
</li>
6767
</ul>
@@ -104,13 +104,8 @@ if (course.value?.link) {
104104
})
105105
}
106106
107-
interface TocItem {
108-
level: number;
109-
text: string;
110-
id: string;
111-
}
107+
const { data: ast } = useAsyncData(() => parseMarkdown(course.value?.doc_str ?? "", { toc: { depth: 4 } }));
112108
113-
const toc = ref<TocItem[]>([]);
114109
const activeTocId = ref('');
115110
116111
function updateActiveToc() {
@@ -141,41 +136,6 @@ function updateActiveToc() {
141136
});
142137
}
143138
onMounted(() => {
144-
watch(
145-
() => course.value?.doc_str as string,
146-
async (val) => {
147-
if (!val) {
148-
toc.value = [];
149-
return;
150-
}
151-
const lines = (val as string).split('\n');
152-
const headingRE = /^(#{1,6})\s+(.+)/;
153-
toc.value = lines
154-
.map((line): TocItem | null => {
155-
const match = line.match(headingRE);
156-
if (match) {
157-
return {
158-
level: match[1].length,
159-
text: match[2].trim(),
160-
id: match[2].trim().toLowerCase().replace(/\s+/g, '-')
161-
};
162-
}
163-
return null;
164-
})
165-
.filter((item): item is TocItem => !!item);
166-
167-
toc.value.forEach(item => {
168-
const selector = `h${item.level}`;
169-
const headings = document.querySelectorAll(selector);
170-
headings.forEach(heading => {
171-
if (heading.textContent && heading.textContent.trim() === item.text) {
172-
item.id = heading.id;
173-
}
174-
});
175-
});
176-
},
177-
{ immediate: true }
178-
);
179139
window.addEventListener('scroll', updateActiveToc, { passive: true });
180140
nextTick(updateActiveToc);
181141
@@ -270,7 +230,7 @@ h6 {
270230
margin-top: 1em;
271231
}
272232
273-
.article code {
233+
.article .shiki {
274234
margin-top: 1em;
275235
}
276236

pages/edit.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ const submitDel = () => {
133133
}).then(() => {
134134
successSnakebar.value = true;
135135
}).catch((err) => {
136-
errorPrompt.value = err.data.message;
136+
errorPrompt.value = `${err.statusCode}: ${err.data.message}`;
137137
errorSnakebar.value = true;
138138
});
139139
};

pages/edit/[major].vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
22
<div class="fill-height">
3-
<v-navigation-drawer v-if="listItems" mobile-breakpoint="sm" class="position-fixed" :model-value="!$vuetify.display.mobile || (props.sidebar && props.sidebarSwap)" persistent>
3+
<v-navigation-drawer v-if="listItems" mobile-breakpoint="sm" class="position-fixed"
4+
:model-value="!$vuetify.display.mobile || (props.sidebar && props.sidebarSwap)" persistent>
45
<v-list v-if="Number(majorId) != 0">
56
<v-list-group v-for="(gradeCourses, index) in listItems" v-show="gradeCourses.length" :key="index"
67
:value="index">
@@ -10,7 +11,7 @@
1011
</template>
1112
<div v-for="(classAndCourses, i) in gradeCourses" :key="i">
1213
<v-divider />
13-
<v-list-subheader v-if="classAndCourses[0] "> {{ classAndCourses[0] }}</v-list-subheader>
14+
<v-list-subheader v-if="classAndCourses[0]"> {{ classAndCourses[0] }}</v-list-subheader>
1415
<v-list-item v-for="course in classAndCourses[1]" :key="course.course_id"
1516
:title="course.course_name" :active="Number($route.params.doc) == course.course_id"
1617
@click="navigateTo(`/edit/${route.params.major}/${course.course_id}`)" />
@@ -67,10 +68,10 @@
6768
</v-row>
6869
</div>
6970

70-
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success" variant="tonal">
71+
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success">
7172
提交成功
7273
</v-snackbar>
73-
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error" variant="tonal">
74+
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error">
7475
{{ errorPrompt }}
7576
</v-snackbar>
7677
</div>
@@ -109,7 +110,7 @@ const proposeDelete = () => {
109110
})
110111
.then(() => { successSnakebar.value = true; })
111112
.catch((err) => {
112-
errorPrompt.value = err.data.message;
113+
errorPrompt.value = `${err.statusCode}: ${err.data.message}`;
113114
errorSnakebar.value = true;
114115
})
115116
};

pages/edit/[major]/[doc].vue

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
<v-col cols="12" md="6">
1616
<v-btn-toggle v-model="toggle" divided>
1717
<v-btn value="unique">
18-
<span class="hidden-sm-and-down">独立页面</span>
18+
<span>独立页面</span>
1919

2020
<v-icon end>
2121
mdi-format-align-left
2222
</v-icon>
2323
</v-btn>
2424

2525
<v-btn value="other">
26-
<span class="hidden-sm-and-down">使用已有文档</span>
26+
<span>使用已有文档</span>
2727

2828
<v-icon end>
2929
mdi-open-in-new
@@ -123,16 +123,16 @@
123123
:subtitle="a.timestamp">
124124
<template #append>
125125
<v-btn icon="mdi-content-copy" variant="text" density="compact"
126-
@click="toClipboard(`/api/files/${a.file_id}`)" />
126+
@click="clipboard.copy(`/api/files/${a.file_id}`)" />
127127
</template>
128128
</v-list-item>
129129
</v-list>
130130
</v-navigation-drawer>
131131

132-
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success" variant="tonal">
132+
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success">
133133
提交成功
134134
</v-snackbar>
135-
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error" variant="tonal">
135+
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error">
136136
{{ errorPrompt }}
137137
</v-snackbar>
138138
</div>
@@ -156,11 +156,10 @@ import type { VTabs } from 'vuetify/components';
156156
import type { VRow } from 'vuetify/components/VGrid';
157157
import axios from 'axios';
158158
import type { AxiosResponse } from 'axios';
159-
import useClipboard from "vue-clipboard3";
159+
import clipboard from "clipboard";
160160
import type { CourseWithDbId } from '~/utils/types';
161161
162162
const route = useRoute();
163-
const { toClipboard } = await useClipboard();
164163
const majorId = route.params.major;
165164
/**
166165
* 课程编号
@@ -365,7 +364,7 @@ const submit = () => {
365364
}).then(() => {
366365
submitted.value = true;
367366
}).catch((err) => {
368-
errorPrompt.value = err.data.message;
367+
errorPrompt.value = `${err.statusCode}: ${err.data.message}`;
369368
errorSnakebar.value = true;
370369
});
371370
}
@@ -383,7 +382,7 @@ const uploadFiles = () => {
383382
axios.post('/api/files/upload', formData, {
384383
onUploadProgress: (progressEvent) => {
385384
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total!);
386-
uploadProgress.value = percentCompleted;
385+
uploadProgress.value = Math.min(percentCompleted, 95);
387386
},
388387
}).then((res: AxiosResponse<{ resList: AttachmentInfo[] }>) => {
389388
newAttachments.value = newAttachments.value.concat(res.data.resList);

pages/index.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
<p class="text-disabled text-h6">{{ runtimeConfig.public.siteSubtitle }}</p>
77
<div class="mt-4">
88
<v-btn class="ma-4" variant="flat" size="large" color="primary" @click="navigateTo('/docs/0/0')">简介</v-btn>
9-
<v-btn class="ma-4" variant="outlined" size="large" color="primary" @click="navigateTo('/signup')">立即注册</v-btn>
9+
<v-btn v-if="!session.loggedIn" class="ma-4" variant="outlined" size="large" color="primary" @click="navigateTo('/signup')">立即注册</v-btn>
10+
<v-btn v-else class="ma-4" variant="outlined" size="large" color="primary" @click="navigateTo('/edit')">我要贡献</v-btn>
1011
</div>
1112
</v-col>
1213
</v-row>
@@ -15,4 +16,5 @@
1516

1617
<script setup lang="ts">
1718
const runtimeConfig = useRuntimeConfig();
19+
const session = useUserSession();
1820
</script>

pages/login.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
</p>
1414
<div>
1515
<span>还没有账号?</span>
16-
<a href="/signup">立即注册</a>
16+
<v-a to="/signup">立即注册</v-a>
17+
<v-a to="/signup" class="ml-2">忘记密码</v-a>
1718
</div>
1819
</div>
1920
</v-form>

pages/review/docs/[id].vue

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,17 @@
114114
<v-list-item v-for="(a, index) in newAttachments" :key="index" :title="a.name" :subtitle="a.timestamp">
115115
<template #append>
116116
<v-btn icon="mdi-content-copy" variant="text" density="compact"
117-
@click="toClipboard(`/api/files/${a.file_id}`)" />
117+
@click="clipboard.copy(`/api/files/${a.file_id}`)" />
118118
<v-btn icon="mdi-download" variant="text" density="compact" :href="`/api/files/${a.file_id}`" />
119119
</template>
120120
</v-list-item>
121121
</v-list>
122122
</v-navigation-drawer>
123123

124-
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success" variant="tonal">
124+
<v-snackbar v-model="successSnakebar" :timeout="2000" color="success">
125125
提交成功
126126
</v-snackbar>
127-
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error" variant="tonal">
127+
<v-snackbar v-model="errorSnakebar" :timeout="2000" color="error">
128128
{{ errorPrompt }}
129129
</v-snackbar>
130130
</div>
@@ -133,10 +133,9 @@
133133
<script setup lang="ts">
134134
import type { VTabs } from 'vuetify/components';
135135
import type { VRow } from 'vuetify/components/VGrid';
136-
import useClipboard from "vue-clipboard3";
136+
import clipboard from "clipboard";
137137
138138
const route = useRoute();
139-
const { toClipboard } = useClipboard();
140139
141140
/**
142141
* 文档更新申请编号
@@ -256,7 +255,7 @@ const submit = (accept: string | true) => {
256255
successSnakebar.value = true;
257256
navigateTo("/review/docs");
258257
}).catch((err) => {
259-
errorPrompt.value = err.data.message;
258+
errorPrompt.value = `${err.statusCode}: ${err.data.message}`;
260259
errorSnakebar.value = true;
261260
});
262261
} else {

0 commit comments

Comments
 (0)