first commit
7
.env.development
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
ENV = 'development'
|
||||||
|
VITE_APP_ENV = 'development'
|
||||||
|
|
||||||
|
#VITE_APP_BASE_URL = 'http://127.0.0.1:7777'
|
||||||
|
VITE_APP_BASE_URL = 'https://www.hxyouzi.com'
|
||||||
|
|
5
.env.production
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 生产环境配置
|
||||||
|
ENV = 'production'
|
||||||
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
|
VITE_APP_BASE_URL = 'https://www.hxyouzi.com'
|
46
.gitea/workflows/youzi.yml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
RUNNER_TOOL_CACHE: /toolcache
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.cn/actions/checkout@v3
|
||||||
|
- id: tool-cache
|
||||||
|
name: 安装node
|
||||||
|
uses: https://gitea.com/kongxiangyiren/gitea-tool-cache@v4
|
||||||
|
with:
|
||||||
|
# 只有node支持版本号别名
|
||||||
|
node-version: 18
|
||||||
|
- uses: https://gitea.cn/actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
# gitea-tool-cache导出 node 具体版本
|
||||||
|
node-version: ${{ steps.tool-cache.outputs.node-version }}
|
||||||
|
registry-url: 'https://registry.npmmirror.com'
|
||||||
|
|
||||||
|
- name: 缓存
|
||||||
|
uses: https://gitea.cn/actions/cache@v3
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-4
|
||||||
|
|
||||||
|
- name: Install and Build # 下载依赖 打包项目
|
||||||
|
run: |
|
||||||
|
yarn install
|
||||||
|
yarn build
|
||||||
|
- name: Appleboy
|
||||||
|
uses: https://gitcode.net/weixin_44697517/scp-action@v0.1.4
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.USER_HOST }} # 服务器地址: xxx.xxx.xxx.xxx
|
||||||
|
username: ${{ secrets.USER_NAME }} # 服务器名字 一般是root
|
||||||
|
key: ${{ secrets.SERVER_SSH_KEY }} # 服务器连接密钥
|
||||||
|
source: './dist/' # 拷贝文件目录
|
||||||
|
target: ${{ secrets.USER_TARGET }} # 服务器目标目录
|
||||||
|
strip_components: 2
|
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||||
|
}
|
14
README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# 个人练手
|
||||||
|
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
### √ 1.登录注册功能
|
||||||
|
### √ 2.首页展示共享文章列表
|
||||||
|
### √ 3.画廊页面展示共享图片
|
||||||
|
|
||||||
|
|
||||||
|
### × 4.控制台功能 --目前已完成:画廊上传共享
|
||||||
|
### √ 5.文章编辑功能
|
||||||
|
|
||||||
|
### 其余功能正在考虑中
|
71
index.html
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>黑心柚子</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
<!-- 网页鼠标点击特效(爱心) -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
! function (e, t, a) {
|
||||||
|
function r() {
|
||||||
|
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
|
||||||
|
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
|
||||||
|
"px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e]
|
||||||
|
.scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
|
||||||
|
requestAnimationFrame(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
function n() {
|
||||||
|
var t = "function" == typeof e.onclick && e.onclick;
|
||||||
|
e.onclick = function (e) {
|
||||||
|
t && t(), o(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function o(e) {
|
||||||
|
var a = t.createElement("div");
|
||||||
|
a.className = "heart", s.push({
|
||||||
|
el: a,
|
||||||
|
x: e.clientX - 5,
|
||||||
|
y: e.clientY - 5,
|
||||||
|
scale: 1,
|
||||||
|
alpha: 1,
|
||||||
|
color: c()
|
||||||
|
}), t.body.appendChild(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
function i(e) {
|
||||||
|
var a = t.createElement("style");
|
||||||
|
a.type = "text/css";
|
||||||
|
try {
|
||||||
|
a.appendChild(t.createTextNode(e))
|
||||||
|
} catch (t) {
|
||||||
|
a.styleSheet.cssText = e
|
||||||
|
}
|
||||||
|
t.getElementsByTagName("head")[0].appendChild(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
function c() {
|
||||||
|
return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math
|
||||||
|
.random()) + ")"
|
||||||
|
}
|
||||||
|
var s = [];
|
||||||
|
e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e
|
||||||
|
.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
|
||||||
|
setTimeout(e, 1e3 / 60)
|
||||||
|
}, i(
|
||||||
|
".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"
|
||||||
|
), n(), r()
|
||||||
|
}(window, document);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
11
niva.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "blog",
|
||||||
|
"uuid": "3056dae6-4b74-4e53-b77a-d8fe1f09c04d",
|
||||||
|
"debug": {
|
||||||
|
"entry": "http://localhost:5173",
|
||||||
|
"resource": "public"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"resource": "dist"
|
||||||
|
}
|
||||||
|
}
|
45
package.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "blog",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview --port 4173"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vant/auto-import-resolver": "^1.1.0",
|
||||||
|
"@vant/touch-emulator": "^1.4.0",
|
||||||
|
"@vicons/ionicons5": "^0.12.0",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"aplayer": "^1.10.1",
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"chinese-workday": "^1.10.0",
|
||||||
|
"colorofchina": "^1.0.6",
|
||||||
|
"gsap": "^3.12.2",
|
||||||
|
"less": "^4.1.3",
|
||||||
|
"naive-ui": "^2.38.1",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"pinia": "^2.0.17",
|
||||||
|
"qs": "^6.11.2",
|
||||||
|
"qweather-icons": "^1.3.3",
|
||||||
|
"rapidoc": "^9.3.4",
|
||||||
|
"unplugin-auto-import": "^0.15.2",
|
||||||
|
"unplugin-vue-components": "^0.24.1",
|
||||||
|
"vant": "^4.8.7",
|
||||||
|
"vfonts": "^0.0.3",
|
||||||
|
"vite-svg-loader": "^4.0.0",
|
||||||
|
"vue": "^3.2.37",
|
||||||
|
"vue-cookies": "^1.8.3",
|
||||||
|
"vue-router": "^4.1.3",
|
||||||
|
"vue-wechat-title": "^2.0.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vicons/ionicons4": "^0.12.0",
|
||||||
|
"@vitejs/plugin-vue": "^3.0.1",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
|
"postcss": "^8.4.22",
|
||||||
|
"tailwindcss": "^3.3.1",
|
||||||
|
"vite": "^3.0.4"
|
||||||
|
}
|
||||||
|
}
|
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
BIN
public/bg/bg-d.webp
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
public/bg/bg-l.webp
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 3.2 KiB |
20
src/App.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import themeOverrides from "@/util/theme.js"; // 主题色等配置
|
||||||
|
import { dateZhCN, zhCN } from "naive-ui";
|
||||||
|
import idx from "./index.vue"; // 具体配置,以及全局组件挂载
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-config-provider :locale="zhCN" :date-locale="dateZhCN" :theme="null" class="config" :theme-overrides="themeOverrides.lightOverrides">
|
||||||
|
<n-message-provider>
|
||||||
|
<n-dialog-provider>
|
||||||
|
<n-notification-provider>
|
||||||
|
<idx />
|
||||||
|
</n-notification-provider>
|
||||||
|
</n-dialog-provider>
|
||||||
|
</n-message-provider>
|
||||||
|
</n-config-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
26
src/api/addr/index.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import request from "@/util/request";
|
||||||
|
|
||||||
|
//getAddressByIP
|
||||||
|
export function getAddressByIP(params) {
|
||||||
|
return request({
|
||||||
|
url: "/addr/ip",
|
||||||
|
method: "get",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//SendMail
|
||||||
|
export function sendMail(data) {
|
||||||
|
return request({
|
||||||
|
url: "/addr/mail",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//SendMailinpass
|
||||||
|
export function sendMailinpass(data) {
|
||||||
|
return request({
|
||||||
|
url: "/addr/mailinpass",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
49
src/api/art/index.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import request from "@/util/request";
|
||||||
|
|
||||||
|
//queryArt
|
||||||
|
export function queryArt() {
|
||||||
|
return request({
|
||||||
|
url: "/art/q",
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//addArt
|
||||||
|
export function addArt(data) {
|
||||||
|
return request({
|
||||||
|
url: "/art/add",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// queryArtInfo
|
||||||
|
export function queryArtInfo(id) {
|
||||||
|
return request({
|
||||||
|
url: "/art/getInfoById?id=" + id,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// myArti
|
||||||
|
export function myArti() {
|
||||||
|
return request({
|
||||||
|
url: "/art/myArticle",
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// deleteArti
|
||||||
|
export function deleteArti(id) {
|
||||||
|
return request({
|
||||||
|
url: "/art/article?id=" + id,
|
||||||
|
method: "delete",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// updArt
|
||||||
|
export function updArt(data) {
|
||||||
|
return request({
|
||||||
|
url: "/art/article",
|
||||||
|
method: "put",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
48
src/api/file/index.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import request from "@/util/request";
|
||||||
|
|
||||||
|
//MyFile
|
||||||
|
export function MyFile() {
|
||||||
|
return request({
|
||||||
|
url: "/file/myfile",
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//ShareFile
|
||||||
|
export function ShareFile(params) {
|
||||||
|
return request({
|
||||||
|
url: "/f/sharefile",
|
||||||
|
method: "get",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function updateFile(id) {
|
||||||
|
return request({
|
||||||
|
url: "/file/" + id,
|
||||||
|
method: "put",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function unUpdateFile(id) {
|
||||||
|
return request({
|
||||||
|
url: "/file/un/" + id,
|
||||||
|
method: "put",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function delFile(id) {
|
||||||
|
return request({
|
||||||
|
url: "/file/" + id,
|
||||||
|
method: "delete",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function updateName(data) {
|
||||||
|
return request({
|
||||||
|
url: "/file/updatename",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function listMusic() {
|
||||||
|
return request({
|
||||||
|
url: "/f/music",
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
14
src/api/index.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const files = import.meta.glob('./*/index.js', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const http = {}
|
||||||
|
// console.log(files);
|
||||||
|
for (const i in files) {
|
||||||
|
const t = i.split("/")
|
||||||
|
const name = t[1]
|
||||||
|
http[name] = files[i]
|
||||||
|
}
|
||||||
|
export default http
|
||||||
|
|
||||||
|
|
9
src/api/mix/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import request from '@/util/request';
|
||||||
|
|
||||||
|
//poetry
|
||||||
|
export function poetry() {
|
||||||
|
return request({
|
||||||
|
url: '/mix/poetry',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
9
src/api/nav/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import request from '@/util/request';
|
||||||
|
|
||||||
|
// listNav
|
||||||
|
export function listNav() {
|
||||||
|
return request({
|
||||||
|
url: '/nav/menu',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
9
src/api/news/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import request from '@/util/request';
|
||||||
|
|
||||||
|
//listBaiduNews
|
||||||
|
export function listBaiduNews() {
|
||||||
|
return request({
|
||||||
|
url: '/news/baidu',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
24
src/api/plink/index.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import request from '@/util/request';
|
||||||
|
|
||||||
|
//listPlink
|
||||||
|
export function listPlink() {
|
||||||
|
return request({
|
||||||
|
url: '/plink/all',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagList
|
||||||
|
export function tagList() {
|
||||||
|
return request({
|
||||||
|
url: '/plink/tag',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addPlink(){
|
||||||
|
return request({
|
||||||
|
url: '/plink/add',
|
||||||
|
method: 'post'
|
||||||
|
});
|
||||||
|
}
|
50
src/api/user/index.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import request from "@/util/request";
|
||||||
|
|
||||||
|
//login
|
||||||
|
export function login(data) {
|
||||||
|
return request({
|
||||||
|
url: "/user/login",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//register
|
||||||
|
export function regis(data) {
|
||||||
|
return request({
|
||||||
|
url: "/user/register",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//autologin
|
||||||
|
export function alogin() {
|
||||||
|
return request({
|
||||||
|
url: "/info/autologin",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//rexpass
|
||||||
|
export function rexpass(data) {
|
||||||
|
return request({
|
||||||
|
url: "/user/respass",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//updatepass
|
||||||
|
export function updatepass(data) {
|
||||||
|
return request({
|
||||||
|
url: "/user/updatepass",
|
||||||
|
method: "put",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//updatetel
|
||||||
|
export function updatetel(data) {
|
||||||
|
return request({
|
||||||
|
url: "/info/update/tel",
|
||||||
|
method: "put",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
32
src/api/wea/index.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import request from '@/util/request';
|
||||||
|
|
||||||
|
//getWeather
|
||||||
|
export function getWeather(params) {
|
||||||
|
return request({
|
||||||
|
url: '/wea/now',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//getJq
|
||||||
|
export function getJq() {
|
||||||
|
return request({
|
||||||
|
url: '/wea/getJq',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//getNextHoliday
|
||||||
|
export function getNextHoliday() {
|
||||||
|
return request({
|
||||||
|
url: '/wea/getNextHoliday',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//iswork
|
||||||
|
export function iswork() {
|
||||||
|
return request({
|
||||||
|
url: '/wea/iswork',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
BIN
src/assets/bg.jpg
Normal file
After Width: | Height: | Size: 166 KiB |
BIN
src/assets/bro/baidu.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
src/assets/bro/bing.png
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
src/assets/bro/google.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
src/assets/font/LCDML.woff2
Normal file
BIN
src/assets/offwork.webp
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/onwork.webp
Normal file
After Width: | Height: | Size: 12 KiB |
0
src/assets/style/base.css
Normal file
12
src/assets/style/index.less
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@purple: #8a2be2;
|
||||||
|
@orange: #ffa500;
|
||||||
|
@blue: #409eff;
|
||||||
|
@dp: #f2e8fc;
|
||||||
|
@ps:0 2px 12px 0 #5C169F10;
|
||||||
|
|
||||||
|
//#BE8AEF #B172EC #A45BE9 #9744E5 #8A2BE2 #7B1DD3 #6C19B9 #5C169F
|
||||||
|
|
||||||
|
|
||||||
|
:root:root {
|
||||||
|
--van-primary-color: #8a2be2;
|
||||||
|
}
|
65
src/assets/style/main.css
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* @import "./base.css"; */
|
||||||
|
/* .n-scrollbar-rail.n-scrollbar-rail--vertical {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
.n-scrollbar-rail__scrollbar {
|
||||||
|
background-color: #409eff !important;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.n-button {
|
||||||
|
background-color: var(--n-color) !important;
|
||||||
|
}
|
||||||
|
.n-scrollbar-rail--vertical {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
/* .a1111111111 {
|
||||||
|
cursor: url("@/cursor/help_26x26.png"), help;
|
||||||
|
cursor: url("@/cursor/link_26x26.png"), pointer;
|
||||||
|
cursor: url("@/cursor/working.png"), progress;
|
||||||
|
cursor: url("@/cursor/busy.png"), wait;
|
||||||
|
cursor: url("@/cursor/cross.png"), crosshair;
|
||||||
|
cursor: url("@/cursor/text_26x26.png"), text;
|
||||||
|
cursor: url("@/cursor/move_26x26.png"), move;
|
||||||
|
cursor: url("@/cursor/unavailable.png"), not-allowed;
|
||||||
|
cursor: url("@/cursor/unavailable.png"), no-drop;
|
||||||
|
cursor: url("@/cursor/cross") all-scroll;
|
||||||
|
cursor: url("@/cursor/horiz_26x26.png"), col-resize;
|
||||||
|
cursor: url("@/cursor/horiz_26x26.png"), w-resize;
|
||||||
|
cursor: url("@/cursor/horiz_26x26.png"), e-resize;
|
||||||
|
cursor: url("@/cursor/horiz_26x26.png"), ew-resize;
|
||||||
|
cursor: url("@/cursor/vert.png"), row-resize;
|
||||||
|
cursor: url("@/cursor/vert.png"), n-resize;
|
||||||
|
cursor: url("@/cursor/vert.png"), s-resize;
|
||||||
|
cursor: url("@/cursor/vert.png"), ns-resize;
|
||||||
|
cursor: url("@/cursor/diag1.png"), nw-resize;
|
||||||
|
cursor: url("@/cursor/diag1.png"), se-resize;
|
||||||
|
cursor: url("@/cursor/diag1.png"), nwse-resize;
|
||||||
|
cursor: url("@/cursor/diag2_26x26.png"), ne-resize;
|
||||||
|
cursor: url("@/cursor/diag2_26x26.png"), sw-resize;
|
||||||
|
cursor: url("@/cursor/diag2_26x26.png"), nesw-resize;
|
||||||
|
cursor: url("@/cursor/handwriting_26x26.png"), copy;
|
||||||
|
cursor: url("@/cursor/alt.png"), grab;
|
||||||
|
} */
|
||||||
|
div {
|
||||||
|
cursor: url("@/cursor/normal.png"), default !important;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
button,
|
||||||
|
li,
|
||||||
|
.aplayer-list-author,
|
||||||
|
.aplayer-icon,
|
||||||
|
.n-back-top,
|
||||||
|
.n-tabs-tab,
|
||||||
|
.n-input__eye,
|
||||||
|
.n-image:not(.n-image--preview-disabled) {
|
||||||
|
cursor: url("@/cursor/link_26x26.png"), pointer !important;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
cursor: url("@/cursor/text_26x26.png"), text !important;
|
||||||
|
}
|
||||||
|
#plink .v-binder-follower-content {
|
||||||
|
width: 20vw;
|
||||||
|
}
|
170
src/components/aplayerView.vue
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 准备一个容器用来存放音乐播放器 -->
|
||||||
|
<div id="aplayer"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// https://aplayer.js.org/#/zh-Hans/
|
||||||
|
// import Hls from 'hls.js/dist/hls.min.js';
|
||||||
|
import APlayer from "aplayer"; // 引入音乐插件
|
||||||
|
import "aplayer/dist/APlayer.min.css"; // 引入音乐插件的样式
|
||||||
|
import { onMounted, reactive } from "vue";
|
||||||
|
|
||||||
|
// window.Hls = Hls;
|
||||||
|
const data = reactive({
|
||||||
|
audio: [],
|
||||||
|
info: {
|
||||||
|
fixed: true, // 开启吸底模式
|
||||||
|
// mini: false, // 开启迷你模式
|
||||||
|
// autoplay: true, // 自动播放(已经没用了)
|
||||||
|
// theme: '#000', // 主题色
|
||||||
|
loop: "all", // 音频循环播放, 可选值: 'all', 'one', 'none'
|
||||||
|
order: "list", // 音频循环顺序, 可选值: 'list', 'random'
|
||||||
|
preload: "metadata", //预加载,可选值: 'none', 'metadata', 'auto'
|
||||||
|
volume: 0.5, // 默认音量,可选值: 0-1
|
||||||
|
mutex: true, // 互斥,默认为true,即不允许多个音频同时播放,当前播放的音频会暂停其他音频
|
||||||
|
lrcType: 3, // 歌词类型,可选值: 1, 2, 3
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onMounted(async () => {
|
||||||
|
// if (import.meta.env.VITE_APP_ENV != "development") {
|
||||||
|
const res = await $http.file.listMusic();
|
||||||
|
data.audio = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
let name = i.split(".")[0];
|
||||||
|
let t = name.split("-");
|
||||||
|
let mid = name + "/"+ name
|
||||||
|
data.audio.push({
|
||||||
|
title: t[1],
|
||||||
|
author: t[0],
|
||||||
|
url: res.baseUrl + mid +'.mp3',
|
||||||
|
pic: res.baseUrl + mid + ".jpg",
|
||||||
|
lrc: res.baseUrl + mid + ".lrc",
|
||||||
|
});
|
||||||
|
console.log('>>>>>',data.audio);
|
||||||
|
});
|
||||||
|
// } else {
|
||||||
|
// const d = {
|
||||||
|
// title: "南屏晚钟",
|
||||||
|
// author: "黄龄",
|
||||||
|
// url: "https://www.hxyouzi.com/music/黄龄 - 南屏晚钟.mp3",
|
||||||
|
// pic: "https://www.hxyouzi.com/img/cover/黄龄 - 南屏晚钟.jpg",
|
||||||
|
// lrc: "/黄龄 - 南屏晚钟.lrc",
|
||||||
|
// };
|
||||||
|
// data.audio.push(d, d, d, d, d, d, d, d, d, d, d);
|
||||||
|
// }
|
||||||
|
// 创建一个音乐播放器实例,并挂载到DOM上,同时进行相关配置
|
||||||
|
const ap = new APlayer({
|
||||||
|
container: document.getElementById("aplayer"),
|
||||||
|
audio: data.audio, // 音乐信息
|
||||||
|
...data.info, // 其他配置信息
|
||||||
|
});
|
||||||
|
const d = document.querySelector("#aplayer > div.aplayer-body > div.aplayer-miniswitcher > button");
|
||||||
|
let flag = true;
|
||||||
|
const app = document.querySelector("#aplayer > div.aplayer-lrc");
|
||||||
|
app.style.display = "block";
|
||||||
|
app.style.transform = "translateY(50px)";
|
||||||
|
d.addEventListener("click", () => {
|
||||||
|
if (flag) {
|
||||||
|
app.style.transform = "translateY(0)";
|
||||||
|
ap.lrc.show();
|
||||||
|
flag = false;
|
||||||
|
} else {
|
||||||
|
app.style.transform = "translateY(50px)";
|
||||||
|
ap.lrc.hide();
|
||||||
|
ap.list.hide();
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// d.click();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#aplayer {
|
||||||
|
width: 480px; // 定个宽度
|
||||||
|
color: var(--n-text-color); // 设置颜色
|
||||||
|
background-color: #f5f5f8; // 设置背景色
|
||||||
|
:deep(.aplayer-body) {
|
||||||
|
color: var(--n-text-color); // 设置颜色
|
||||||
|
background-color: #f5f5f8; // 设置背景色
|
||||||
|
// bottom: 10px; // 设置底部距离
|
||||||
|
.aplayer-icon-lrc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.aplayer-icon):hover svg path {
|
||||||
|
fill: @blue; // 设置颜色
|
||||||
|
}
|
||||||
|
:deep(.aplayer-list) {
|
||||||
|
width: 418px !important;
|
||||||
|
background-color: #f5f5f8;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
:deep(.aplayer-list-light) {
|
||||||
|
background: inherit;
|
||||||
|
color: @orange;
|
||||||
|
}
|
||||||
|
:deep(.aplayer-list) ol {
|
||||||
|
max-height: 160px !important;
|
||||||
|
// margin-bottom: 12px;
|
||||||
|
.aplayer-list-light .aplayer-list-author {
|
||||||
|
color: @orange !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.aplayer-list) ol li {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
:deep(.aplayer-list) ol li:hover {
|
||||||
|
background: inherit;
|
||||||
|
color: @blue;
|
||||||
|
border: none;
|
||||||
|
.aplayer-list-author {
|
||||||
|
color: @blue !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.aplayer-info) {
|
||||||
|
padding: 0 !important;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
:deep(.aplayer-icon) svg {
|
||||||
|
color: var(--n-text-color); // 设置颜色
|
||||||
|
background-color: var(--v-bgc); // 设置背景色
|
||||||
|
}
|
||||||
|
:deep(.aplayer-icon):hover svg {
|
||||||
|
color: @blue; // 设置颜色
|
||||||
|
}
|
||||||
|
:deep(.aplayer-miniswitcher) {
|
||||||
|
color: var(--n-text-color); // 设置颜色
|
||||||
|
background-color: var(--v-bgc); // 设置背景色
|
||||||
|
}
|
||||||
|
:deep(.aplayer-lrc) {
|
||||||
|
// text-align: right !important;
|
||||||
|
width: 368px !important;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
// left: unset;
|
||||||
|
bottom: 12px;
|
||||||
|
z-index: 999;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
:deep(.aplayer-lrc-contents) {
|
||||||
|
transition: all 0.5s ease !important;
|
||||||
|
p {
|
||||||
|
font-size: 12px !important;
|
||||||
|
color: @purple !important;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.aplayer-lrc-contents) {
|
||||||
|
.aplayer-lrc-current {
|
||||||
|
opacity: 1;
|
||||||
|
font-size: 13px !important;
|
||||||
|
color: @orange !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
#aplayer {
|
||||||
|
width: 100vw !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
80
src/components/arti.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<TransitionGroup name="list" tag="div" @enter="onEnter" @before-enter="onBeforeEnter">
|
||||||
|
<n-card v-for="(item, index) in list" size="small" :key="item.id" :bordered="false"
|
||||||
|
@click="handdleClickArti(item.id)" :data-index="index">
|
||||||
|
<div class="titel mt-2">
|
||||||
|
<n-h6 prefix="bar">
|
||||||
|
<n-text type="primary">
|
||||||
|
{{ item.title }}
|
||||||
|
</n-text>
|
||||||
|
</n-h6>
|
||||||
|
</div>
|
||||||
|
<div class="cont">
|
||||||
|
<div class="pro">{{ item.pro }}</div>
|
||||||
|
<div class="author mb-2 mr-4">{{ item.nickname }}</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
//mark import
|
||||||
|
import gsap from 'gsap';
|
||||||
|
|
||||||
|
|
||||||
|
//mark data
|
||||||
|
const router = useRouter()
|
||||||
|
const props = defineProps({
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//mark method
|
||||||
|
function handdleClickArti(id) {
|
||||||
|
router.push('/mini/' + id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onBeforeEnter(el) {
|
||||||
|
el.style.opacity = 0
|
||||||
|
if (el.dataset.index % 2 == 0)
|
||||||
|
el.style.transform = 'translateX(-500px)'
|
||||||
|
else
|
||||||
|
el.style.transform = 'translateX(500px)'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onEnter(el, done) {
|
||||||
|
gsap.to(el, {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
ease: 'slow',
|
||||||
|
delay: el.dataset.index * 0.4,
|
||||||
|
onComplete: done
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.n-card) {
|
||||||
|
width: 90%;
|
||||||
|
margin: 0 0 20px 5%;
|
||||||
|
box-shadow: @ps;
|
||||||
|
background-color: #fafbfc;
|
||||||
|
|
||||||
|
}
|
||||||
|
:deep(.n-card__content) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.pro {
|
||||||
|
text-indent: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
30
src/components/consoleMenu.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n-menu v-model:value="activeKey" :collapsed="collapsed" inverted :collapsed-width="64" :icon-size="20" :collapsed-icon-size="26" :options="menuOptions" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import menuInfo from "@/util/consoleMenu.js";
|
||||||
|
const props = defineProps({
|
||||||
|
collapsed: Boolean,
|
||||||
|
});
|
||||||
|
const activeKey = ref("0");
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { path } = route;
|
||||||
|
menuInfo.menuList.forEach((i, idx) => {
|
||||||
|
if (path == i) {
|
||||||
|
activeKey.value = idx + "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let menuOptions = ref([]);
|
||||||
|
menuOptions.value = menuInfo.options;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.orange) {
|
||||||
|
svg {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
152
src/components/edition.vue
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-[100%] h-[100%] relative">
|
||||||
|
<div class=" mb-6">
|
||||||
|
<n-input v-model:value="info.title" type="text" placeholder="请输入标题..." size="medium" clearable>
|
||||||
|
<template #prefix>
|
||||||
|
<n-button type="primary" size="mini" text>标题</n-button>
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
</div>
|
||||||
|
<div class=" mb-6">
|
||||||
|
<n-input v-model:value="info.pro" type="text" placeholder="请输入简介..." size="medium" clearable>
|
||||||
|
<template #prefix>
|
||||||
|
<n-button type="primary" size="mini" text>简介</n-button>
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
</div>
|
||||||
|
<div class="" style="border:1px solid #ccc;border-radius: 4px;overflow: hidden;">
|
||||||
|
<Toolbar style="border-bottom: 1px solid #ccc;font-size: 12px;" :editor="editorRef" :defaultConfig="toolbarConfig"
|
||||||
|
:mode="mode" />
|
||||||
|
<Editor style="height: 500px; overflow-y: hidden;" v-model="info.cont" :defaultConfig="editorConfig" :mode="mode"
|
||||||
|
@onCreated="handleCreated" />
|
||||||
|
</div>
|
||||||
|
<div class="btns absolute bottom-0 w-[100%]">
|
||||||
|
<n-button type="primary" @click="handleSubmit">提交</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
//mark import
|
||||||
|
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
||||||
|
import { onMounted, watchEffect } from "vue";
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
//mark data
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const info = reactive({
|
||||||
|
cont: "",
|
||||||
|
title: "",
|
||||||
|
pro: ""
|
||||||
|
})
|
||||||
|
const mode = 'simple' // 或 'simple','default'
|
||||||
|
const toolbarConfig = {}
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const editorConfig = ref({ MENU_CONF: {} })
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
|
||||||
|
//mark method
|
||||||
|
const handleCreated = (editor) => {
|
||||||
|
editorRef.value = editor
|
||||||
|
|
||||||
|
}
|
||||||
|
//mark 周期、内置函数等
|
||||||
|
editorConfig.value.placeholder = '请输入内容...'
|
||||||
|
toolbarConfig.excludeKeys = ['fullScreen', 'header1','header2','header3', 'emoji', 'line-height', 'headings', 'list-ul', 'list-ol', 'line', 'hr', 'link', '|', 'image', 'code', 'code-theme', 'insertVideo']
|
||||||
|
editorConfig.value.MENU_CONF['uploadImage'] = {
|
||||||
|
server: "https://www.hxyouzi.com/api/art/upimg",
|
||||||
|
fieldName: 'file',
|
||||||
|
headers: {
|
||||||
|
Authorization: cookie.get("token"),
|
||||||
|
},
|
||||||
|
maxNumberOfFiles: 1,
|
||||||
|
// 单个文件上传成功之后
|
||||||
|
customInsert(res, insertFn) {
|
||||||
|
console.log(res);
|
||||||
|
if (res.code == 1) {
|
||||||
|
const url = res.completePath
|
||||||
|
const alt = ""
|
||||||
|
const href = res.completePath
|
||||||
|
insertFn(url, alt, href)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
watchEffect(async () => {
|
||||||
|
console.log(props.id);
|
||||||
|
if (props.id != -1) {
|
||||||
|
const res = await $http.art.queryArtInfo(props.id)
|
||||||
|
console.log(res);
|
||||||
|
info.cont = res.data[0].cont
|
||||||
|
info.title = res.data[0].title
|
||||||
|
info.pro = res.data[0].pro
|
||||||
|
} else {
|
||||||
|
info.cont = ''
|
||||||
|
info.title = ''
|
||||||
|
info.pro = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
if (info.title == '') {
|
||||||
|
$msg.error("标题不能为空");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (info.pro == '') {
|
||||||
|
$msg.error("简介不能为空");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(info.cont);
|
||||||
|
if (info.cont == '' || info.cont == '<p><br></p>') {
|
||||||
|
$msg.error("内容不能为空");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (parseInt(props.id) < 0) {
|
||||||
|
const res = await $http.art.addArt(info)
|
||||||
|
if (res.code == 1) {
|
||||||
|
$msg.success(res.msg)
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
else $msg.error(res.msg)
|
||||||
|
} else {
|
||||||
|
const res = await $http.art.updArt({
|
||||||
|
id: props.id,
|
||||||
|
...info
|
||||||
|
})
|
||||||
|
if (res.code == 1) {
|
||||||
|
$msg.success(res.msg)
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
else $msg.error(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log(editorRef.value);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.w-e-bar-item button) {
|
||||||
|
padding: 0 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-button) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
141
src/components/falls.vue
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<div class="box flex" ref="curcomp">
|
||||||
|
<div class="col" v-for="item in fileArr">
|
||||||
|
<div v-for="i in item">
|
||||||
|
<slot :file="i"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive, watch } from "vue";
|
||||||
|
const prop = defineProps({
|
||||||
|
fileList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
count: [String, Number],
|
||||||
|
pathname: String,
|
||||||
|
bot: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 20
|
||||||
|
},
|
||||||
|
imgW: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { fileList, count, pathname, bot, imgW } = toRefs(prop);
|
||||||
|
const list = reactive({})
|
||||||
|
const lh = ref([])
|
||||||
|
const fileArr = ref([])
|
||||||
|
const originList = ref([])
|
||||||
|
|
||||||
|
initList(count.value)
|
||||||
|
|
||||||
|
watch(() => count.value, init)
|
||||||
|
|
||||||
|
watch(() => fileList.value, initPart, { immediate: true })
|
||||||
|
|
||||||
|
|
||||||
|
function initPart() {
|
||||||
|
console.log("initPart", fileList.value);
|
||||||
|
if (fileList.value.length < 1) {
|
||||||
|
// console.log("query");
|
||||||
|
const c = count.value
|
||||||
|
initList(c)
|
||||||
|
}
|
||||||
|
fileArr.value = []
|
||||||
|
originList.value = originList.value.concat(fileList.value)
|
||||||
|
const c = count.value
|
||||||
|
getlist(fileList.value)
|
||||||
|
for (let i = 0; i < c; i++) {
|
||||||
|
fileArr.value.push(list["file" + i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
fileArr.value = []
|
||||||
|
// console.log("init",fileList.value);
|
||||||
|
const c = count.value
|
||||||
|
// console.log("列数--->", c);
|
||||||
|
initList(c)
|
||||||
|
getlist(originList.value)
|
||||||
|
// console.log("list分组情况", list);
|
||||||
|
for (let i = 0; i < c; i++) {
|
||||||
|
fileArr.value.push(list["file" + i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initList(c) {
|
||||||
|
lh.value = []
|
||||||
|
for (let i = 0; i < c; i++) {
|
||||||
|
list["file" + i] = []
|
||||||
|
lh.value.push(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getlist(fl) {
|
||||||
|
if (lh.value.length != count.value) return
|
||||||
|
fl.forEach(async i => {
|
||||||
|
const { height, width } = await getImageSizeByCheck(i[pathname.value])
|
||||||
|
const index = getlhmIndex()
|
||||||
|
const h = imgW.value * height / width
|
||||||
|
list["file" + index].push(i)
|
||||||
|
// console.log("^^^",index,h,list["file" + index]);
|
||||||
|
lh.value[index] += (h + Number(bot.value))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImageSize(url) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let image = new Image();
|
||||||
|
image.onload = function () {
|
||||||
|
resolve(image.height);
|
||||||
|
};
|
||||||
|
image.onerror = function () {
|
||||||
|
reject(new Error('error'));
|
||||||
|
};
|
||||||
|
image.src = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImageSizeByCheck(url) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let image = new Image();
|
||||||
|
image.src = url;
|
||||||
|
let height = 0
|
||||||
|
let width = 0
|
||||||
|
let timer = setInterval(() => {
|
||||||
|
if (image.width > 0 && image.height > 0) {
|
||||||
|
height = image.height
|
||||||
|
width = image.width
|
||||||
|
resolve({ height, width })
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
}, 40)
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getlhmIndex() {
|
||||||
|
const min = Math.min(...lh.value)
|
||||||
|
// console.log(">>>>>>",lh.value,min,lh.value.findIndex(i => i == min));
|
||||||
|
return lh.value.findIndex(i => i == min)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.col:not(:last-child) {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width:1000px) {
|
||||||
|
.col:not(:last-child) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
36
src/components/foot.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div class="beian" ref="footer">
|
||||||
|
<a href="https://beian.miit.gov.cn" target="_blank">皖ICP备2021017362号-1</a>
|
||||||
|
<a class="swag" target="_blank" href="https://www.hxyouzi.com/swag">api文档</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { usesizeStore } from '@/stores/size.js';
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
|
const footer = ref(null);
|
||||||
|
const size = usesizeStore();
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
size.setFootH(footer.value.clientHeight)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.beian {
|
||||||
|
padding: 20px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: @mozi;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beian a {
|
||||||
|
font-size: 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: @blue;
|
||||||
|
}
|
||||||
|
.beian .swag {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
189
src/components/menu.vue
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<template>
|
||||||
|
<div class="box flex justify-between items-center md:py-1" ref="menuH">
|
||||||
|
<div class="md:hidden">
|
||||||
|
<n-menu v-model:value="activeKey" :options="menuInfo.options" mode="horizontal" />
|
||||||
|
</div>
|
||||||
|
<n-icon size="30" class="menuIcon ml-2 hidden md:block text-pp-500" @click="active = true">
|
||||||
|
<MdMenu />
|
||||||
|
</n-icon>
|
||||||
|
|
||||||
|
<div class="right flex items-center">
|
||||||
|
<div class="wea text-white md:hidden">
|
||||||
|
<wea />
|
||||||
|
</div>
|
||||||
|
<div v-if="!userinfo" class="login mx-8">
|
||||||
|
<n-button color="#8a2be2" size="small" @click="goLogin">登录</n-button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="uinfo flex items-center">
|
||||||
|
<n-dropdown :options="userOp" @select="handleSelect" :show-arrow="true">
|
||||||
|
<div class="uinfo mx-8 text-white flex items-center">
|
||||||
|
<n-avatar round size="small" :src="userinfo.avaUrl" />
|
||||||
|
<div class="ml-2"></div>
|
||||||
|
<n-button text class="userinfo">
|
||||||
|
{{ userinfo.nickname }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</n-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-drawer v-model:show="active" width="40%" :auto-focus="false" placement="left" show-mask="transparent">
|
||||||
|
<n-drawer-content>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center" :style="{ height: menuH.clientHeight + 'px' }">
|
||||||
|
<n-icon size="24" class="orange mx-2">
|
||||||
|
<orange />
|
||||||
|
</n-icon>
|
||||||
|
菜单
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n-menu v-model:value="activeKey" mode="vertical" :options="menuInfo.options.slice(1)"
|
||||||
|
@click="active = false"></n-menu>
|
||||||
|
</n-drawer-content>
|
||||||
|
</n-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import wea from "@/components/wea.vue";
|
||||||
|
import orange from "@/icon/orange.svg";
|
||||||
|
import { usesizeStore } from "@/stores/size.js";
|
||||||
|
import { renderIcon } from "@/util/h.js";
|
||||||
|
import menuInfo from "@/util/menu.js";
|
||||||
|
import { IosLogOut, IosSettings, MdHome, MdMenu } from "@vicons/ionicons4";
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
|
let activeKey = ref("1");
|
||||||
|
const active = ref(false);
|
||||||
|
const menuH = ref(null);
|
||||||
|
const sizeStore = usesizeStore();
|
||||||
|
let { setMenuH } = sizeStore
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { path, fullPath } = route;
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { push } = router;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let userinfo = ref(cookie.get("userinfo"));
|
||||||
|
let userOp = [
|
||||||
|
{
|
||||||
|
label: "控制台",
|
||||||
|
key: "profile",
|
||||||
|
icon: renderIcon(MdHome),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "退出登录",
|
||||||
|
key: "logout",
|
||||||
|
icon: renderIcon(IosLogOut),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "设置",
|
||||||
|
key: "2",
|
||||||
|
icon: renderIcon(IosSettings),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
menuInfo.menuList.forEach((i, idx) => {
|
||||||
|
if (path == i) {
|
||||||
|
activeKey.value = idx + 1 + "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log(5555, setMenu);
|
||||||
|
setMenuH(menuH.value.clientHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
function goLogin() {
|
||||||
|
push("/login");
|
||||||
|
}
|
||||||
|
function handleSelect(k) {
|
||||||
|
switch (k) {
|
||||||
|
case "logout":
|
||||||
|
logout();
|
||||||
|
break;
|
||||||
|
case "profile":
|
||||||
|
goConsole();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function logout() {
|
||||||
|
cookie.remove("token");
|
||||||
|
cookie.remove("userinfo");
|
||||||
|
userinfo.value = "";
|
||||||
|
// log.setLoginStatus(false);
|
||||||
|
$dialog.success({
|
||||||
|
title: "登出",
|
||||||
|
content: "已退出登录,是否重新登录?",
|
||||||
|
positiveText: "登录",
|
||||||
|
negativeText: "回首页",
|
||||||
|
onPositiveClick: () => {
|
||||||
|
push({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
redirect: fullPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {
|
||||||
|
push("/");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function goConsole() {
|
||||||
|
if (document.documentElement.clientWidth < 1000) {
|
||||||
|
push("/m");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
push("/console");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.login {
|
||||||
|
:deep(.n-button) {
|
||||||
|
background-color: @purple;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.orange) {
|
||||||
|
color: #ffa500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.menu-item) {
|
||||||
|
color: @orange !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.router-link-active) {
|
||||||
|
color: @purple !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-drawer-header),
|
||||||
|
:deep(.n-drawer-body-content-wrapper) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-menu-item-content--disabled) {
|
||||||
|
opacity: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1023px) {
|
||||||
|
:deep(.n-menu-item-content--selected::after) {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
height: 3px;
|
||||||
|
width: 70%;
|
||||||
|
background-color: @purple;
|
||||||
|
position: absolute;
|
||||||
|
left: 15%;
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
153
src/components/pass.vue
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n-steps :current="current">
|
||||||
|
<n-step title="邮箱" description=""></n-step>
|
||||||
|
<n-step title="修改密码"></n-step>
|
||||||
|
</n-steps>
|
||||||
|
<div class="step1 mt-5" v-if="current == 1">
|
||||||
|
<n-form-item-row label="邮箱">
|
||||||
|
<n-input v-model:value="form1.username" placeholder="请填写邮箱" @keyup.enter="getCode" />
|
||||||
|
</n-form-item-row>
|
||||||
|
<n-form-item-row label="验证码">
|
||||||
|
<n-input-group>
|
||||||
|
<n-input v-model:value="form1.code" placeholder="请填写验证码" />
|
||||||
|
<n-button type="info" @click="getCode">获取验证码</n-button>
|
||||||
|
</n-input-group>
|
||||||
|
</n-form-item-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-form ref="pass" :model="form2" class="step2 mt-5" v-if="current == 2" :rules="rules">
|
||||||
|
<n-form-item label="密码" path="password">
|
||||||
|
<n-input v-model:value="form2.password" type="password" show-password-on="mousedown" placeholder="请输入密码"
|
||||||
|
@keydown.enter.prevent />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="重复密码" path="repassword">
|
||||||
|
<n-input ref="reInput" type="password" show-password-on="mousedown" v-model:value="form2.repassword"
|
||||||
|
placeholder="请再次输入密码" @keyup.enter="" />
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
|
||||||
|
<div class="btns">
|
||||||
|
<n-button v-if="current == 1" type="primary" secondary @click="back">返回登录</n-button>
|
||||||
|
<n-button v-if="current == 2" type="primary" secondary @click="current--">上一步</n-button>
|
||||||
|
<n-button v-if="current == 1" type="primary" @click="next">下一步</n-button>
|
||||||
|
<n-button v-if="current == 2" type="primary" @click="submit">确定改密</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
const emit = defineEmits(["back"]);
|
||||||
|
|
||||||
|
const pass = ref(null)
|
||||||
|
const repass = ref(null)
|
||||||
|
|
||||||
|
const current = ref(1);
|
||||||
|
const form1 = reactive({
|
||||||
|
username: "",
|
||||||
|
code: "",
|
||||||
|
});
|
||||||
|
const form2 = reactive({
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
repassword: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
password: [
|
||||||
|
{ required: true, message: "密码不能为空", trigger: "blur" },
|
||||||
|
{ validator: validatePassword, message: "必须是含有数字,字母的6-12位密码", trigger: "blur" },
|
||||||
|
],
|
||||||
|
repassword: [
|
||||||
|
{ required: true, message: "密码不能为空", trigger: "blur" },
|
||||||
|
{ validator: validatePassword, message: "必须是含有数字,字母的6-12位密码", trigger: "blur" },
|
||||||
|
{ validator: validatePasswordSame, message: "两次密码输入不一致", trigger: ["blur", "password-input"] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function validatePasswordSame(rule, value) {
|
||||||
|
return value === form2.password;
|
||||||
|
}
|
||||||
|
function validatePassword(r, v) {
|
||||||
|
return /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,12}$/.test(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCode() {
|
||||||
|
if (!form1.username) {
|
||||||
|
$msg.error("请输入邮箱");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await $http.addr.sendMailinpass({ mail: form1.username });
|
||||||
|
console.log(888, res);
|
||||||
|
if (res.code == "1") $msg.success(res.msg);
|
||||||
|
else if (res.code == "0") $msg.error(res.msg);
|
||||||
|
}
|
||||||
|
async function next() {
|
||||||
|
if (!form1.username) {
|
||||||
|
$msg.error("请输入邮箱");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!form1.code) {
|
||||||
|
$msg.error("请输入验证码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await $http.user.rexpass(form1);
|
||||||
|
if (res.code == "1") {
|
||||||
|
$msg.success(res.msg);
|
||||||
|
current.value++;
|
||||||
|
form2.username = form1.username;
|
||||||
|
} else if (res.code == "0") $msg.error(res.msg);
|
||||||
|
}
|
||||||
|
function submit() {
|
||||||
|
pass.value.validate(async errors => {
|
||||||
|
if (!errors) {
|
||||||
|
const res = await $http.user.updatepass(form2);
|
||||||
|
if (res.code == "1") {
|
||||||
|
$msg.success(res.msg);
|
||||||
|
emit("back");
|
||||||
|
} else if (res.code == "0") $msg.error(res.msg);
|
||||||
|
} else {
|
||||||
|
$msg.error(errors[0][0].message)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
emit("back");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.btns {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
:deep(.n-button) {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-step--wait-status) {
|
||||||
|
.n-step-indicator {
|
||||||
|
border: 1px solid @blue;
|
||||||
|
|
||||||
|
.n-step-indicator-slot__index {
|
||||||
|
color: @blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-step-content-header__title {
|
||||||
|
color: @blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-step--finish-status) {
|
||||||
|
.n-step-content-header__title {
|
||||||
|
color: @purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
42
src/components/router.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!-- 渐变过渡 -->
|
||||||
|
<template>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<div>
|
||||||
|
<component :is="Component" />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 缩放过渡 -->
|
||||||
|
<!-- <template>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition name="scale" mode="out-in">
|
||||||
|
<component :is="Component" />
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.scale-enter-active,
|
||||||
|
.scale-leave-active {
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scale-enter-from,
|
||||||
|
.scale-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
</style> -->
|
58
src/components/wea.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="info">
|
||||||
|
<div class="loc flex items-center">
|
||||||
|
<n-icon size="18" color="#8a2be2">
|
||||||
|
<IosPin />
|
||||||
|
</n-icon>
|
||||||
|
{{ addr }}
|
||||||
|
</div>
|
||||||
|
<div class="wea">
|
||||||
|
<i :class="'qiIcon qi-' + now.icon + '-fill'"></i>
|
||||||
|
<div class="tem">{{ now.text }} {{ now.temp }} ℃</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { IosPin } from "@vicons/ionicons4";
|
||||||
|
const city = ref("合肥市");
|
||||||
|
const addr = ref("安徽省合肥市");
|
||||||
|
let now = ref({});
|
||||||
|
onMounted(async () => {
|
||||||
|
const res = await $http.addr.getAddressByIP();
|
||||||
|
console.log("111", res);
|
||||||
|
city.value = res.city;
|
||||||
|
addr.value = ['上海','重庆','北京','天津'].includes(res.province) ? res.country + res.city : res.province + res.city;
|
||||||
|
const r = await $http.wea.getWeather({ city: city.value });
|
||||||
|
now.value = r.now;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.loc {
|
||||||
|
margin-right: 15px;
|
||||||
|
|
||||||
|
:deep(n-icon) {
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wea {
|
||||||
|
display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.qiIcon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: @purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tem {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}</style>
|
BIN
src/cursor/alt.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/cursor/busy.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/cursor/cross.png
Normal file
After Width: | Height: | Size: 639 B |
BIN
src/cursor/diag1.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/cursor/diag2_26x26.png
Normal file
After Width: | Height: | Size: 604 B |
BIN
src/cursor/handwriting_26x26.png
Normal file
After Width: | Height: | Size: 523 B |
BIN
src/cursor/help_26x26.png
Normal file
After Width: | Height: | Size: 796 B |
BIN
src/cursor/horiz_26x26.png
Normal file
After Width: | Height: | Size: 532 B |
BIN
src/cursor/link_26x26.png
Normal file
After Width: | Height: | Size: 895 B |
BIN
src/cursor/loc_26x26.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
src/cursor/move_26x26.png
Normal file
After Width: | Height: | Size: 907 B |
BIN
src/cursor/normal.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/cursor/text_26x26.png
Normal file
After Width: | Height: | Size: 400 B |
BIN
src/cursor/unavailable.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/cursor/vert.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/cursor/working.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
1
src/icon/article.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M26 30H11a2.002 2.002 0 0 1-2-2v-6h2v6h15V6h-9V4h9a2.002 2.002 0 0 1 2 2v22a2.002 2.002 0 0 1-2 2z" fill="currentColor"></path><path d="M17 10h7v2h-7z" fill="currentColor"></path><path d="M16 15h8v2h-8z" fill="currentColor"></path><path d="M15 20h9v2h-9z" fill="currentColor"></path><path d="M9 19a5.005 5.005 0 0 1-5-5V3h2v11a3 3 0 0 0 6 0V5a1 1 0 0 0-2 0v10H8V5a3 3 0 0 1 6 0v9a5.005 5.005 0 0 1-5 5z" fill="currentColor"></path></svg>
|
After Width: | Height: | Size: 549 B |
1
src/icon/back.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694156756216" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4088" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M379.616 515.648L705.44 841.472c20 20 20 52.416 0 72.416s-52.416 20-72.416 0L270.976 551.84c-20-20-20-52.416 0-72.416l362.048-362.048c20-20 52.416-20 72.416 0s20 52.416 0 72.416L379.616 515.616z" p-id="4089"></path></svg>
|
After Width: | Height: | Size: 552 B |
1
src/icon/group.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="48px" height="48.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#7B1DD3" d="M822.327949 1008.51541h-620.624867a186.404679 186.404679 0 0 1-186.18746-186.187461v-620.624867a186.404679 186.404679 0 0 1 186.18746-186.18746h620.624867a186.404679 186.404679 0 0 1 186.187461 186.18746v620.624867a186.404679 186.404679 0 0 1-186.187461 186.187461zM510.960453 298.520561a84.063638 84.063638 0 0 0-57.842237 22.590745 98.679354 98.679354 0 0 0-27.090276 35.996243 158.259341 158.259341 0 0 0-10.891966 37.237492 107.399133 107.399133 0 0 0-1.365375 17.129246 123.380224 123.380224 0 0 0 13.002091 55.452832 100.944635 100.944635 0 0 0 39.719992 45.119428l-131.386285 60.883299a24.173339 24.173339 0 0 0-13.684778 23.273433v104.016728a27.400588 27.400588 0 0 0 6.206249 17.811933 18.835965 18.835965 0 0 0 15.050153 7.540593h335.94424a19.487621 19.487621 0 0 0 15.732841-7.540593 27.400588 27.400588 0 0 0 6.206248-17.811933v-104.016728a23.831995 23.831995 0 0 0-13.684778-23.273433l-91.014637-43.754053-38.975241-18.618746a100.199885 100.199885 0 0 0 39.657929-46.546865 131.479378 131.479378 0 0 0 10.954028-52.753114 158.259341 158.259341 0 0 0-3.413436-30.814024 116.615413 116.615413 0 0 0-34.134368-58.959363 84.932513 84.932513 0 0 0-58.959362-22.994151z m158.104185 41.085366a62.062487 62.062487 0 0 0-21.22537 3.72375 86.887481 86.887481 0 0 0-19.177309 9.961029 153.449498 153.449498 0 0 1 8.22328 27.928119 161.021122 161.021122 0 0 1 2.730749 30.069275 157.049123 157.049123 0 0 1-6.516561 45.491802 149.167187 149.167187 0 0 1-18.122246 39.347617 72.706203 72.706203 0 0 0 18.618746 15.763872l67.710173 32.179399a53.001364 53.001364 0 0 1 22.901058 20.852996 58.680081 58.680081 0 0 1 8.595654 31.031243v88.252856h72.520016a15.298403 15.298403 0 0 0 12.412497-5.802842 22.46662 22.46662 0 0 0 4.778812-14.739841v-83.474045a19.735871 19.735871 0 0 0-10.954029-19.177308l-104.699415-49.929271a89.152762 89.152762 0 0 0 29.44865-33.172399 99.299979 99.299979 0 0 0 11.636716-47.570896 101.81351 101.81351 0 0 0-6.206249-35.220461 93.404043 93.404043 0 0 0-16.818934-29.076275 79.408952 79.408952 0 0 0-24.824994-19.549684 68.920392 68.920392 0 0 0-31.000212-6.919967z m-314.780932 0a66.468923 66.468923 0 0 0-30.503713 7.168218 80.898451 80.898451 0 0 0-24.576744 19.549683 94.428074 94.428074 0 0 0-16.756872 29.076275 105.164884 105.164884 0 0 0 5.430468 83.474045 85.335919 85.335919 0 0 0 30.814024 33.172399l-106.002727 49.246583a17.998121 17.998121 0 0 0-10.954029 19.177308v83.474045a22.435589 22.435589 0 0 0 4.716749 14.739841 15.515622 15.515622 0 0 0 12.412497 5.802842h70.534017v-88.252856a58.959362 58.959362 0 0 1 8.533592-31.031243 52.753114 52.753114 0 0 1 23.583745-20.852996l70.471953-33.544774a68.610079 68.610079 0 0 0 15.763872-13.002091 143.240219 143.240219 0 0 1-19.115246-40.030304 154.970029 154.970029 0 0 1-6.919967-46.17449 157.576654 157.576654 0 0 1 11.698778-59.517925 101.223916 101.223916 0 0 0-18.618746-8.905967 60.572987 60.572987 0 0 0-20.48062-3.599624z" /></svg>
|
After Width: | Height: | Size: 3.1 KiB |
1
src/icon/hot.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711591952359" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3959" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M414.72 981.333s-416.427-91.52-230.4-545.92c0 0 42.667 50.56 36.48 74.88 0 0 33.067-114.773 104.533-183.253 61.44-59.093 123.734-224.64 66.347-284.373 0 0 284.8 59.733 316.587 359.04 0 0 36.48-95.36 111.146-104.747a204.587 204.587 0 0 0 0 130.987s235.947 403.84-170.666 540.373c0 0 121.813-138.453-136.534-375.893 0 0-61.013 128-97.28 171.946-0.213 0-101.973 114.134-0.213 216.96z" p-id="3960" fill="#d81e06"></path></svg>
|
After Width: | Height: | Size: 755 B |
12
src/icon/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const icons = import.meta.glob('./*.svg', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const icon = {}
|
||||||
|
console.log(icons);
|
||||||
|
for (const i in icons) {
|
||||||
|
const t = i.split("/")
|
||||||
|
const name = t[1].split('.')[0]
|
||||||
|
icon[name] = icons[i]
|
||||||
|
}
|
||||||
|
export default icon
|
1
src/icon/lr.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20"><g fill="none"><path d="M14.768 15.712l1.513-1.461l-1.513-1.461a.75.75 0 1 1 1.042-1.08l1.886 1.821a1 1 0 0 1 0 1.44l-1.886 1.82a.75.75 0 0 1-1.042-1.079zm-9.534 0L3.72 14.251l1.513-1.461a.75.75 0 0 0-1.042-1.08l-1.886 1.821a1 1 0 0 0 0 1.44l1.886 1.82a.75.75 0 0 0 1.042-1.079zM8 14.252a.75.75 0 0 1-.75.75h-.5a.75.75 0 1 1 0-1.5h.5a.75.75 0 0 1 .75.75zm1.75.75a.75.75 0 1 1 0-1.5h.5a.75.75 0 0 1 0 1.5h-.5zm2.25-.75c0 .415.336.75.75.75h.5a.75.75 0 1 0 0-1.5h-.5a.75.75 0 0 0-.75.75zm5-9.252a2 2 0 0 0-2-2h-10a2 2 0 0 0-2 2v4.25a.75.75 0 0 0 1.5 0V5a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 .5.5v4.25a.75.75 0 1 0 1.5 0V5z" fill="currentColor"></path></g></svg>
|
After Width: | Height: | Size: 755 B |
1
src/icon/mail.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="48px" height="48.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#7B1DD3" d="M873.472 0H150.528C67.584 0 0 67.584 0 150.528v722.944C0 956.416 67.584 1024 150.528 1024h722.944c82.944 0 150.528-67.584 150.528-150.528V150.528C1024 67.584 956.416 0 873.472 0z m-61.952 656.896c0 41.472-33.792 74.752-74.752 74.752H287.232c-41.472 0-74.752-33.28-74.752-74.752V367.104c0-41.472 33.28-74.752 74.752-74.752h449.024c41.472 0 74.752 33.28 74.752 74.752v289.792z" /><path fill="#7B1DD3" d="M712.192 427.008L512 556.032 311.808 427.008c-9.216-6.144-21.504-3.072-27.648 6.144-6.144 9.216-3.072 21.504 6.144 27.648l210.944 135.68c3.072 2.048 7.168 3.072 10.752 3.072 3.584 0 7.68-1.024 10.752-3.072L733.696 460.8a20.48 20.48 0 0 0 6.144-27.648 20.48 20.48 0 0 0-27.648-6.144z" /></svg>
|
After Width: | Height: | Size: 976 B |
1
src/icon/more.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711588832708" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6877" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M713.8 523.5c-8.9-8.9-23.2-8.6-32.1 0.3L512.1 693.6 342.7 523.7c-8.9-8.9-23.2-9.1-32.1-0.3v0.5c-8.9 8.9-8.9 23 0 31.9l185.3 185.5c0.5 0.5 1.1 1 1.7 1.5l0.1 0.1c0.5 0.4 1.1 0.8 1.7 1.2 0.1 0.1 0.2 0.1 0.2 0.2 0.5 0.4 1.1 0.7 1.7 1 0.1 0 0.2 0.1 0.3 0.1 0.6 0.3 1.2 0.6 1.8 0.8 0.1 0 0.1 0.1 0.2 0.1 0.6 0.2 1.2 0.5 1.9 0.7h0.2c0.6 0.2 1.3 0.4 2 0.5h0.1c0.7 0.1 1.4 0.2 2 0.3h0.1c0.7 0.1 1.4 0.1 2.1 0.1 0.7 0 1.4 0 2.1-0.1h0.1c0.7-0.1 1.4-0.2 2-0.3h0.1c0.7-0.1 1.3-0.3 2-0.5h0.2c0.6-0.2 1.3-0.4 1.9-0.7 0.1 0 0.1-0.1 0.2-0.1 0.6-0.2 1.2-0.5 1.8-0.8 0.1 0 0.2-0.1 0.3-0.1 0.6-0.3 1.1-0.6 1.7-1 0.1-0.1 0.2-0.1 0.2-0.2 0.6-0.4 1.1-0.8 1.7-1.2l0.1-0.1c0.6-0.5 1.1-1 1.7-1.5l185.8-185.5c8.8-8.8 8.8-23.4-0.1-32.3z m-218.3-23.1c0.5 0.5 1.1 1 1.7 1.5l0.1 0.1c0.5 0.4 1.1 0.8 1.7 1.2 0.1 0.1 0.2 0.1 0.2 0.2 0.5 0.4 1.1 0.7 1.7 1 0.1 0 0.2 0.1 0.3 0.1 0.6 0.3 1.2 0.6 1.8 0.8 0.1 0 0.1 0.1 0.2 0.1 0.6 0.2 1.2 0.5 1.9 0.7h0.2c0.6 0.2 1.3 0.4 2 0.5h0.1c0.7 0.1 1.4 0.2 2 0.3h0.1c0.7 0.1 1.4 0.1 2.1 0.1 0.7 0 1.4 0 2.1-0.1h0.1c0.7-0.1 1.4-0.2 2-0.3h0.1c0.7-0.1 1.3-0.3 2-0.5h0.2c0.6-0.2 1.3-0.4 1.9-0.7 0.1 0 0.1-0.1 0.2-0.1 0.6-0.2 1.2-0.5 1.8-0.8 0.1 0 0.2-0.1 0.3-0.1 0.6-0.3 1.1-0.6 1.7-1 0.1-0.1 0.2-0.1 0.2-0.2 0.6-0.4 1.1-0.8 1.7-1.2l0.1-0.1c0.6-0.5 1.1-1 1.7-1.5l185.8-185.5c8.9-8.9 8.9-23.5 0-32.4-8.9-8.9-23.2-8.6-32.1 0.3L511.7 452.6 342.3 282.7c-8.9-8.9-23.2-9.1-32.1-0.3v0.5c-8.9 8.9-8.9 23 0 31.9l185.3 185.6z" p-id="6878" fill="#8a2be2"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
17
src/icon/orange.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
<svg
|
||||||
|
t="1652688633372"
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="1737"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M544.768 904.192c40.96-3.412992 80.212992-12.971008 117.76-28.672s72.020992-36.864 103.424-63.488L578.56 624.64c-10.24 6.144-21.504 10.923008-33.792 14.336v265.216z m267.264-138.24c26.624-31.403008 47.787008-65.876992 63.488-103.424 15.700992-37.547008 25.259008-76.8 28.672-117.76H638.976c-3.412992 12.288-8.192 23.552-14.336 33.792l187.392 187.392z m92.16-286.72c-3.412992-40.96-12.971008-80.212992-28.672-117.76s-36.864-72.020992-63.488-103.424L624.64 445.44c6.144 10.24 10.923008 21.504 14.336 33.792h265.216zM765.952 211.968c-31.403008-26.624-65.876992-47.787008-103.424-63.488-37.547008-15.700992-76.8-25.259008-117.76-28.672v265.216c12.288 3.412992 23.552 8.192 33.792 14.336l187.392-187.392z m-286.72-92.16c-40.96 3.412992-80.212992 12.971008-117.76 28.672s-72.020992 36.864-103.424 63.488l187.392 187.392c10.24-6.144 21.504-10.923008 33.792-14.336V119.808zM211.968 258.048c-26.624 31.403008-47.787008 65.876992-63.488 103.424-15.700992 37.547008-25.259008 76.8-28.672 117.76h265.216c3.412992-12.288 8.192-23.552 14.336-33.792L211.968 258.048z m-92.16 286.72c3.412992 40.96 12.971008 80.212992 28.672 117.76s36.864 72.020992 63.488 103.424l187.392-187.392c-6.144-10.923008-10.923008-22.187008-14.336-33.792H119.808z m138.24 267.264c31.403008 26.624 65.876992 47.787008 103.424 63.488 37.547008 15.700992 76.8 25.259008 117.76 28.672V638.976c-11.604992-3.412992-22.868992-8.192-33.792-14.336L258.048 812.032z m253.952 158.72c-129.707008-3.412992-237.739008-48.299008-324.096-134.656S56.660992 641.707008 53.248 512c3.412992-129.707008 48.299008-237.739008 134.656-324.096S382.292992 56.660992 512 53.248c129.707008 3.412992 237.739008 48.299008 324.096 134.656S967.339008 382.292992 970.752 512c-3.412992 129.707008-48.299008 237.739008-134.656 324.096S641.707008 967.339008 512 970.752z m0-393.216c18.432-0.683008 33.792-7.168 46.08-19.456s18.432-27.648 18.432-46.08-6.144-33.792-18.432-46.08-27.648-18.432-46.08-18.432-33.792 6.144-46.08 18.432-18.432 27.648-18.432 46.08 6.144 33.792 18.432 46.08 27.648 18.772992 46.08 19.456z"
|
||||||
|
p-id="1738"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
|
After Width: | Height: | Size: 2.2 KiB |
1
src/icon/pic.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024"><path d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 632H136v-39.9l138.5-164.3l150.1 178L658.1 489L888 761.6V792zm0-129.8L664.2 396.8c-3.2-3.8-9-3.8-12.2 0L424.6 666.4l-144-170.7c-3.2-3.8-9-3.8-12.2 0L136 652.7V232h752v430.2zM304 456a88 88 0 1 0 0-176a88 88 0 0 0 0 176zm0-116c15.5 0 28 12.5 28 28s-12.5 28-28 28s-28-12.5-28-28s12.5-28 28-28z" fill="currentColor"></path></svg>
|
After Width: | Height: | Size: 554 B |
1
src/icon/profile.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="7" r="4"></circle><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path><path d="M21 21v-2a4 4 0 0 0-3-3.85"></path></g></svg>
|
After Width: | Height: | Size: 396 B |
1
src/icon/qq.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="48px" height="48.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#7B1DD3" d="M824.8 613.2c-16-51.4-34.4-94.6-62.7-165.3C766.5 262.2 689.3 112 511.5 112 331.7 112 256.2 265.2 261 447.9c-28.4 70.8-46.7 113.7-62.7 165.3-34 109.5-23 154.8-14.6 155.8 18 2.2 70.1-82.4 70.1-82.4 0 49 25.2 112.9 79.8 159-26.4 8.1-85.7 29.9-71.6 53.8 11.4 19.3 196.2 12.3 249.5 6.3 53.3 6 238.1 13 249.5-6.3 14.1-23.8-45.3-45.7-71.6-53.8 54.6-46.2 79.8-110.1 79.8-159 0 0 52.1 84.6 70.1 82.4 8.5-1.1 19.5-46.4-14.5-155.8z" /></svg>
|
After Width: | Height: | Size: 710 B |
1
src/icon/right.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694158711559" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5049" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M724 511.4L361 947.6c-12.2 14.6-33.9 16.6-48.5 4.4-14.6-12.2-16.6-33.9-4.4-48.5l326.2-392-326.3-391c-12.2-14.6-10.2-36.3 4.4-48.5 14.6-12.2 36.3-10.2 48.5 4.4l363.1 435z" p-id="5050"></path></svg>
|
After Width: | Height: | Size: 527 B |
1
src/icon/user.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="48px" height="48.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#7B1DD3" d="M490.7 100.8c115.8 0 209.6 92.3 209.6 206.2 0 113.9-93.9 206.2-209.6 206.2S281.1 420.9 281.1 307c0-113.9 93.9-206.2 209.6-206.2z m0 0c115.8 0 209.6 92.3 209.6 206.2 0 113.9-93.9 206.2-209.6 206.2S281.1 420.9 281.1 307c0-113.9 93.9-206.2 209.6-206.2zM412.1 582h174.7c149.6 0 270.8 119.2 270.8 266.3v17.2c0 58-121.2 60.2-270.8 60.2H412.1c-149.6 0-270.8-0.1-270.8-60.2v-17.2c0-147.1 121.2-266.3 270.8-266.3z m0 0" /></svg>
|
After Width: | Height: | Size: 699 B |
13
src/index.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<n-global-style />
|
||||||
|
<RouterView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useDialog, useMessage, useNotification } from "naive-ui";
|
||||||
|
window.$msg = useMessage();
|
||||||
|
window.$dialog = useDialog();
|
||||||
|
window.$note = useNotification();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
36
src/main.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { createPinia } from "pinia";
|
||||||
|
import { createApp } from "vue";
|
||||||
|
|
||||||
|
import http from '@/api/index.js';
|
||||||
|
import icon from "@/icon/index.js";
|
||||||
|
import "@vant/touch-emulator";
|
||||||
|
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
|
||||||
|
import "qweather-icons/font/qweather-icons.css";
|
||||||
|
import "tailwindcss/tailwind.css";
|
||||||
|
import "vfonts/FiraCode.css";
|
||||||
|
import VueWechatTitle from "vue-wechat-title";
|
||||||
|
import App from "./App.vue";
|
||||||
|
import "./assets/style/main.css";
|
||||||
|
import router from "./router/guard.js";
|
||||||
|
import { formatTimeBydate } from './util/index.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Date.prototype.format = formatTimeBydate
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
document.oncontextmenu = function () {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
app.use(createPinia());
|
||||||
|
app.use(router).use(VueWechatTitle);
|
||||||
|
|
||||||
|
|
||||||
|
window.$http = http
|
||||||
|
// window.$icon = icon
|
||||||
|
for (const key in icon) {
|
||||||
|
// console.log(key, icon[key]);
|
||||||
|
app.component('icon-'+key, icon[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.mount("#app");
|
38
src/router/guard.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import NProgress from "nprogress";
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
import router from "./index";
|
||||||
|
// import 'nprogress/nprogress.css'; // nprogress样式文件
|
||||||
|
import "@/router/nprogress.less";
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const token = cookie.get("token");
|
||||||
|
const userinfo = cookie.get("userinfo");
|
||||||
|
// 路由发生变化修改页面title
|
||||||
|
if (to.meta.title) {
|
||||||
|
document.title = to.meta.title;
|
||||||
|
}
|
||||||
|
// 开启进度条
|
||||||
|
NProgress.start();
|
||||||
|
if (to.meta?.auth) {
|
||||||
|
if (!!token && !!userinfo) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
redirect: to.fullPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//当路由跳转结束后
|
||||||
|
router.afterEach(() => {
|
||||||
|
// 关闭进度条
|
||||||
|
NProgress.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
143
src/router/index.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
import HomeView from "../views/HomeView.vue";
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
name: "home",
|
||||||
|
redirect: "/home",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: HomeView,
|
||||||
|
redirect: "/home",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/home",
|
||||||
|
component: () => import("@/views/home/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/gallery",
|
||||||
|
component: () => import("@/views/gallery/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/article",
|
||||||
|
component: () => import("@/views/article/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tour",
|
||||||
|
component: () => import("@/views/tour/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/plink",
|
||||||
|
component: () => import("@/views/plink/index.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
component: () => import("@/views/login.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/swag",
|
||||||
|
component: () => import("@/views/swag/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/mini/:id",
|
||||||
|
component: () => import("@/views/article/mini/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/console",
|
||||||
|
component: () => import("@/views/console/index.vue"),
|
||||||
|
redirect: "/console/home",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/console/home",
|
||||||
|
component: () => import("@/views/console/home/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "控制台",
|
||||||
|
name: "首页",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/console/profile",
|
||||||
|
component: () => import("@/views/console/profile/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "控制台",
|
||||||
|
name: "个人信息",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/console/gallery",
|
||||||
|
component: () => import("@/views/console/gallery/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "控制台",
|
||||||
|
name: "画廊管理",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/console/article",
|
||||||
|
component: () => import("@/views/console/article/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "控制台",
|
||||||
|
name: "文章管理",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/m",
|
||||||
|
component: () => import("@/views/mobile/index.vue"),
|
||||||
|
redirect: "/m/home",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/m/home",
|
||||||
|
component: () => import("@/views/mobile/home/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "我的",
|
||||||
|
name: "控制台",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/m/profile",
|
||||||
|
component: () => import("@/views/mobile/info/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "个人信息",
|
||||||
|
name: "控制台",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/m/gallery",
|
||||||
|
component: () => import("@/views/mobile/gallery/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "画廊管理",
|
||||||
|
name: "控制台",
|
||||||
|
auth: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/m/article",
|
||||||
|
component: () => import("@/views/mobile/arti/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "文章管理",
|
||||||
|
name: "控制台",
|
||||||
|
auth: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
82
src/router/nprogress.less
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/* Make clicks pass-through */
|
||||||
|
#nprogress {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
background: @purple;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1031;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fancy blur effect */
|
||||||
|
#nprogress .peg {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove these to get rid of the spinner */
|
||||||
|
#nprogress .spinner {
|
||||||
|
/* display: block; */
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1031;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .spinner-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
border: solid 2px transparent;
|
||||||
|
border-top-color: #29d;
|
||||||
|
border-left-color: #29d;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
||||||
|
animation: nprogress-spinner 400ms linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nprogress-custom-parent {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nprogress-custom-parent #nprogress .spinner,
|
||||||
|
.nprogress-custom-parent #nprogress .bar {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes nprogress-spinner {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes nprogress-spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
14
src/stores/login.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
export const loginStore = defineStore("login", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
loginStatus: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
setLoginStatus(v) {
|
||||||
|
this.loginStatus = v;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
21
src/stores/size.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
export const usesizeStore = defineStore("size", {
|
||||||
|
state: () => ({
|
||||||
|
menuH: 0,
|
||||||
|
navH: 0,
|
||||||
|
conH: 0,
|
||||||
|
footH:0,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setMenuH(v) {
|
||||||
|
this.menuH = v;
|
||||||
|
},
|
||||||
|
setConH(v) {
|
||||||
|
this.conH = v;
|
||||||
|
},
|
||||||
|
setFootH(v){
|
||||||
|
this.footH = v;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
34
src/util/consoleMenu.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import article from "@/icon/article.svg";
|
||||||
|
import orange from "@/icon/orange.svg";
|
||||||
|
import pic from "@/icon/pic.svg";
|
||||||
|
import profile from "@/icon/profile.svg";
|
||||||
|
import { NIcon } from "naive-ui";
|
||||||
|
import { RouterLink } from "vue-router";
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
label: () => h(RouterLink, { to: "/console/home", class: "menu-item" }, { default: () => "黑心柚子" }),
|
||||||
|
key: "0",
|
||||||
|
icon: () => h(NIcon, { class: "orange" }, { default: () => h(orange) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () => h(RouterLink, { to: "/console/profile", class: "menu-item" }, { default: () => "个人信息" }),
|
||||||
|
key: "1",
|
||||||
|
icon: () => h(NIcon, { class: "orange" }, { default: () => h(profile) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () => h(RouterLink, { to: "/console/gallery", class: "menu-item" }, { default: () => "画廊管理" }),
|
||||||
|
key: "2",
|
||||||
|
icon: () => h(NIcon, { class: "orange" }, { default: () => h(pic) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () => h(RouterLink, { to: "/console/article", class: "menu-item" }, { default: () => "文章管理" }),
|
||||||
|
key: "3",
|
||||||
|
icon: () => h(NIcon, { class: "orange" }, { default: () => h(article) }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const consoleMenuInfo = {
|
||||||
|
options,
|
||||||
|
menuList: ["/console/home", "/console/profile", "/console/gallery", "/console/article"],
|
||||||
|
};
|
||||||
|
export default consoleMenuInfo;
|
9
src/util/h.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { NIcon } from 'naive-ui';
|
||||||
|
|
||||||
|
export function renderIcon(icon) {
|
||||||
|
return () => {
|
||||||
|
return h(NIcon, null, {
|
||||||
|
default: () => h(icon)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
136
src/util/index.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// 将res的key对应的值复制给dzdata的同名key
|
||||||
|
export function ObjectCopy(res, dzdata) {
|
||||||
|
Object.keys(dzdata).map(key => {
|
||||||
|
dzdata[key] = res[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 时间格式化
|
||||||
|
* @param {*} time 传入时间参数,支持字符串和时间戳
|
||||||
|
* @param {*} f 对应格式 "YYYY-MM-DD hh:mm:ss" "YYYY年MM月DD日hh时mm分ss秒"
|
||||||
|
* 格式可以自定义,对应的字符串对应的时间会更新,输入单个字符表示可以不补零
|
||||||
|
* @return {*} 返回格式化的日期
|
||||||
|
*/
|
||||||
|
export function formatTime(time, f) {
|
||||||
|
const date = new Date(time);
|
||||||
|
if (!(date instanceof Date && !isNaN(date.getTime()))) {
|
||||||
|
console.error("不合法的日期!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth();
|
||||||
|
const day = date.getDate();
|
||||||
|
const hour = date.getHours();
|
||||||
|
const minute = date.getMinutes();
|
||||||
|
const second = date.getSeconds();
|
||||||
|
const monthAdd0 = timeAdd0(month + 1);
|
||||||
|
const dayAdd0 = timeAdd0(day);
|
||||||
|
const hourAdd0 = timeAdd0(hour);
|
||||||
|
const minuteAdd0 = timeAdd0(minute);
|
||||||
|
const secondAdd0 = timeAdd0(second);
|
||||||
|
let str = f.toString();
|
||||||
|
str = str.replace("YYYY", year);
|
||||||
|
if (f.includes("M")) {
|
||||||
|
if (f.includes("MM")) str = str.replace("MM", monthAdd0);
|
||||||
|
else str = str.replace("M", month + 1);
|
||||||
|
}
|
||||||
|
if (f.includes("D")) {
|
||||||
|
if (f.includes("DD")) str = str.replace("DD", dayAdd0);
|
||||||
|
else str = str.replace("D", day);
|
||||||
|
}
|
||||||
|
if (f.includes("h")) {
|
||||||
|
if (f.includes("h")) str = str.replace("hh", hourAdd0);
|
||||||
|
else str = str.replace("h", hour);
|
||||||
|
}
|
||||||
|
if (f.includes("m")) {
|
||||||
|
if (f.includes("mm")) str = str.replace("mm", minuteAdd0);
|
||||||
|
else str = str.replace("m", minute);
|
||||||
|
}
|
||||||
|
if (f.includes("s")) {
|
||||||
|
if (f.includes("ss")) str = str.replace("ss", secondAdd0);
|
||||||
|
else str = str.replace("s", second);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 补零函数 为时间服务 不足两位的自定在数字前面加上0
|
||||||
|
* @param {*} n 待补零数字
|
||||||
|
* @return {*} 补零后数字
|
||||||
|
*/
|
||||||
|
function timeAdd0(n) {
|
||||||
|
const s = n.toString();
|
||||||
|
return s.padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function deepclone(obj) {
|
||||||
|
let str, newobj = obj.constructor === Array ? [] : {};
|
||||||
|
if (typeof obj !== "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (window.JSON) {
|
||||||
|
str = JSON.stringify(obj), //系列化对象
|
||||||
|
newobj = JSON.parse(str); //还原
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i in obj) {
|
||||||
|
newobj[i] = typeof obj[i] === "object" ? deepclone(obj[i]) : obj[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTimeBydate(f) {
|
||||||
|
return formatTime(this, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(fn, delay) {
|
||||||
|
let timer = null;
|
||||||
|
return function () {
|
||||||
|
let _this = this;
|
||||||
|
let args = arguments;
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(_this, args);
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function throttle(fn, delay) {
|
||||||
|
let timer = null;
|
||||||
|
return function () {
|
||||||
|
let _this = this;
|
||||||
|
let args = arguments;
|
||||||
|
if (!timer) {
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(_this, args);
|
||||||
|
timer = null;
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字典查询 通个一个字段返回另一个字段的值
|
||||||
|
/**
|
||||||
|
* @description 字典查询
|
||||||
|
* @param {*} dict 字典数组
|
||||||
|
* @param {*} ckey 查询字段
|
||||||
|
* @param {*} cvalue 查询值
|
||||||
|
* @param {*} rkey 返回字段
|
||||||
|
* @return {*} 返回值
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getDictValue(dict, ckey, cvalue,rkey) {
|
||||||
|
let result = "";
|
||||||
|
dict.map(item => {
|
||||||
|
if (item[ckey] == cvalue) {
|
||||||
|
result = item[rkey];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
32
src/util/menu.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import orange from "@/icon/orange.svg";
|
||||||
|
import { NIcon } from "naive-ui";
|
||||||
|
import { RouterLink } from "vue-router";
|
||||||
|
|
||||||
|
const menuInfo = {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: () => "",
|
||||||
|
key: "0",
|
||||||
|
disabled: true,
|
||||||
|
icon: () => h(NIcon, { class: "orange" }, { default: () => h(orange) }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "1",
|
||||||
|
label: () => h(RouterLink, { to: "/home", class: "menu-item" }, { default: () => "首页" }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2",
|
||||||
|
label: () => h(RouterLink, { to: "/gallery", class: "menu-item" }, { default: () => "画廊" }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "3",
|
||||||
|
label: () => h(RouterLink, { to: "/article", class: "menu-item" }, { default: () => "文章" }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "4",
|
||||||
|
label: () => h(RouterLink, { to: "/plink", class: "menu-item" }, { default: () => "友链" }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
menuList: ["/home", "/gallery", "/article","/plink"],
|
||||||
|
};
|
||||||
|
export default menuInfo;
|
58
src/util/request.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import qs from 'qs';
|
||||||
|
import cookie from 'vue-cookies';
|
||||||
|
import router from '../router/index.js';
|
||||||
|
|
||||||
|
let baseURL = import.meta.env.VITE_APP_BASE_URL + '/api';
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = axios.create({
|
||||||
|
baseURL,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加请求拦截器
|
||||||
|
request.interceptors.request.use(
|
||||||
|
function (config) {
|
||||||
|
// 在发送请求之前做些什么
|
||||||
|
if (config.method == 'post' || config.method == 'POST') {
|
||||||
|
config.data = qs.stringify(config.data);
|
||||||
|
}
|
||||||
|
if (cookie.get('token') && cookie.get('token') != '') {
|
||||||
|
config.headers['Authorization'] = cookie.get('token');
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
async function (error) {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加响应拦截器
|
||||||
|
request.interceptors.response.use(
|
||||||
|
function (response) {
|
||||||
|
// const msg = useMessage()
|
||||||
|
// 2xx 范围内的状态码都会触发该函数。
|
||||||
|
// 对响应数据做点什么
|
||||||
|
if (response.data.code == 9) {
|
||||||
|
window.$msg.error('登录过期,请重新登录');
|
||||||
|
router.push('/login');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (response.data.code == 8) {
|
||||||
|
window.$msg.error(response.data.msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
async function (error) {
|
||||||
|
// 超出 2xx 范围的状态码都会触发该函数。
|
||||||
|
// 对响应错误做点什么
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export default request;
|
45
src/util/theme.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const themeOverrides = {
|
||||||
|
lightOverrides: {
|
||||||
|
common: {
|
||||||
|
primaryColor: "#8a2be2", //默认颜色
|
||||||
|
primaryColorHover: "#8a2be2", //hover颜色
|
||||||
|
primaryColorPressed: "#8a2be2", //点击颜色
|
||||||
|
successColor: "#409EFF",
|
||||||
|
successColorHover: "#409EFF",
|
||||||
|
successColorPressed: "#409EFF",
|
||||||
|
infoColor: "#8a2be2",
|
||||||
|
infoColorHover: "#8a2be2",
|
||||||
|
infoColorPressed: "#8a2be2",
|
||||||
|
},
|
||||||
|
Radio: {
|
||||||
|
buttonColorActive: "#FFA50040",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
darkOverrides: {
|
||||||
|
common: {
|
||||||
|
primaryColor: "#409EFF", //默认颜色
|
||||||
|
primaryColorHover: "#409EFF", //hover颜色
|
||||||
|
primaryColorPressed: "#409EFF", //点击颜色
|
||||||
|
successColor: "#409EFF",
|
||||||
|
successColorHover: "#409EFF",
|
||||||
|
successColorPressed: "#409EFF",
|
||||||
|
},
|
||||||
|
Menu: {
|
||||||
|
itemColorHover: "rgb(38, 52, 69)", //hover背景颜色
|
||||||
|
itemTextColor: "#777", //默认文字颜色
|
||||||
|
itemTextColorHover: "rgb(191, 203, 217)", ////hover文字颜色
|
||||||
|
arrowColor: "rgb(191, 203, 217)", //默认标签颜色
|
||||||
|
arrowColorHover: "rgb(191, 203, 217)", // hover 标签颜色
|
||||||
|
itemHeight: "56px", //item的高度
|
||||||
|
itemIconColor: "rgb(191, 203, 217)", //默认icon颜色
|
||||||
|
itemIconColorHover: "rgb(191, 203, 217)", //hover icon颜色
|
||||||
|
itemIconColorCollapsed: "rgb(191, 203, 217)", //收缩 icon颜色
|
||||||
|
itemColorActiveCollapsed: "rgb(38, 52, 69)",
|
||||||
|
},
|
||||||
|
Radio: {
|
||||||
|
buttonColorActive: "#409EFF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default themeOverrides;
|
39
src/views/HomeView.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<n-layout style="">
|
||||||
|
<n-layout-header ref="nav">
|
||||||
|
<navH />
|
||||||
|
</n-layout-header>
|
||||||
|
<n-scrollbar :style="cH">
|
||||||
|
<router>
|
||||||
|
<router-view />
|
||||||
|
</router>
|
||||||
|
</n-scrollbar>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import navH from "@/components/menu.vue";
|
||||||
|
import router from "@/components/router.vue";
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
|
const nav = ref(null);
|
||||||
|
let contH = ref(0);
|
||||||
|
let cH = ref({});
|
||||||
|
onMounted(() => {
|
||||||
|
getH();
|
||||||
|
});
|
||||||
|
function getH() {
|
||||||
|
contH.value = nav.value.$el.clientHeight;
|
||||||
|
cH.value = {
|
||||||
|
height: document.documentElement.clientHeight - contH.value + "px",
|
||||||
|
backgroundColor:"#f1f2f3"
|
||||||
|
};
|
||||||
|
// console.log(4, contH.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.n-layout-header) {
|
||||||
|
background-color: @dp;
|
||||||
|
}
|
||||||
|
</style>
|
35
src/views/article/index.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="box xxx" :style="{ height: `calc(100vh - ${size.menuH}px)` }">
|
||||||
|
<n-scrollbar class="pt-6" trigger="hover" :style="{ height: `calc(100vh - ${size.menuH}px)` }">
|
||||||
|
<arti :list="artList" />
|
||||||
|
</n-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import arti from '@/components/arti.vue';
|
||||||
|
import { usesizeStore } from "@/stores/size.js";
|
||||||
|
|
||||||
|
const size = usesizeStore();
|
||||||
|
const artList = ref([])
|
||||||
|
|
||||||
|
|
||||||
|
//mark 周期、内置函数等
|
||||||
|
onMounted(async () => {
|
||||||
|
const res = await $http.art.queryArt()
|
||||||
|
artList.value = res.data
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.n-layout-sider) {
|
||||||
|
width: unset !important;
|
||||||
|
max-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-scrollbar-rail) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
62
src/views/article/mini/index.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<n-scrollbar style="max-height: 100vh">
|
||||||
|
<div class="title">
|
||||||
|
{{ info.title }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="author">
|
||||||
|
{{ info.nickname }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cont">
|
||||||
|
<Editor v-model="info.cont" :defaultConfig="editorConfig" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time">--发表于 {{ formatTime(info.updated_at, "YYYY年MM月DD日hh时") }}</div>
|
||||||
|
</n-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { formatTime } from "@/util/index.js";
|
||||||
|
import { Editor } from "@wangeditor/editor-for-vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
const editorConfig = { readOnly: true };
|
||||||
|
const info = ref({updated_at:new Date()});
|
||||||
|
const route = useRoute();
|
||||||
|
// console.log(route.params.id);
|
||||||
|
getInfo(route.params.id);
|
||||||
|
async function getInfo(id) {
|
||||||
|
const res = await $http.art.queryArtInfo(id);
|
||||||
|
// console.log("**********", res);
|
||||||
|
info.value = res.data[0];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.title {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.author {
|
||||||
|
padding: 5px 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: @purple;
|
||||||
|
}
|
||||||
|
.cont {
|
||||||
|
width: 96%;
|
||||||
|
margin: 0px auto;
|
||||||
|
min-height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-style: italic;
|
||||||
|
text-align: right;
|
||||||
|
padding: 5px 30px;
|
||||||
|
}
|
||||||
|
:deep(#w-e-element-3) {
|
||||||
|
margin: unset !important;
|
||||||
|
}
|
||||||
|
</style>
|
176
src/views/console/article/index.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-8 mt-8 relative">
|
||||||
|
<h1 class="text-center text-lg">我的文章</h1>
|
||||||
|
<div class="absolute top-0 right-[5%]">
|
||||||
|
<n-button type="primary" @click="add">写文章</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table w-[95%] mx-auto">
|
||||||
|
<n-data-table :columns="columns" striped :single-line="false" :paginate-single-page="false" :pagination="pagination"
|
||||||
|
:data="artiList">
|
||||||
|
<template #empty>
|
||||||
|
<div>
|
||||||
|
<span>您还未发表过文章,</span><span class="text-pp-200 hover:underline" @click="add">去写一篇 ></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-data-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-drawer v-model:show="dshow" :width="702" :mask-closable="false">
|
||||||
|
<n-drawer-content :title="dtitle" closable>
|
||||||
|
<artiView v-if="show == 'view'" :id="opId" />
|
||||||
|
<edition v-if="show == 'add'" :id="-1" @close="handdleCloseEditor" />
|
||||||
|
<edition v-if="show == 'edit'" :id="curId" @close="handdleCloseEditor" />
|
||||||
|
</n-drawer-content>
|
||||||
|
</n-drawer>
|
||||||
|
|
||||||
|
<n-modal v-model:show="showModal" preset="dialog" title="删除" content="是否永久删除该文章?" positive-text="确认"
|
||||||
|
negative-text="取消" @positive-click="submitCallback" @negative-click="cancelCallback" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import edition from '@/components/edition.vue';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import artiView from './view.vue';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const dshow = ref(false)
|
||||||
|
const showModal = ref(false)
|
||||||
|
const artiList = ref([])
|
||||||
|
const pagination = ref({
|
||||||
|
pageSize: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dtitle = ref('');
|
||||||
|
const opId = ref(-1);
|
||||||
|
const curId = ref(-1);
|
||||||
|
const delId = ref(-1);
|
||||||
|
const show = ref('')
|
||||||
|
const columns = createColumns({
|
||||||
|
view: (row) => {
|
||||||
|
dshow.value = true
|
||||||
|
dtitle.value = '文章预览'
|
||||||
|
opId.value = row.id
|
||||||
|
show.value = 'view'
|
||||||
|
},
|
||||||
|
edit: (row) => {
|
||||||
|
dshow.value = true
|
||||||
|
dtitle.value = '编辑文章'
|
||||||
|
curId.value = row.id
|
||||||
|
show.value = 'edit'
|
||||||
|
},
|
||||||
|
remove: (row) => {
|
||||||
|
showModal.value = true
|
||||||
|
delId.value = row.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function createColumns({ edit, remove, view }) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
key: 'key',
|
||||||
|
align: 'center',
|
||||||
|
width: "80",
|
||||||
|
render: (_, index) => {
|
||||||
|
return `${index + 1}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "标题",
|
||||||
|
align: 'center',
|
||||||
|
width: "300",
|
||||||
|
key: "title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "简介",
|
||||||
|
align: 'center',
|
||||||
|
key: "pro",
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
align: 'center',
|
||||||
|
key: "actions",
|
||||||
|
width: "200",
|
||||||
|
render(row) {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
[
|
||||||
|
h(NButton,
|
||||||
|
{
|
||||||
|
type: "primary",
|
||||||
|
text: true,
|
||||||
|
size: "small",
|
||||||
|
onClick: () => view(row)
|
||||||
|
},
|
||||||
|
{ default: () => "预览" }
|
||||||
|
),
|
||||||
|
h(NButton,
|
||||||
|
{
|
||||||
|
type: "primary",
|
||||||
|
text: true,
|
||||||
|
size: "small",
|
||||||
|
style: "margin-left: 10px",
|
||||||
|
onClick: () => edit(row)
|
||||||
|
},
|
||||||
|
{ default: () => "编辑" }
|
||||||
|
),
|
||||||
|
h(NButton,
|
||||||
|
{
|
||||||
|
type: "primary",
|
||||||
|
text: true,
|
||||||
|
style: "margin-left: 10px",
|
||||||
|
size: "small",
|
||||||
|
onClick: () => remove(row)
|
||||||
|
},
|
||||||
|
{ default: () => "删除" }
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
function add() {
|
||||||
|
dshow.value = true
|
||||||
|
dtitle.value = '新建文章'
|
||||||
|
show.value = 'add'
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
async function getList() {
|
||||||
|
// 获取文章列表
|
||||||
|
const res = await $http.art.myArti()
|
||||||
|
artiList.value = res.data
|
||||||
|
}
|
||||||
|
function handdleCloseEditor() {
|
||||||
|
dshow.value = false
|
||||||
|
show.value = ''
|
||||||
|
dtitle.value = ''
|
||||||
|
curId.value = -1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
async function submitCallback() {
|
||||||
|
const res = await $http.art.deleteArti(delId.value)
|
||||||
|
if (res.code == 1) $msg.success(res.msg)
|
||||||
|
else $msg.error(res.msg)
|
||||||
|
showModal.value = false
|
||||||
|
delId.value = -1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelCallback() {
|
||||||
|
showModal.value = false
|
||||||
|
delId.value = -1
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
67
src/views/console/article/view.vue
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<n-scrollbar style="max-height: 100vh">
|
||||||
|
<div class="title">
|
||||||
|
{{ info.title }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="author">
|
||||||
|
{{ info.nickname }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cont">
|
||||||
|
<Editor v-model="info.cont" :defaultConfig="editorConfig" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time">--发表于 {{ formatTime(info.updated_at, "YYYY年MM月DD日hh时") }}</div>
|
||||||
|
</n-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { formatTime } from "@/util/index.js";
|
||||||
|
import { Editor } from "@wangeditor/editor-for-vue";
|
||||||
|
|
||||||
|
const editorConfig = { readOnly: true };
|
||||||
|
const info = ref({updated_at:new Date()});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: [String,Number],
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
getInfo(props.id);
|
||||||
|
async function getInfo(id) {
|
||||||
|
const res = await $http.art.queryArtInfo(id);
|
||||||
|
// console.log("**********", res);
|
||||||
|
info.value = res.data[0];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.title {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.author {
|
||||||
|
padding: 5px 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: @purple;
|
||||||
|
}
|
||||||
|
.cont {
|
||||||
|
width: 96%;
|
||||||
|
margin: 0px auto;
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-style: italic;
|
||||||
|
text-align: right;
|
||||||
|
padding: 5px 30px;
|
||||||
|
}
|
||||||
|
:deep(#w-e-element-3) {
|
||||||
|
margin: unset !important;
|
||||||
|
}
|
||||||
|
</style>
|
317
src/views/console/gallery/index.vue
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
<template>
|
||||||
|
<div class="div w-[100%]" ref="gw">
|
||||||
|
<div class="list px-3">
|
||||||
|
<n-upload style="height: 200px; width: 50%" multiple directory-dnd action="https://www.hxyouzi.com/api/file/upload"
|
||||||
|
accept="image/*" :headers="{ Authorization: token }" :show-file-list="false" :on-finish="fileFinish">
|
||||||
|
<n-upload-dragger>
|
||||||
|
<div style="margin-bottom: 12px">
|
||||||
|
<n-icon size="48" class="up">
|
||||||
|
<MdCloudUpload />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<n-text style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </n-text>
|
||||||
|
<n-p depth="3" style="margin: 8px 0 0 0"> 请不要上传大于5m的图片</n-p>
|
||||||
|
</n-upload-dragger>
|
||||||
|
</n-upload>
|
||||||
|
|
||||||
|
|
||||||
|
<falls :fileList="list" :count="acount" :bot="bottomLen" pathname="filepath" v-slot="{ file }">
|
||||||
|
<div class="img mb-1" @contextmenu="handdlemenu($event, file)" @longpress="handdlemenu($event, file)">
|
||||||
|
<n-image height="200px" :src="file.filepath" object-fit="fill" :img-props="{ loading: 'lazy' }" />
|
||||||
|
</div>
|
||||||
|
</falls>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="munur" v-if="menuShow" :style="{ position: 'fixed', top: y + 'px', left: x + 'px' }">
|
||||||
|
<template v-for="i in menuList">
|
||||||
|
<n-button v-if="i.show" type="primary" :tertiary="!i.active" @click="handdleclickmenu(i.id)"
|
||||||
|
@mouseenter="handdleentermenu(i.id)" @mouseleave="handdleleavemenu(i.id)">{{ i.name
|
||||||
|
}}</n-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<n-modal v-model:show="showModal" preset="dialog" title="Dialog">
|
||||||
|
<template #header>
|
||||||
|
<div>提示</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div style="text-align: center">{{ currentImg.filename }}</div>
|
||||||
|
<n-form-item label="">
|
||||||
|
<n-input v-model:value="fileName" placeholder="请输入新的文件名" @keyup.enter="" />
|
||||||
|
</n-form-item>
|
||||||
|
</div>
|
||||||
|
<template #action>
|
||||||
|
<n-button @click="handdleClose">取消</n-button>
|
||||||
|
<n-button type="primary" @click="updatename">确认</n-button>
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import falls from '@/components/falls.vue';
|
||||||
|
import { MdCloudUpload } from "@vicons/ionicons4";
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
|
||||||
|
|
||||||
|
const acount = ref(2)
|
||||||
|
const bottomLen = ref(20)
|
||||||
|
// console.log(4444, size.conH);
|
||||||
|
const list = ref([]);
|
||||||
|
const token = cookie.get("token");
|
||||||
|
function fileFinish(file) {
|
||||||
|
const res = JSON.parse(file.event.currentTarget.response);
|
||||||
|
// console.log(res);
|
||||||
|
if (res.code == 1) {
|
||||||
|
$msg.success(res.msg);
|
||||||
|
getMyfile();
|
||||||
|
} else {
|
||||||
|
$msg.error(res.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMyfile() {
|
||||||
|
list.value = [];
|
||||||
|
const res = await $http.file.MyFile();
|
||||||
|
list.value = res.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMyfile();
|
||||||
|
});
|
||||||
|
|
||||||
|
const menuShow = ref(false);
|
||||||
|
const x = ref(0);
|
||||||
|
const y = ref(0);
|
||||||
|
let imgTimer
|
||||||
|
const menuList = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "修改文件名",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "分享",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "取消分享",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "下载",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "删除",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const fileName = ref("");
|
||||||
|
const showModal = ref(false);
|
||||||
|
const currentImg = ref({});
|
||||||
|
function handdlemenu(e, img) {
|
||||||
|
menuShow.value = false
|
||||||
|
const { clientX, clientY } = e;
|
||||||
|
x.value = clientX;
|
||||||
|
y.value = clientY;
|
||||||
|
menuShow.value = true;
|
||||||
|
menuList.value[1].show = !img.share;
|
||||||
|
menuList.value[2].show = !!img.share;
|
||||||
|
currentImg.value = img;
|
||||||
|
console.log(img);
|
||||||
|
imgTimer && clearTimeout(imgTimer)
|
||||||
|
imgTimer = setTimeout(() => {
|
||||||
|
menuShow.value = false
|
||||||
|
resetMenuList()
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
function handdleclickmenu(id) {
|
||||||
|
switch (id) {
|
||||||
|
case 1:
|
||||||
|
showModal.value = true;
|
||||||
|
menuShow.value = false;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
share(currentImg.value.id)
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
unshare(currentImg.value.id)
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
downloadImg(currentImg.value)
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
remove(currentImg.value.id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resetMenuList()
|
||||||
|
}
|
||||||
|
function handdleentermenu(id) {
|
||||||
|
menuList.value[id - 1].active = true;
|
||||||
|
imgTimer && clearTimeout(imgTimer)
|
||||||
|
}
|
||||||
|
function handdleleavemenu(id) {
|
||||||
|
menuList.value[id - 1].active = false;
|
||||||
|
imgTimer = setTimeout(() => {
|
||||||
|
menuShow.value = false
|
||||||
|
resetMenuList()
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMenuList() {
|
||||||
|
menuList.value = ([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "修改文件名",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "分享",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "取消分享",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "下载",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "删除",
|
||||||
|
active: false,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
async function share(id) {
|
||||||
|
menuShow.value = false;
|
||||||
|
const res = await $http.file.updateFile(id);
|
||||||
|
res.code ? $msg.success(res.msg) : $msg.error(res.msg);
|
||||||
|
getMyfile();
|
||||||
|
}
|
||||||
|
async function unshare(id) {
|
||||||
|
menuShow.value = false;
|
||||||
|
const res = await $http.file.unUpdateFile(id);
|
||||||
|
res.code ? $msg.success(res.msg) : $msg.error(res.msg);
|
||||||
|
getMyfile()
|
||||||
|
}
|
||||||
|
async function remove(id) {
|
||||||
|
menuShow.value = false;
|
||||||
|
const res = await $http.file.delFile(id);
|
||||||
|
res.code ? $msg.success(res.msg) : $msg.error(res.msg);
|
||||||
|
getMyfile()
|
||||||
|
}
|
||||||
|
async function downloadImg(img) {
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.download = img.filename;
|
||||||
|
a.href = img.filepath;
|
||||||
|
a.target = "_BLANK";
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
function handdleClose() {
|
||||||
|
fileName.value = "";
|
||||||
|
showModal.value = false;
|
||||||
|
}
|
||||||
|
async function updatename() {
|
||||||
|
if (fileName.value != "") {
|
||||||
|
const res = await $http.file.updateName({ id: currentImg.value.id, name: fileName.value });
|
||||||
|
res.code ? $msg.success(res.msg) : $msg.error(res.msg);
|
||||||
|
handdleClose();
|
||||||
|
getMyfile()
|
||||||
|
} else {
|
||||||
|
$msg.error("文件名不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pw = 200
|
||||||
|
const gw = ref(null)
|
||||||
|
function arrCount() {
|
||||||
|
const cw = gw.value.clientWidth
|
||||||
|
if (cw < 750) {
|
||||||
|
acount.value = 2
|
||||||
|
// pivW.value = 180
|
||||||
|
bottomLen.value = 10
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// pivW.value = 200
|
||||||
|
bottomLen.value = 20
|
||||||
|
acount.value = Math.floor(cw * 0.95 / (pw + 20))
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
arrCount()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.up.n-icon) {
|
||||||
|
color: @purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-upload-dragger) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.munur {
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:deep(.n-image) {
|
||||||
|
display: flex;
|
||||||
|
img {
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: @ps;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
}
|
||||||
|
</style>
|
7
src/views/console/home/index.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div>控制台首页。</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
98
src/views/console/index.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<n-layout has-sider>
|
||||||
|
<n-layout-sider ref="sider" bordered collapse-mode="width" inverted :collapsed-width="64" show-trigger :width="240" :collapsed="collapsed" :show-trigger="false" @collapse="collapsed = true" @expand="collapsed = false">
|
||||||
|
<div ref="consoleSiderHeader" class="" @click="collapsed = false">
|
||||||
|
<n-menu :collapsed="collapsed" inverted :collapsed-width="64" :icon-size="20" :collapsed-icon-size="26" :options="menuOptions" />
|
||||||
|
</div>
|
||||||
|
<div class="menu">
|
||||||
|
<console-menu :collapsed="collapsed" />
|
||||||
|
</div>
|
||||||
|
</n-layout-sider>
|
||||||
|
<n-layout>
|
||||||
|
<n-layout-header :style="{ height: navH + 'px' }">
|
||||||
|
<div class="header bg-[#f2e8fc] flex items-center justify-between">
|
||||||
|
<div class="index ml-4">{{ route.meta.name }}</div>
|
||||||
|
<div class="info flex items-center">
|
||||||
|
<div class="wea">
|
||||||
|
<wea></wea>
|
||||||
|
</div>
|
||||||
|
<div class="my">
|
||||||
|
<div class="uinfo mx-8 text-white flex items-center">
|
||||||
|
<n-avatar round size="small" :src="userinfo.avaUrl" />
|
||||||
|
<div class="ml-2"></div>
|
||||||
|
<n-button text class="userinfo">
|
||||||
|
{{ userinfo.nickname }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<n-button color="#8a2be2" size="small" class="mr-4" @click="goHome"> 回首页 </n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-layout-header>
|
||||||
|
<n-layout-content :content-style="cst" :native-scrollbar="false">
|
||||||
|
<router-view></router-view>
|
||||||
|
</n-layout-content>
|
||||||
|
</n-layout>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import consoleMenu from "@/components/consoleMenu.vue";
|
||||||
|
import wea from "@/components/wea.vue";
|
||||||
|
import { usesizeStore } from "@/stores/size.js";
|
||||||
|
import { MdMenu } from "@vicons/ionicons4";
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const size = usesizeStore();
|
||||||
|
const userinfo = cookie.get("userinfo");
|
||||||
|
const collapsed = ref(false);
|
||||||
|
let consoleSiderHeader = ref(null);
|
||||||
|
let navH = ref(0);
|
||||||
|
const cst = ref({});
|
||||||
|
|
||||||
|
const menuOptions = [
|
||||||
|
{
|
||||||
|
label: "控制台",
|
||||||
|
key: "0",
|
||||||
|
disabled: true,
|
||||||
|
icon: () => h(NIcon, { class: "console-sider-header" }, { default: () => h(MdMenu) }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
navH.value = consoleSiderHeader.value.clientHeight;
|
||||||
|
size.setConH(document.documentElement.clientHeight - navH.value);
|
||||||
|
cst.value = {
|
||||||
|
height: document.documentElement.clientHeight - navH.value + "px",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function goHome() {
|
||||||
|
router.replace("/");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
:deep(.n-layout-sider) {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.col {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.nocol {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
:deep(.n-menu-item-content--disabled) {
|
||||||
|
opacity: unset !important;
|
||||||
|
}
|
||||||
|
:deep(.console-sider-header) {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
98
src/views/console/profile/index.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<div class="list">
|
||||||
|
<div class="row flex items-center">
|
||||||
|
<div class="label">头像</div>
|
||||||
|
<n-avatar round :src="userinfo.avaUrl" />
|
||||||
|
<div class="edit" @click="showModal = true">更换</div>
|
||||||
|
</div>
|
||||||
|
<div class="row flex items-center mt-4">
|
||||||
|
<div class="label">手机号</div>
|
||||||
|
<div>{{ userinfo.tel }} <span v-if="!userinfo.tel" class="text-gray-500 text-[12px] underline italic">尚未绑定手机号</span>
|
||||||
|
</div>
|
||||||
|
<div class="edit" @click="showTelModal = true">更换</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-modal :closable="false" :show="showModal" title="更改头像" @mask-click="showModal = false" @esc="showModal = false"
|
||||||
|
@negative-click="showModal = false" preset="dialog" negative-text="取消">
|
||||||
|
<div style="margin: 10px 0; width: 100%; display: flex; justify-content: center">
|
||||||
|
<n-avatar round :size="80" :src="userinfo.avaUrl" fallback-src="https://www.hxyouzi.com/gin/assets/ava/默认.png" />
|
||||||
|
</div>
|
||||||
|
<n-upload action="https://www.hxyouzi.com/api/info/update/avater" :headers="{ Authorization: token }"
|
||||||
|
:show-file-list="false" :on-finish="fileFinish" accept=".jpg,.jpeg,.png,.gif,.jfif"
|
||||||
|
style="width: 100%; display: flex; justify-content: center">
|
||||||
|
<n-button type="primary">上传头像</n-button>
|
||||||
|
</n-upload>
|
||||||
|
</n-modal>
|
||||||
|
|
||||||
|
<n-modal :closable="false" :show="showTelModal" title="更改手机号" @mask-click="showTelModal = false"
|
||||||
|
@esc="showTelModal = false" @negative-click="showTelModal = false" preset="dialog" negative-text="取消"
|
||||||
|
positive-text="确认" @positive-click="submitCallback">
|
||||||
|
<n-input v-model:value="tel" type="text" :placeholder="userinfo.tel || '请输入手机号'" clearable></n-input>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import cookie from "vue-cookies";
|
||||||
|
|
||||||
|
|
||||||
|
let userinfo = ref(cookie.get("userinfo"));
|
||||||
|
let token = ref(cookie.get("token"));
|
||||||
|
const showModal = ref(false);
|
||||||
|
const showTelModal = ref(false);
|
||||||
|
const tel = ref('')
|
||||||
|
|
||||||
|
|
||||||
|
function fileFinish(file) {
|
||||||
|
const res = JSON.parse(file.event.currentTarget.response);
|
||||||
|
// console.log(res);
|
||||||
|
if (res.code == 1) {
|
||||||
|
$msg.success(res.msg);
|
||||||
|
autologin();
|
||||||
|
} else {
|
||||||
|
$msg.error(res.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function autologin() {
|
||||||
|
const res = await $http.user.alogin();
|
||||||
|
if (res.code == 1) {
|
||||||
|
cookie.set("token", res.token, "1d");
|
||||||
|
cookie.set("userinfo", res.data, "1d");
|
||||||
|
userinfo.value = cookie.get("userinfo");
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function submitCallback() {
|
||||||
|
if (!tel.value) return
|
||||||
|
if (!/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(tel.value)) {
|
||||||
|
$msg.error('请输入正确的手机号!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await $http.user.updatetel({ tel: tel.value })
|
||||||
|
if (res.code == 1) {
|
||||||
|
$msg.success(res.msg)
|
||||||
|
showTelModal.value = false
|
||||||
|
}
|
||||||
|
else $msg.error(res.msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.label {
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
// padding: 0 10px;
|
||||||
|
margin-left: 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: @blue;
|
||||||
|
cursor: url("@/cursor/link_26x26.png"), pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit:hover {
|
||||||
|
color: @purple;
|
||||||
|
}</style>
|
101
src/views/gallery/index.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<n-scrollbar class="bg-[#f8f9fa]" :on-scroll="onScroll" :style="{ maxHeight: contH + 'px' }">
|
||||||
|
<div class="w-[40vw] mx-auto my-6 md:w-[80vw]">
|
||||||
|
<n-input-group>
|
||||||
|
<n-input v-model:value="kw" placeholder="图片名 | 分享人" @keyup.enter.native="search" :style="{ width: '80%' }" />
|
||||||
|
<n-button :style="{ width: '20%' }" type="info" @click="search"> 搜索 </n-button>
|
||||||
|
</n-input-group>
|
||||||
|
</div>
|
||||||
|
<div class="list">
|
||||||
|
<xPic :fileList="sharelist"></xPic>
|
||||||
|
</div>
|
||||||
|
<n-back-top :right="20" />
|
||||||
|
</n-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { usesizeStore } from '@/stores/size.js';
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import xPic from './xPic.vue';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const kw = ref("");
|
||||||
|
const sharelist = ref([]);
|
||||||
|
const ps = ref(20), pn = ref(1), total = ref(0)
|
||||||
|
|
||||||
|
const size = usesizeStore()
|
||||||
|
const contH = ref(0)
|
||||||
|
|
||||||
|
let reqLock = false // 请求锁
|
||||||
|
onMounted(() => {
|
||||||
|
getSharefile();
|
||||||
|
contH.value = document.documentElement.clientHeight - size.menuH - 10
|
||||||
|
})
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
sharelist.value = [];
|
||||||
|
pn.value = 1
|
||||||
|
getSharefile()
|
||||||
|
}
|
||||||
|
async function getSharefile() {
|
||||||
|
if (reqLock) return
|
||||||
|
reqLock = true
|
||||||
|
const res = await $http.file.ShareFile({
|
||||||
|
keyword: kw.value,
|
||||||
|
page_size: ps.value,
|
||||||
|
page_num: pn.value,
|
||||||
|
});
|
||||||
|
total.value = res.total
|
||||||
|
res.data.forEach(i => i.ishow = false)
|
||||||
|
sharelist.value = res.data;
|
||||||
|
setTimeout(() => {
|
||||||
|
reqLock = false
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 触底加载新内容
|
||||||
|
|
||||||
|
let lastTop = 0 // 记录上一次滚动距离,方便判断滚动的方向
|
||||||
|
|
||||||
|
|
||||||
|
function onScroll(e) {
|
||||||
|
if (reqLock) return
|
||||||
|
if (ps.value * pn.value >= total.value) return
|
||||||
|
let st = e.target.scrollTop
|
||||||
|
let ch = e.target.clientHeight
|
||||||
|
let sh = e.target.scrollHeight
|
||||||
|
|
||||||
|
if (st > lastTop) {
|
||||||
|
if (st + ch + 200 >= sh) {
|
||||||
|
// reqLock = false
|
||||||
|
pn.value++
|
||||||
|
getSharefile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTop = st
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search {
|
||||||
|
padding: 0 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
margin-top: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space::after {
|
||||||
|
height: 0;
|
||||||
|
flex: auto;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
</style>
|
184
src/views/gallery/xPic.vue
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div class="box flex justify-center" ref="curcomp">
|
||||||
|
<falls :fileList="fileList" :count="acount" :bot="bottomLen" pathname="filepath" v-slot="{ file }">
|
||||||
|
<div class="img" @mouseenter="movein(file)" @mouseleave="moveout(file)">
|
||||||
|
<n-image width="200px" :imgW="pivW" :src="file.filepath" object-fit="cover" :img-props="{ loading: 'lazy' }" />
|
||||||
|
<div class="title w-[120px]" v-if="!ismobile && file.ishow">
|
||||||
|
<n-ellipsis style="max-width: 120px" :line-clamp="1">{{ file.filename }}</n-ellipsis>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip" v-if="ismobile || file.ishow">
|
||||||
|
<div class="block">
|
||||||
|
<div class="uploadBy flex items-center">
|
||||||
|
<n-ellipsis :line-clamp="1">由{{ file.nickname }}分享</n-ellipsis>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<n-tooltip trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button text size="small" @click="downloadImg(file)">
|
||||||
|
<n-icon size="20">
|
||||||
|
<MdDownload />
|
||||||
|
</n-icon>
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
下载
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</falls>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import falls from '@/components/falls.vue';
|
||||||
|
import { MdDownload } from "@vicons/ionicons4";
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
|
||||||
|
const pivW = ref(200);
|
||||||
|
const prop = defineProps({
|
||||||
|
fileList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { fileList } = toRefs(prop);
|
||||||
|
const pw = 200
|
||||||
|
const acount = ref(2)
|
||||||
|
const bottomLen = ref(20)
|
||||||
|
const ismobile = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
arrCount()
|
||||||
|
window.addEventListener("resize", arrCount)
|
||||||
|
|
||||||
|
function movein(file) {
|
||||||
|
if (ismobile.value) return
|
||||||
|
file.ishow = true
|
||||||
|
}
|
||||||
|
function moveout(file) {
|
||||||
|
if (ismobile.value) return
|
||||||
|
file.ishow = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadImg(img) {
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.download = img.filename;
|
||||||
|
a.href = img.filepath;
|
||||||
|
a.target = "_BLANK";
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrCount() {
|
||||||
|
// console.log('w=>', document.documentElement.clientWidth);
|
||||||
|
const cw = document.documentElement.clientWidth
|
||||||
|
if (cw < 1000) {
|
||||||
|
acount.value = 2
|
||||||
|
pivW.value = 180
|
||||||
|
bottomLen.value = 10
|
||||||
|
ismobile.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pivW.value = 200
|
||||||
|
bottomLen.value = 20
|
||||||
|
ismobile.value = false
|
||||||
|
acount.value = Math.floor(cw * 0.95 / (pw + 20))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.img {
|
||||||
|
// width: 100%;
|
||||||
|
// height: 200px;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: @ps;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #00000080;
|
||||||
|
position: absolute;
|
||||||
|
color: #ddd;
|
||||||
|
top: 0px;
|
||||||
|
display: flex;
|
||||||
|
font-size: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: url("@/cursor/link_26x26.png"), pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #00000080;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
bottom: 0px;
|
||||||
|
cursor: url("@/cursor/link_26x26.png"), pointer !important;
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-icon:hover {
|
||||||
|
color: @purple !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadBy {
|
||||||
|
font-size: 12px;
|
||||||
|
color: @orange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-space)>div,
|
||||||
|
.block {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-image) {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: @ps;
|
||||||
|
display: flex;
|
||||||
|
img {
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
// margin-right: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:1000px) {
|
||||||
|
:deep(.n-image) {
|
||||||
|
img {
|
||||||
|
width: 180px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
// margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
54
src/views/home/index.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="box xxx" :style="{ height: `calc(100vh - ${size.menuH}px)` }">
|
||||||
|
<n-layout>
|
||||||
|
<n-layout has-sider>
|
||||||
|
<n-layout-content class="sm:w-[90vh] w-[70%]" :content-style="{ height: `calc(100vh - ${size.menuH}px)`,backgroundColor:'#f1f2f3' }">
|
||||||
|
<n-scrollbar class="pt-6" trigger="hover" :style="{ height: `calc(100vh - ${size.menuH}px)` }">
|
||||||
|
<search></search>
|
||||||
|
<NavMenu />
|
||||||
|
</n-scrollbar>
|
||||||
|
</n-layout-content>
|
||||||
|
<n-layout-sider class="sm:hidden" :content-style="{width: '30vw',height: `calc(100vh - ${size.menuH}px)`,overflow:'hidden',backgroundColor:'#f1f2f3' }">
|
||||||
|
<Suspense>
|
||||||
|
<side />
|
||||||
|
</Suspense>
|
||||||
|
</n-layout-sider>
|
||||||
|
</n-layout>
|
||||||
|
</n-layout>
|
||||||
|
</div>
|
||||||
|
<aplayerView></aplayerView>
|
||||||
|
<foot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import aplayerView from "@/components/aplayerView.vue";
|
||||||
|
import foot from "@/components/foot.vue";
|
||||||
|
import { usesizeStore } from "@/stores/size.js";
|
||||||
|
import NavMenu from "./navMenu.vue";
|
||||||
|
import search from './search.vue';
|
||||||
|
import side from './side.vue';
|
||||||
|
|
||||||
|
const size = usesizeStore();
|
||||||
|
const artList = ref([])
|
||||||
|
|
||||||
|
|
||||||
|
//mark 周期、内置函数等
|
||||||
|
onMounted(async () => {
|
||||||
|
const res = await $http.art.queryArt()
|
||||||
|
artList.value = res.data
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
:deep(.n-layout-sider) {
|
||||||
|
width: unset !important;
|
||||||
|
max-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-scrollbar-rail) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
159
src/views/home/navMenu.vue
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="box flex flex-wrap pl-[2%] mt-8">
|
||||||
|
<div class="nav-card w-[300px] mx-[2%] mb-6 p-2 bg-[#fafbfc]" v-for="(item, index) in navList" :key="index">
|
||||||
|
<div class="t" @click="expand(index)">
|
||||||
|
<n-divider title-placement="center">
|
||||||
|
<div class="flex items-center font-bold">
|
||||||
|
{{ item.menuClass }}
|
||||||
|
<n-icon size="24" class="tiao mx-2">
|
||||||
|
<icon-more />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</n-divider>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap pt-3" v-show="item.exp">
|
||||||
|
<div class="w-[45%] ml-[3%] mb-2" v-for="(it, idx) in item.children" :key="index">
|
||||||
|
<!-- <n-popover trigger="hover"> -->
|
||||||
|
<!-- <template #trigger> -->
|
||||||
|
<n-button class="w-[100%] text-[12px] text-ellipsis hover:bg-gradient-to-r hover:from-pp-100 hover:to-pp-400 font-bold" ghost color="#B172EC" @click="link(it.menuLink)">
|
||||||
|
<!-- <n-button class="w-[100%] text-[12px] text-ellipsis" ghost color="#ff69b4" @click="link(it.menuLink)"> -->
|
||||||
|
{{ it.menuName }}
|
||||||
|
</n-button>
|
||||||
|
<!-- </template> -->
|
||||||
|
<!-- <span class="text-[12px]">{{ it.menuName }}</span> -->
|
||||||
|
<!-- </n-popover> -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
|
//mark import
|
||||||
|
|
||||||
|
//mark data
|
||||||
|
const navList = ref([])
|
||||||
|
const active = ref(-1)
|
||||||
|
//mark method
|
||||||
|
function formatNav(list) {
|
||||||
|
let menu = []
|
||||||
|
let result = []
|
||||||
|
list.forEach(i => {
|
||||||
|
if (!menu.includes(i.menuClass)) {
|
||||||
|
menu.push(i.menuClass)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menu.forEach(i => {
|
||||||
|
result.push({
|
||||||
|
menuClass: i,
|
||||||
|
children: list.filter(it => it.menuClass == i),
|
||||||
|
exp: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
console.log(result);
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function expand(index) {
|
||||||
|
console.log(index, active.value);
|
||||||
|
if (active.value == index) {
|
||||||
|
navList.value[index].exp = !navList.value[index].exp
|
||||||
|
} else {
|
||||||
|
navList.value.forEach(i => {
|
||||||
|
i.exp = false
|
||||||
|
})
|
||||||
|
navList.value[index].exp = true
|
||||||
|
}
|
||||||
|
|
||||||
|
active.value = index
|
||||||
|
}
|
||||||
|
function link(url) {
|
||||||
|
window.open(url, "_blank")
|
||||||
|
}
|
||||||
|
//mark 周期、内置函数等
|
||||||
|
onMounted(async () => {
|
||||||
|
const res = await $http.nav.listNav()
|
||||||
|
navList.value = formatNav(res.data)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.nav-card {
|
||||||
|
box-shadow: @ps;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
20%,
|
||||||
|
50%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
60% {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiao {
|
||||||
|
animation: bounce 1s infinite;
|
||||||
|
/* 播放名为bounce的动画,循环无限次,每次动画时长1秒 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-divider) {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-button__content) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-button):hover {
|
||||||
|
// background-color: @purple !important;
|
||||||
|
border: none !important;
|
||||||
|
color: white !important;
|
||||||
|
animation: swing .8s ease-in-out 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes swing {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
20%,
|
||||||
|
50%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: translateX(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
60% {
|
||||||
|
transform: translateX(3px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|