(
47 | url: string,
48 | params?: T,
49 | config?: PureHttpRequestConfig
50 | ): Promise;
51 | get(
52 | url: string,
53 | params?: T,
54 | config?: PureHttpRequestConfig
55 | ): Promise;
56 | }
57 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/utils/mitt.ts:
--------------------------------------------------------------------------------
1 | import type { Emitter } from "mitt";
2 | import mitt from "mitt";
3 |
4 | type Events = {
5 | resize: {
6 | detail: {
7 | width: number;
8 | height: number;
9 | };
10 | };
11 | openPanel: string;
12 | tagViewsChange: string;
13 | tagViewsShowModel: string;
14 | logoChange: boolean;
15 | changLayoutRoute: {
16 | indexPath: string;
17 | parentPath: string;
18 | };
19 | };
20 |
21 | export const emitter: Emitter = mitt();
22 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/utils/progress/index.ts:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress";
2 | import "nprogress/nprogress.css";
3 |
4 | NProgress.configure({
5 | // 动画方式
6 | easing: "ease",
7 | // 递增进度条的速度
8 | speed: 500,
9 | // 是否显示加载ico
10 | showSpinner: false,
11 | // 自动递增间隔
12 | trickleSpeed: 200,
13 | // 初始化时的最小百分比
14 | minimum: 0.3
15 | });
16 |
17 | export default NProgress;
18 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/utils/propTypes.ts:
--------------------------------------------------------------------------------
1 | import { CSSProperties, VNodeChild } from "vue";
2 | import { createTypes, VueTypeValidableDef, VueTypesInterface } from "vue-types";
3 |
4 | export type VueNode = VNodeChild | JSX.Element;
5 |
6 | type PropTypes = VueTypesInterface & {
7 | readonly style: VueTypeValidableDef;
8 | readonly VNodeChild: VueTypeValidableDef;
9 | };
10 |
11 | const propTypes = createTypes({
12 | func: undefined,
13 | bool: undefined,
14 | string: undefined,
15 | number: undefined,
16 | object: undefined,
17 | integer: undefined
18 | }) as PropTypes;
19 |
20 | propTypes.extend([
21 | {
22 | name: "style",
23 | getter: true,
24 | type: [String, Object],
25 | default: undefined
26 | },
27 | {
28 | name: "VNodeChild",
29 | getter: true,
30 | type: undefined
31 | }
32 | ]);
33 |
34 | export { propTypes };
35 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/utils/responsive.ts:
--------------------------------------------------------------------------------
1 | // 响应式storage
2 | import { App } from "vue";
3 | import Storage from "responsive-storage";
4 | import { routerArrays } from "@/layout/types";
5 |
6 | const nameSpace = "responsive-";
7 |
8 | export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
9 | const configObj = Object.assign(
10 | {
11 | // layout模式以及主题
12 | layout: Storage.getData("layout", nameSpace) ?? {
13 | layout: config.Layout ?? "vertical",
14 | theme: config.Theme ?? "default",
15 | darkMode: config.DarkMode ?? false,
16 | sidebarStatus: config.SidebarStatus ?? true,
17 | epThemeColor: config.EpThemeColor ?? "#409EFF"
18 | },
19 | configure: Storage.getData("configure", nameSpace) ?? {
20 | grey: config.Grey ?? false,
21 | weak: config.Weak ?? false,
22 | hideTabs: config.HideTabs ?? false,
23 | showLogo: config.ShowLogo ?? true,
24 | showModel: config.ShowModel ?? "smart",
25 | multiTagsCache: config.MultiTagsCache ?? false
26 | }
27 | },
28 | config.MultiTagsCache
29 | ? {
30 | // 默认显示首页tag
31 | tags: Storage.getData("tags", nameSpace) ?? routerArrays
32 | }
33 | : {}
34 | );
35 |
36 | app.use(Storage, { nameSpace, memory: configObj });
37 | };
38 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/utils/sso.ts:
--------------------------------------------------------------------------------
1 | import { removeToken, setToken, type DataInfo } from "./auth";
2 | import { subBefore, getQueryMap } from "@pureadmin/utils";
3 |
4 | /**
5 | * 简版前端单点登录,根据实际业务自行编写
6 | * 划重点:
7 | * 判断是否为单点登录,不为则直接返回不再进行任何逻辑处理,下面是单点登录后的逻辑处理
8 | * 1.清空本地旧信息;
9 | * 2.获取url中的重要参数信息,然后通过 setToken 保存在本地;
10 | * 3.删除不需要显示在 url 的参数
11 | * 4.使用 window.location.replace 跳转正确页面
12 | */
13 | (function () {
14 | // 获取 url 中的参数
15 | const params = getQueryMap(location.href) as DataInfo;
16 | const must = ["username", "roles", "accessToken"];
17 | const mustLength = must.length;
18 | if (Object.keys(params).length !== mustLength) return;
19 |
20 | // url 参数满足 must 里的全部值,才判定为单点登录,避免非单点登录时刷新页面无限循环
21 | let sso = [];
22 | let start = 0;
23 |
24 | while (start < mustLength) {
25 | if (Object.keys(params).includes(must[start]) && sso.length <= mustLength) {
26 | sso.push(must[start]);
27 | } else {
28 | sso = [];
29 | }
30 | start++;
31 | }
32 |
33 | if (sso.length === mustLength) {
34 | // 判定为单点登录
35 |
36 | // 清空本地旧信息
37 | removeToken();
38 |
39 | // 保存新信息到本地
40 | setToken(params);
41 |
42 | // 删除不需要显示在 url 的参数
43 | delete params["roles"];
44 | delete params["accessToken"];
45 |
46 | const newUrl = `${location.origin}${location.pathname}${subBefore(
47 | location.hash,
48 | "?"
49 | )}?${JSON.stringify(params)
50 | .replace(/["{}]/g, "")
51 | .replace(/:/g, "=")
52 | .replace(/,/g, "&")}`;
53 |
54 | // 替换历史记录项
55 | window.location.replace(newUrl);
56 | } else {
57 | return;
58 | }
59 | })();
60 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/article/add-edit-article/validator.ts:
--------------------------------------------------------------------------------
1 | export const tagV = (rule, value, cb) => {
2 | if (!value.length) {
3 | return cb(new Error("请选择标签"));
4 | }
5 | cb();
6 | };
7 |
8 | export const coverV = (rule, value, cb) => {
9 | if (!value.length) {
10 | return cb(new Error("请先上传封面"));
11 | }
12 | cb();
13 | };
14 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/error/404.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
24 | 404
25 |
26 |
41 | 抱歉,你访问的页面不存在
42 |
43 |
59 | 返回首页
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/login/utils/motion.ts:
--------------------------------------------------------------------------------
1 | import { h, defineComponent, withDirectives, resolveDirective } from "vue";
2 |
3 | /** 封装@vueuse/motion动画库中的自定义指令v-motion */
4 | export default defineComponent({
5 | name: "Motion",
6 | props: {
7 | delay: {
8 | type: Number,
9 | default: 50
10 | }
11 | },
12 | render() {
13 | const { delay } = this;
14 | const motion = resolveDirective("motion");
15 | return withDirectives(
16 | h(
17 | "div",
18 | {},
19 | {
20 | default: () => [this.$slots.default()]
21 | }
22 | ),
23 | [
24 | [
25 | motion,
26 | {
27 | initial: { opacity: 0, y: 100 },
28 | enter: {
29 | opacity: 1,
30 | y: 0,
31 | transition: {
32 | delay
33 | }
34 | }
35 | }
36 | ]
37 | ]
38 | );
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/login/utils/rule.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from "vue";
2 | import type { FormRules } from "element-plus";
3 |
4 | /** 密码正则(密码格式应为6-18位数字、字母、符号的任意两种组合) */
5 | export const REGEXP_PWD =
6 | /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){6,18}$/;
7 |
8 | /** 登录校验 */
9 | const loginRules = reactive({
10 | password: [
11 | {
12 | validator: (rule, value, callback) => {
13 | if (value === "") {
14 | callback(new Error("请输入密码"));
15 | } else if (!REGEXP_PWD.test(value)) {
16 | callback(
17 | new Error("密码格式应为6-18位数字、字母、符号的任意两种组合")
18 | );
19 | } else {
20 | callback();
21 | }
22 | },
23 | trigger: "blur"
24 | }
25 | ]
26 | });
27 |
28 | export { loginRules };
29 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/login/utils/static.ts:
--------------------------------------------------------------------------------
1 | import bg from "@/assets/login/bg.png";
2 | import avatar from "@/assets/login/avatar.svg?component";
3 | import illustration from "@/assets/login/illustration.svg?component";
4 |
5 | export { bg, avatar, illustration };
6 |
--------------------------------------------------------------------------------
/blog-v3-admin/src/views/pageheader/routerList.js:
--------------------------------------------------------------------------------
1 | export const routerList = [
2 | {
3 | label: "首页",
4 | value: "Home",
5 | disabled: false
6 | },
7 | {
8 | label: "时间轴",
9 | value: "Archives",
10 | disabled: false
11 | },
12 | {
13 | label: "文章列表",
14 | value: "ArticleList",
15 | disabled: false
16 | },
17 | {
18 | label: "前端",
19 | value: "Front",
20 | disabled: false
21 | },
22 | {
23 | label: "后端",
24 | value: "Back",
25 | disabled: false
26 | },
27 | {
28 | label: "网站列表",
29 | value: "SiteList",
30 | disabled: false
31 | },
32 | {
33 | label: "友链",
34 | value: "LinkList",
35 | disabled: false
36 | },
37 | {
38 | label: "分类",
39 | value: "Category",
40 | disabled: false
41 | },
42 | {
43 | label: "标签",
44 | value: "Tag",
45 | disabled: false
46 | },
47 | {
48 | label: "相册",
49 | value: "PhotoAlbum",
50 | disabled: false
51 | },
52 | {
53 | label: "说说",
54 | value: "Talk",
55 | disabled: false
56 | },
57 | {
58 | label: "留言",
59 | value: "MessageList",
60 | disabled: false
61 | },
62 | {
63 | label: "留言发布/编辑",
64 | value: "PublishMessage",
65 | disabled: false
66 | },
67 | {
68 | label: "留言详情",
69 | value: "MessageDetail",
70 | disabled: false
71 | },
72 | {
73 | label: "用户中心",
74 | value: "UserCenter",
75 | disabled: false
76 | }
77 | ];
78 |
--------------------------------------------------------------------------------
/blog-v3-admin/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: "class",
4 | corePlugins: {
5 | preflight: false
6 | },
7 | content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
8 | theme: {
9 | extend: {
10 | colors: {
11 | bg_color: "var(--el-bg-color)",
12 | primary: "var(--el-color-primary)",
13 | primary_light_9: "var(--el-color-primary-light-9)",
14 | text_color_primary: "var(--el-text-color-primary)",
15 | text_color_regular: "var(--el-text-color-regular)",
16 | text_color_disabled: "var(--el-text-color-disabled)"
17 | }
18 | }
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/blog-v3-admin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "Node",
6 | "strict": false,
7 | "jsx": "preserve",
8 | "importHelpers": true,
9 | "experimentalDecorators": true,
10 | "strictFunctionTypes": false,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "isolatedModules": true,
14 | "allowSyntheticDefaultImports": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "sourceMap": true,
17 | "baseUrl": ".",
18 | "allowJs": false,
19 | "resolveJsonModule": true,
20 | "lib": ["dom", "esnext"],
21 | "incremental": true,
22 | "paths": {
23 | "@/*": ["src/*"],
24 | "@build/*": ["build/*"]
25 | },
26 | "types": [
27 | "node",
28 | "vite/client",
29 | "element-plus/global",
30 | "@pureadmin/table/volar",
31 | "@pureadmin/descriptions/volar",
32 | "unplugin-vue-define-options/macros-global"
33 | ],
34 | "typeRoots": ["./node_modules/@types/", "./types"]
35 | },
36 | "include": [
37 | "mock/*.ts",
38 | "src/**/*.ts",
39 | "src/**/*.tsx",
40 | "src/**/*.vue",
41 | "types/*.d.ts",
42 | "vite.config.ts"
43 | ],
44 | "exclude": ["node_modules", "dist", "**/*.js"]
45 | }
46 |
--------------------------------------------------------------------------------
/blog-v3-admin/types/shims-tsx.d.ts:
--------------------------------------------------------------------------------
1 | import Vue, { VNode } from "vue";
2 |
3 | declare module "*.tsx" {
4 | import Vue from "compatible-vue";
5 | export default Vue;
6 | }
7 |
8 | declare global {
9 | namespace JSX {
10 | interface Element extends VNode {}
11 | interface ElementClass extends Vue {}
12 | interface ElementAttributesProperty {
13 | $props: any;
14 | }
15 | interface IntrinsicElements {
16 | [elem: string]: any;
17 | }
18 | interface IntrinsicAttributes {
19 | [elem: string]: any;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/blog-v3-admin/types/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import { DefineComponent } from "vue";
3 | const component: DefineComponent<{}, {}, any>;
4 | export default component;
5 | }
6 |
7 | declare module "*.scss" {
8 | const scss: Record;
9 | export default scss;
10 | }
11 |
--------------------------------------------------------------------------------
/blog-v3/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | /node_modules
3 | /dist
4 | .DS_Store
5 | /src/assets
6 | /src/api
--------------------------------------------------------------------------------
/blog-v3/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | // .eslintrc.js
2 |
3 | // @ts-check
4 | const { defineConfig } = require("eslint-define-config");
5 |
6 | module.exports = defineConfig({
7 | root: true,
8 | env: {
9 | browser: true,
10 | node: true,
11 | es6: true,
12 | },
13 | parser: "vue-eslint-parser",
14 | parserOptions: {
15 | ecmaVersion: 12,
16 | sourceType: "module",
17 | },
18 | extends: ["eslint:recommended", "plugin:vue/vue3-essential", "prettier"],
19 | overrides: [
20 | {
21 | env: {
22 | node: true,
23 | },
24 | files: [".eslintrc.{js,cjs}"],
25 | parserOptions: {
26 | sourceType: "script",
27 | },
28 | },
29 | ],
30 | plugins: ["vue"],
31 | rules: {
32 | "no-console": "off",
33 | "no-debugger": "warn",
34 | "vue/no-unused-vars": "warn",
35 | "vue/multi-word-component-names": "off",
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/blog-v3/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .DS_Store
--------------------------------------------------------------------------------
/blog-v3/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 | shell-emulator=true
4 | # https://registry.npmmirror.com/ 淘宝
5 | # https://registry.npmjs.org/ 官方
6 | registry=https://registry.npmmirror.com/
7 |
8 |
9 |
--------------------------------------------------------------------------------
/blog-v3/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | .local
3 | .output.js
4 | /node_modules/**
5 |
6 | **/*.svg
7 | **/*.sh
8 |
9 | /public/*
--------------------------------------------------------------------------------
/blog-v3/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // 一行的字符数,如果超过会进行换行,默认为 80
3 | printWidth: 100,
4 | // 一个缩进使用几个空格数
5 | tabWidth: 2,
6 | // 是否使用 tab 进行缩进,默认为 false,表示用空格进行缩减
7 | useTabs: false,
8 | // 是否在句尾使用分号
9 | semi: true,
10 | // 字符串是否强制使用单引号,默认为 false,使用双引号
11 | singleQuote: false,
12 | // 是否使用尾逗号,有三个可选值""
13 | trailingComma: "es5",
14 | // 对象大括号直接是否有空格,默认为 true,效果:{ foo: bar }
15 | bracketSpacing: true,
16 | // 处理换行符 lf,crlf,cr,auto
17 | endOfLine: "auto",
18 | // Object 对象中的引号处理方式
19 | quoteProps: "consistent",
20 | };
21 |
--------------------------------------------------------------------------------
/blog-v3/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/blog-v3/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Mr Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/blog-v3/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by 'unplugin-auto-import'
2 | export {}
3 | declare global {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/blog-v3/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "jsx": "preserve",
8 | "paths": {
9 | "@/*": ["src/*"]
10 | },
11 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/blog-v3/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/blog-v3/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/public/favicon.ico
--------------------------------------------------------------------------------
/blog-v3/server.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 |
3 | const app = express();
4 |
5 | const port = 8080; // 自定义端口号(不要与已存在端口冲突)
6 |
7 | app.use(express.static("dist")); // dist 是项目的打包资源路径
8 |
9 | app.listen(port, () => console.log(`服务器 ${port} 开启成功!`));
10 |
--------------------------------------------------------------------------------
/blog-v3/src/api/category.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取所有的category */
4 | export const getAllCategory = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/api/category/getCategoryDictionary", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/blog-v3/src/api/chat.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取聊天信息 */
4 | export const getChatList = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/chat/getChatList", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 清空聊天记录 */
13 | export const clearChat = (data) => {
14 | return new Promise((resolve, reject) => {
15 | http.post("/api/chat/delete", data).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 撤回单条聊天记录 */
22 | export const deleteOneChat = (id) => {
23 | return new Promise((resolve, reject) => {
24 | http.delete("/api/chat/deleteOne/" + id, {}).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/blog-v3/src/api/comment.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 发表评论 */
4 | export const addComment = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/comment/add", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 回复评论 */
13 | export const applyComment = (data) => {
14 | return new Promise((resolve, reject) => {
15 | http.post("/api/comment/apply", data).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 删除自己的评论 */
22 | export const deleteComment = (id, parent_id = 0) => {
23 | return new Promise((resolve, reject) => {
24 | http.delete(`/api/comment/delete/${id}/${parent_id}`, {}).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
30 | /** 获取父级评论 */
31 | export const frontGetParentComment = (data) => {
32 | return new Promise((resolve, reject) => {
33 | http.post("/api/comment/frontGetParentComment", data).then((res) => {
34 | resolve(res);
35 | });
36 | });
37 | };
38 |
39 | /** 获取子级评论 */
40 | export const frontGetChildrenComment = (data) => {
41 | return new Promise((resolve, reject) => {
42 | http.post("/api/comment/frontGetChildrenComment", data).then((res) => {
43 | resolve(res);
44 | });
45 | });
46 | };
47 |
48 | // 获取评论总条数
49 | export const frontGetCommentTotal = (data) => {
50 | return new Promise((resolve, reject) => {
51 | http.post("/api/comment/getCommentTotal", data).then((res) => {
52 | resolve(res);
53 | });
54 | });
55 | };
56 |
--------------------------------------------------------------------------------
/blog-v3/src/api/config.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 首页获取网站config */
4 | export const homeGetConfig = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/api/config", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 增加网站访问量 */
13 | export const addView = () => {
14 | return new Promise((resolve, reject) => {
15 | http.put("/api/config/addView", {}).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 获取所有的背景图片 */
22 | export const getAllPageHeader = () => {
23 | return new Promise((resolve, reject) => {
24 | http.get("/api/pageHeader/getAll", {}).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/blog-v3/src/api/home.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取首页数据统计 */
4 | export const homeGetStatistic = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/api/statistic", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 获取ColaKey */
13 | export const getKey = () => {
14 | return new Promise((resolve, reject) => {
15 | http
16 | .post("https://luckycola.com.cn/ai/getColaKey", {
17 | uid: "xIlyH01695893781527LY3zhJGJ0n",
18 | appKey: "651549153295914ff09985c1",
19 | })
20 | .then((res) => {
21 | resolve(res);
22 | });
23 | });
24 | };
25 |
26 | /** 一言api 认证ColaKey成功后获取句子 */
27 | export const getSentence = (ColaKey) => {
28 | return new Promise((resolve, reject) => {
29 | http
30 | .post("https://luckycola.com.cn/tools/yiyan", {
31 | ColaKey,
32 | })
33 | .then((res) => {
34 | resolve(res);
35 | });
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/blog-v3/src/api/like.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 点赞 */
4 | export const addLike = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/like/addLike", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 判断当前用户是否点赞了 */
13 | export const getIsLikeByIdOrIpAndType = (data) => {
14 | return new Promise((resolve, reject) => {
15 | http.post("/api/like/getIsLikeByIdOrIpAndType", data).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 取消点赞 */
22 | export const cancelLike = (data) => {
23 | return new Promise((resolve, reject) => {
24 | http.post("/api/like/cancelLike", data).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/blog-v3/src/api/links.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 分页获取友链 */
4 | export const getFriendLinks = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/links/getLinksList", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 新增友链 */
13 | export const addFriendLinks = (data) => {
14 | return new Promise((resolve, reject) => {
15 | http.post("/api/links/add", data).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 申请修改友链 */
22 | export const updateFriendLinks = (data) => {
23 | return new Promise((resolve, reject) => {
24 | http.post("/api/links/frontUpdate", data).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/blog-v3/src/api/message.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取留言列表 */
4 | export const getMessageList = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/message/getMessageList", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 获取所有留言 */
13 | export const getAllMessage = () => {
14 | return new Promise((resolve, reject) => {
15 | http.get("/api/message/getAllMessage").then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 新增留言 */
22 | export const addMessage = (data) => {
23 | return new Promise((resolve, reject) => {
24 | http.post("/api/message/add", data).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
30 | /** 编辑留言 */
31 | export const updateMessage = (data) => {
32 | return new Promise((resolve, reject) => {
33 | http.post("/api/message/update", data).then((res) => {
34 | resolve(res);
35 | });
36 | });
37 | };
38 |
39 | /** 删除留言 */
40 | export const deleteMessage = (id) => {
41 | return new Promise((resolve, reject) => {
42 | http.put("/api/message/delete", { idList: [id] }).then((res) => {
43 | resolve(res);
44 | });
45 | });
46 | };
47 |
48 | /** 获取热门标签 */
49 | export const getMessageTag = () => {
50 | return new Promise((resolve, reject) => {
51 | http.get("/api/message/getHotTagList", {}).then((res) => {
52 | resolve(res);
53 | });
54 | });
55 | };
56 |
--------------------------------------------------------------------------------
/blog-v3/src/api/music.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取榜单 */
4 | export const reqToplist = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/wapi/toplist/detail", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 获取榜单歌曲列表 */
13 | export const reqTopDetaliList = ({ id, limit, offset }) => {
14 | return new Promise((resolve, reject) => {
15 | http
16 | .get(`/wapi/playlist/track/all?id=${id}&limit=${limit}&offset=${offset}`, {})
17 | .then((res) => {
18 | resolve(res);
19 | });
20 | });
21 | };
22 |
23 | /** 获取歌曲详情 主要是播放地址 */
24 | export const reqMusicDetail = ({ id, level }) => {
25 | return new Promise((resolve, reject) => {
26 | http.get(`/wapi/song/url/v1?id=${id}&level=${level}`, {}).then((res) => {
27 | resolve(res);
28 | });
29 | });
30 | };
31 |
32 | // 获取音乐的描述
33 | export const reqMusicDescription = (id) => {
34 | return new Promise((resolve, reject) => {
35 | http.get(`/wapi//song/detail?ids=${id}`, {}).then((res) => {
36 | resolve(res);
37 | });
38 | });
39 | };
40 |
41 | // 搜索
42 | export const reqSearch = (keyWords, offset, limit) => {
43 | return new Promise((resolve, reject) => {
44 | http
45 | .get(`/wapi/search?keywords=${keyWords}&offset=${offset}&limit=${limit}`, {})
46 | .then((res) => {
47 | resolve(res);
48 | });
49 | });
50 | };
51 |
52 | // 根据歌曲id获取歌词
53 | export const reqMusicLyricById = (id) => {
54 | return new Promise((resolve, reject) => {
55 | http.get(`/wapi/lyric?id=${id}`, {}).then((res) => {
56 | resolve(res);
57 | });
58 | });
59 | };
60 |
--------------------------------------------------------------------------------
/blog-v3/src/api/notify.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 分页获取消息列表 */
4 | export const getNotifylist = (data) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/notify/getNotifyList", data).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 阅读消息列表 */
13 | export const updateNotify = (id) => {
14 | return new Promise((resolve, reject) => {
15 | http.put("/api/notify/update/" + id, {}).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
21 | /** 删除消息列表 */
22 | export const deleteNotify = (id) => {
23 | return new Promise((resolve, reject) => {
24 | http.put("/api/notify/delete/" + id, {}).then((res) => {
25 | resolve(res);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/blog-v3/src/api/photo.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取所有的相册 */
4 | export const getAllAlbum = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/api/photoAlbum/getAllAlbumList", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
12 | /** 获取相册内的图片 */
13 | export const getAllPhotosByAlbumId = (id) => {
14 | return new Promise((resolve, reject) => {
15 | http.get(`/api/photo/getAllPhotosByAlbumId/${id}`, {}).then((res) => {
16 | resolve(res);
17 | });
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/blog-v3/src/api/tag.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取所有的tag */
4 | export const getAllTag = () => {
5 | return new Promise((resolve, reject) => {
6 | http.get("/api/tag/getTagDictionary", {}).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/blog-v3/src/api/talk.js:
--------------------------------------------------------------------------------
1 | import http from "@/config/request";
2 |
3 | /** 获取说说列表 */
4 | export const getTalkList = (param) => {
5 | return new Promise((resolve, reject) => {
6 | http.post("/api/talk/blogGetTalkList", param).then((res) => {
7 | resolve(res);
8 | });
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/bofang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/bofang.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/bofangliebiao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/bofangliebiao.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/danquxunhuan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/danquxunhuan.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/fenlei.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/fenlei.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/fuzhi1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/fuzhi1.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/link.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/lrc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/lrc.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/qiansemoshi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/qiansemoshi.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/quanbuxunhuan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/quanbuxunhuan.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/shangyishou.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/shangyishou.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/shensemoshi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/shensemoshi.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/shuaxin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/shuaxin.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/shunxubofang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/shunxubofang.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/suiji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/suiji.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/suijibofang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/suijibofang.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/tupian.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/tupian.jpeg
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/xiangyou.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/xiangyou.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/xiangzuo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/xiangzuo.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/xiayishou.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/xiayishou.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/context-menu/zanting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/context-menu/zanting.png
--------------------------------------------------------------------------------
/blog-v3/src/assets/css/iconFont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/css/iconFont/iconfont.ttf
--------------------------------------------------------------------------------
/blog-v3/src/assets/css/iconFont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/css/iconFont/iconfont.woff
--------------------------------------------------------------------------------
/blog-v3/src/assets/css/iconFont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/css/iconFont/iconfont.woff2
--------------------------------------------------------------------------------
/blog-v3/src/assets/img/blogAvatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/assets/img/blogAvatar.png
--------------------------------------------------------------------------------
/blog-v3/src/components/Comment/tool.js:
--------------------------------------------------------------------------------
1 | export function keepLastIndex(dom) {
2 | var range;
3 | if (window.getSelection) {
4 | //ie11 10 9 ff safari
5 | dom.focus(); //解决ff不获取焦点无法定位问题
6 | range = window.getSelection(); //创建range
7 | range.selectAllChildren(dom); //range 选择obj下所有子内容
8 | range.collapseToEnd(); //光标移至最后
9 | } else if (document.selection) {
10 | range = document.selection.createRange(); //创建选择对象
11 | range.moveToElementText(dom); //range定位到obj
12 | range.collapse(false); //光标移至最后
13 | range.select();
14 | }
15 | }
16 |
17 | export function getCurrentIndex() {
18 | var range;
19 | if (window.getSelection) {
20 | //ie11 10 9 ff safari
21 | range = window.getSelection(); //创建range
22 | return range.focusOffset;
23 | } else if (document.selection) {
24 | range = document.selection.createRange(); //创建选择对象
25 | return range.focusOffset;
26 | }
27 | }
28 |
29 | // 获取当前type类型数字的公共方法
30 | export function getCurrentType(type) {
31 | let res = 0;
32 | switch (type) {
33 | case "article":
34 | res = 1;
35 | break;
36 | case "talk":
37 | res = 2;
38 | break;
39 | case "message":
40 | res = 3;
41 | break;
42 | case "comment":
43 | res = 4;
44 | }
45 |
46 | return res;
47 | }
48 |
--------------------------------------------------------------------------------
/blog-v3/src/components/GsapCount/index.vue:
--------------------------------------------------------------------------------
1 |
51 |
52 |
53 |
54 | {{ d.num.toFixed(0) }}
55 |
56 |
57 |
--------------------------------------------------------------------------------
/blog-v3/src/components/HomeArticle/components/article-skeleton.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
55 |
--------------------------------------------------------------------------------
/blog-v3/src/components/Layout/index.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/blog-v3/src/components/Layout/main/blog-main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/blog-v3/src/components/Music/controls/components/blogAvatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/blog-v3/src/components/Music/controls/components/blogAvatar.png
--------------------------------------------------------------------------------
/blog-v3/src/components/Music/list/components/special-title.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
27 | {{ title }}
28 |
29 |
30 |
31 |
41 |
--------------------------------------------------------------------------------
/blog-v3/src/components/RightSide/components/item/right-side-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ props.title }}
5 |
6 |
7 |
8 |
28 |
40 |
--------------------------------------------------------------------------------
/blog-v3/src/components/RightSide/components/skeleton/mobile-top-skeleton.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/blog-v3/src/components/RightSide/components/skeleton/right-side-skeleton-item.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/blog-v3/src/components/RightSide/components/skeleton/right-side-top-skeleton.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/blog-v3/src/components/SkeletonItem/skeleton-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/blog-v3/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
39 |
40 |
47 |
48 |
49 |
56 |
--------------------------------------------------------------------------------
/blog-v3/src/components/SwitchTheme/index.vue:
--------------------------------------------------------------------------------
1 |
43 |
44 |
45 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/blog-v3/src/config/config.js:
--------------------------------------------------------------------------------
1 | export const config = {
2 | ENCRYPTION: true,
3 | };
4 |
--------------------------------------------------------------------------------
/blog-v3/src/directives/copy.js:
--------------------------------------------------------------------------------
1 | import { ElMessage } from "element-plus";
2 |
3 | const vCopy = {
4 | beforeMount(el, binding) {
5 | el.targetContent = binding.value;
6 | el.addEventListener("click", () => {
7 | if (!el.targetContent) return console.warn("没有需要复制的目标内容");
8 | // 创建textarea标签
9 | const textarea = document.createElement("textarea");
10 | // 设置相关属性
11 | textarea.readOnly = "readonly";
12 | textarea.style.position = "fixed";
13 | textarea.style.top = "-99999px";
14 | // 把目标内容赋值给它的value属性
15 | textarea.value = el.targetContent;
16 | // 插入到页面
17 | document.body.appendChild(textarea);
18 | // 调用onselect()方法
19 | textarea.select();
20 | // 把目标内容复制进剪贴板, 该API会返回一个Boolean
21 | const res = document.execCommand("Copy");
22 | res && ElMessage.info("复制成功,剪贴板内容:" + el.targetContent);
23 | // 移除textarea标签
24 | document.body.removeChild(textarea);
25 | });
26 | },
27 | updated(el, binding) {
28 | // 实时更新最新的目标内容
29 | el.targetContent = binding.value;
30 | },
31 | unmounted(el) {
32 | el.removeEventListener("click", () => {});
33 | },
34 | };
35 |
36 | export default vCopy;
37 |
--------------------------------------------------------------------------------
/blog-v3/src/directives/imageLoading.js:
--------------------------------------------------------------------------------
1 | const image = {
2 | beforeMount(el, binding) {
3 | if (!binding.value) return;
4 | var img = new Image();
5 | img.src = binding.value;
6 | // 创建一个loading的img标签
7 | let cup = document.createElement("div");
8 | cup.classList.add("coffee_cup");
9 | el.appendChild(cup); // 插入dom
10 |
11 | img.onload = () => {
12 | el.removeChild(cup); // 加载成功移除loading
13 | };
14 |
15 | img.onerror = () => {
16 | el.removeChild(cup); // 加载失败移除loading 展示el自定义的错误
17 | };
18 | },
19 | };
20 | export default image;
21 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/comment.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/darkDisc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/delete.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/lightDisc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/lightRocket.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/icons/svg/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blog-v3/src/main.js:
--------------------------------------------------------------------------------
1 | import App from "./App.vue";
2 | import { createApp } from "vue";
3 | import router from "./router";
4 | import { createPinia } from "pinia"; //引入pinia
5 | import "./assets/css/iconFont/iconfont.css";
6 | import "element-plus/dist/index.css"; // 引入样式
7 | import "element-plus/theme-chalk/dark/css-vars.css";
8 | import piniaPluginPersist from "pinia-plugin-persist";
9 | // animate
10 | import "animate.css";
11 | // tailwind.css https://www.tailwindcss.cn/docs
12 | import "./styles/tailwind.css";
13 | // svg
14 | import "virtual:svg-icons-register";
15 | // 指令
16 | import vCopy from "./directives/copy";
17 | import image from "./directives/imageLoading";
18 |
19 | const app = createApp(App);
20 | app.directive("copy", vCopy);
21 | app.directive("image", image);
22 | app.use(router).use(createPinia().use(piniaPluginPersist)).mount("#app");
23 |
--------------------------------------------------------------------------------
/blog-v3/src/styles/markdown.scss:
--------------------------------------------------------------------------------
1 | // 对于markdown样式的调整
2 | .md-preview-v3 {
3 | background: rgba(255, 255, 255, 0.3);
4 | }
5 |
6 | .code-block::scrollbar-thumb {
7 | background-color: #efbcda !important;
8 | }
9 | .code-block::-webkit-scrollbar-thumb {
10 | background-color: #efbcda !important;
11 | }
12 |
--------------------------------------------------------------------------------
/blog-v3/src/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/blog-v3/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | // 进入前
2 | .component-enter-from,
3 | .component-leave-to {
4 | opacity: 0;
5 | }
6 | .component-enter-to,
7 | .component-leave-from {
8 | opacity: 1;
9 | }
10 |
11 | // 设置进入和离开过程中的动画时长0.5s
12 | .component-enter-active,
13 | .component-leave-active {
14 | transition: all 0.6s;
15 | }
16 |
17 | // 进入前
18 | .main-enter-from,
19 | .main-leave-to {
20 | transform: translateX(-10px);
21 | }
22 | .main-enter-to,
23 | .main-leave-from {
24 | transform: translateX(0);
25 | }
26 |
27 | // 设置进入和离开过程中的动画时长0.5s
28 | .main-enter-active,
29 | .main-leave-active {
30 | transition: all 0.6s;
31 | }
32 |
--------------------------------------------------------------------------------
/blog-v3/src/utils/encipher.js:
--------------------------------------------------------------------------------
1 | import CryptoJS from "crypto-js";
2 |
3 | // 十六位十六进制数作为密钥
4 | const SECRET_KEY = CryptoJS.enc.Utf8.parse("1234567812345678");
5 | // 十六位十六进制数作为密钥偏移量
6 | const SECRET_IV = CryptoJS.enc.Utf8.parse("1234567812345678");
7 |
8 | /**
9 | * 加密方法
10 | * @param data
11 | * @returns {string}
12 | */
13 | export function _encrypt(data) {
14 | if (typeof data === "object") {
15 | try {
16 | // eslint-disable-next-line no-param-reassign
17 | data = JSON.stringify(data);
18 | } catch (error) {
19 | console.log("encrypt error:", error);
20 | }
21 | }
22 | const dataHex = CryptoJS.enc.Utf8.parse(data);
23 | const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
24 | iv: SECRET_IV,
25 | mode: CryptoJS.mode.CBC,
26 | padding: CryptoJS.pad.Pkcs7,
27 | });
28 | return encrypted.ciphertext.toString();
29 | }
30 |
31 | /**
32 | * 解密方法
33 | * @param data
34 | * @returns {string}
35 | */
36 | export function _decrypt(data) {
37 | const encryptedHexStr = CryptoJS.enc.Hex.parse(data);
38 | const str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
39 | const decrypt = CryptoJS.AES.decrypt(str, SECRET_KEY, {
40 | iv: SECRET_IV,
41 | mode: CryptoJS.mode.CBC,
42 | padding: CryptoJS.pad.Pkcs7,
43 | });
44 | const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
45 | return decryptedStr.toString();
46 | }
47 |
--------------------------------------------------------------------------------
/blog-v3/src/utils/enum.js:
--------------------------------------------------------------------------------
1 | export const MODELLIST = [
2 | "RANDOM", // 随机
3 | "LISTLOOP", // 列表循环
4 | "SINGLECYCLE", // 单曲循环
5 | ];
6 |
7 | export const PLAYTYPE = {
8 | CUSTOM: "CUSTOM", // 播放用户添加的歌曲
9 | TOP: "TOP", // 当前歌曲排行榜列表歌曲
10 | };
11 |
12 | export const LYRICTYPE = {
13 | COMMON: "COMMON",
14 | SPECIAL: "SPECIAL",
15 | };
16 |
--------------------------------------------------------------------------------
/blog-v3/src/views/404/index.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 | 返回上一页
29 | 返回首页
32 |
33 |
{{ 5 - time }} 秒后自动返回首页......
34 |
35 |
36 |
37 |
38 |
39 |
48 |
--------------------------------------------------------------------------------
/blog-v3/src/views/archives/archives.vue:
--------------------------------------------------------------------------------
1 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
69 |
--------------------------------------------------------------------------------
/blog-v3/src/views/message/useMessage.js:
--------------------------------------------------------------------------------
1 | // 字体大小列表
2 | export const fontSizeList = [
3 | {
4 | key: 12,
5 | },
6 | {
7 | key: 14,
8 | },
9 | {
10 | key: 16,
11 | },
12 | {
13 | key: 18,
14 | },
15 | {
16 | key: 20,
17 | },
18 | {
19 | key: 22,
20 | },
21 | {
22 | key: 24,
23 | },
24 | {
25 | key: 28,
26 | },
27 | {
28 | key: 32,
29 | },
30 | {
31 | key: 36,
32 | },
33 | ];
34 | // 字体宽度
35 | export const fontWeightList = [
36 | {
37 | key: 400,
38 | },
39 | {
40 | key: 500,
41 | },
42 | {
43 | key: 600,
44 | },
45 | {
46 | key: 700,
47 | },
48 | {
49 | key: 800,
50 | },
51 | {
52 | key: 900,
53 | },
54 | ];
55 | // 背景色列表
56 | export const predefineColors = [
57 | "#ff4500",
58 | "#ff8c00",
59 | "#ffd700",
60 | "#90ee90",
61 | "#00ced1",
62 | "#1e90ff",
63 | "#c71585",
64 | "rgba(255, 69, 0, 0.68)",
65 | "rgb(255, 120, 0)",
66 | "hsv(51, 100, 98)",
67 | "hsva(120, 40, 94, 0.5)",
68 | "hsl(181, 100%, 37%)",
69 | "hsla(209, 100%, 56%, 0.73)",
70 | "#c7158577",
71 | ];
72 | // 选项
73 | export const opTabList = [
74 | {
75 | key: 0,
76 | label: "背景颜色",
77 | },
78 | {
79 | key: 1,
80 | label: "字体",
81 | },
82 | {
83 | key: 2,
84 | label: "背景图片",
85 | },
86 | {
87 | key: 3,
88 | label: "标签",
89 | },
90 | ];
91 | // 渐变色列表
92 | export const gradientList = [];
93 |
--------------------------------------------------------------------------------
/blog-v3/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
4 | theme: {
5 | extend: {},
6 | colors: {
7 | "primary-blue": "#5cbfef",
8 | },
9 | },
10 | corePlugins: {
11 | preflight: false,
12 | },
13 | plugins: [],
14 | };
15 |
--------------------------------------------------------------------------------
/zhifupay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzym99/vue3-blog/42c6177d254d2760180d08b1d560d083642291ea/zhifupay.png
--------------------------------------------------------------------------------