feat: 笔记
This commit is contained in:
commit
d8dc41da44
3
.browserslistrc
Normal file
3
.browserslistrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
8
.env.development
Normal file
8
.env.development
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
ENV = 'development'
|
||||||
|
|
||||||
|
# 开发环境
|
||||||
|
VUE_APP_BASE_API = '/dev-api'
|
||||||
|
|
||||||
|
# 开发环境websocket地址
|
||||||
|
VUE_APP_SOCKET = 'ws://localhost:8068/websocket'
|
8
.env.production
Normal file
8
.env.production
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# 生产环境配置
|
||||||
|
ENV = 'production'
|
||||||
|
|
||||||
|
# 生产环境
|
||||||
|
VUE_APP_BASE_API = '/prod-api'
|
||||||
|
|
||||||
|
# 生产环境websocket地址
|
||||||
|
VUE_APP_SOCKET = 'wss://pnkx.top/websocket/websocket'
|
7
.env.staging
Normal file
7
.env.staging
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
NODE_ENV = production
|
||||||
|
|
||||||
|
# 测试环境配置
|
||||||
|
ENV = 'staging'
|
||||||
|
|
||||||
|
# 若依管理系统/测试环境
|
||||||
|
VUE_APP_BASE_API = '/stage-api'
|
17
.eslintrc.js
Normal file
17
.eslintrc.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/essential',
|
||||||
|
'@vue/standard'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
}
|
||||||
|
}
|
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 聊天室
|
||||||
|
|
||||||
|
## 预览
|
||||||
|
![输入图片说明](src/assets/images/image.png)
|
||||||
|
![输入图片说明](src/assets/images/image1.png)
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
43
package.json
Normal file
43
package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "code",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint",
|
||||||
|
"start": "node index.js",
|
||||||
|
"server": "nodemon index.js --ignore client"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"element-ui": "^2.13.2",
|
||||||
|
"es6-promise": "^4.2.8",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
|
"sass-loader": "^8.0.0",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-router": "^3.2.0",
|
||||||
|
"quill": "1.3.7",
|
||||||
|
"vue-quill-editor": "^3.0.6",
|
||||||
|
"vuex": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"sass": "1.32.0",
|
||||||
|
"sass-loader": "10.1.0",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
18
public/index.html
Normal file
18
public/index.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title>chat</title>
|
||||||
|
<script src="https://pv.sohu.com/cityjson?ie=utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
37
src/App.vue
Normal file
37
src/App.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<chat/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chat from '@/components/Note/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
Chat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
html, body{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-image: url('./assets/main-bg.png');
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
#app {
|
||||||
|
width: 60vw;
|
||||||
|
background-color: #FFF;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
132
src/api/index.js
Normal file
132
src/api/index.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询笔记列表
|
||||||
|
export function listNote(query) {
|
||||||
|
return request({
|
||||||
|
url: '/note/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询笔记详细
|
||||||
|
export function getNote(id) {
|
||||||
|
return request({
|
||||||
|
url: '/note/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询笔记详细(白名单)
|
||||||
|
export function getNoteWhite(id) {
|
||||||
|
return request({
|
||||||
|
url: '/customer/note/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增笔记
|
||||||
|
export function addNote(data) {
|
||||||
|
return request({
|
||||||
|
url: '/note',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改笔记
|
||||||
|
export function updateNote(data) {
|
||||||
|
return request({
|
||||||
|
url: '/note',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除笔记
|
||||||
|
export function delNote(id) {
|
||||||
|
return request({
|
||||||
|
url: '/note/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出笔记
|
||||||
|
export function exportNote(query) {
|
||||||
|
return request({
|
||||||
|
url: '/note/export',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 查询笔记文件夹列表
|
||||||
|
export function listFolder(query) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询笔记文件夹树形列表
|
||||||
|
export function treeList(query) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder/treeList',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 查询笔记文件夹详细
|
||||||
|
export function getFolder(id) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增笔记文件夹
|
||||||
|
export function addFolder(data) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改笔记文件夹
|
||||||
|
export function updateFolder(data) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除笔记文件夹
|
||||||
|
export function delFolder(id) {
|
||||||
|
return request({
|
||||||
|
url: '/note/folder/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 根据字典类型查询字典数据信息
|
||||||
|
export function getDicts (dictType) {
|
||||||
|
return request({
|
||||||
|
url: '/client/dictType/' + dictType,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户详细信息
|
||||||
|
export function getInfo () {
|
||||||
|
return request({
|
||||||
|
url: '/getInfo',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
9
src/assets/icons/index.js
Normal file
9
src/assets/icons/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import SvgIcon from '@/components/SvgIcon'// svg component
|
||||||
|
|
||||||
|
// register globally
|
||||||
|
Vue.component('svg-icon', SvgIcon)
|
||||||
|
|
||||||
|
const req = require.context('./svg', false, /\.svg$/)
|
||||||
|
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||||
|
requireAll(req)
|
1
src/assets/icons/svg/back.svg
Normal file
1
src/assets/icons/svg/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="1640915624153" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7420" xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240"><defs><style type="text/css"></style></defs><path d="M932.7 417.5c-17-38-41.1-72-71.9-101.3-30.5-29-65.9-51.7-105.2-67.6C715 232.3 672.1 224 627.9 224h-139V101.2c0-27.9-30.4-45.2-54.4-31L116.8 257.9c-23.6 14-23.6 48.1 0 62.1l317.7 187.7c24 14.2 54.4-3.1 54.4-31V364h139c51.5 0 100 19.1 136.5 53.8 35.5 33.8 55.1 78.1 55.1 124.9v98.1c0 46.8-19.6 91.2-55.1 124.9-36.5 34.7-85 53.8-136.5 53.8H397.1c-51.5 0-100-19.1-136.5-53.8-35.5-33.8-55.1-78.1-55.1-124.9 0-38.7-31.3-70-70-70s-70 31.3-70 70c0 43.3 9 85.4 26.7 125.1 17 38 41.1 72 71.9 101.3 30.5 29 65.9 51.7 105.2 67.6 40.6 16.4 83.6 24.7 127.8 24.7h230.7c44.2 0 87.2-8.3 127.8-24.7 39.3-15.9 74.7-38.6 105.2-67.6 30.7-29.2 54.9-63.3 71.9-101.3 17.7-39.7 26.7-81.8 26.7-125.1v-98.1c0-43.4-9-85.5-26.7-125.2z" p-id="7421" fill="#8a8a8a"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
12
src/assets/icons/svg/文件夹.svg
Normal file
12
src/assets/icons/svg/文件夹.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?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="1640597797311" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7514"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</defs>
|
||||||
|
<path d="M921.6 766.165333l-29.866667 149.333334H37.12l35.84-149.333334z" fill="#2189FF" p-id="7515"></path>
|
||||||
|
<path
|
||||||
|
d="M33.152 947.456a31.914667 31.914667 0 0 1-22.741333-14.08v-0.213333l-0.256-0.426667v-0.597333a31.914667 31.914667 0 0 1-4.522667-17.066667V156.032a79.573333 79.573333 0 0 1 79.488-79.488H393.813333a32 32 0 0 1 24.576 11.52l109.141334 130.944h305.578666a79.616 79.616 0 0 1 79.488 79.488v110.464h74.837334a32 32 0 0 1 24.746666 11.690667 32 32 0 0 1 6.656 26.581333L923.306667 921.941333a32.042667 32.042667 0 0 1-31.402667 25.6H37.077333a32.64 32.64 0 0 1-3.925333-0.085333z m832.512-63.744l82.176-410.88H180.778667l-102.741334 410.88zM69.12 155.861333v489.898667l51.2-212.48a32.085333 32.085333 0 0 1 31.146667-24.490667h696.704V298.325333a15.488 15.488 0 0 0-15.488-15.488h-320.554667a32 32 0 0 1-24.576-11.52L378.368 140.373333H84.693333a15.488 15.488 0 0 0-15.616 15.488z"
|
||||||
|
fill="#4D4D4D" p-id="7516"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
17
src/assets/icons/svg/编辑02.svg
Normal file
17
src/assets/icons/svg/编辑02.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?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="1640597828992" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8428"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</defs>
|
||||||
|
<path d="M242.901333 567.637333l216.021334 216.021334-55.466667 55.466666-230.4 10.837334 14.122667-226.56z"
|
||||||
|
fill="#1574FF" p-id="8429"></path>
|
||||||
|
<path
|
||||||
|
d="M770.304 36.096a100.096 100.096 0 0 1 70.272 29.141333l120.576 120.576a99.626667 99.626667 0 0 1 0 140.8L425.941333 861.610667a32 32 0 0 1-21.077333 9.344l-230.4 11.136a32 32 0 0 1-33.493333-34.133334l14.165333-226.773333a32 32 0 0 1 9.301333-20.650667l535.509334-535.552a98.944 98.944 0 0 1 70.357333-28.885333z m-380.885333 771.541333l526.464-526.464a35.541333 35.541333 0 0 0 0-50.133333l-120.576-120.576a35.456 35.456 0 0 0-50.133334 0l-526.933333 526.976-11.178667 179.2z"
|
||||||
|
fill="#4D4D4D" p-id="8430"></path>
|
||||||
|
<path
|
||||||
|
d="M981.333333 987.904h-938.666666a32 32 0 0 1-32-32 32 32 0 0 1 32-32h938.666666a32 32 0 0 1 32 32 32 32 0 0 1-32 32z"
|
||||||
|
fill="#4D4D4D" p-id="8431"></path>
|
||||||
|
<path d="M727.381333 299.008m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#474A54" p-id="8432"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
17
src/assets/icons/svg/验证码.svg
Normal file
17
src/assets/icons/svg/验证码.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?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="1640597873120" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9199"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
d="M784.426667 825.429333a581.077333 581.077333 0 0 1-66.986667 55.893334 660.096 660.096 0 0 1-195.84 96 26.325333 26.325333 0 0 1-16.213333 0 649.472 649.472 0 0 1-197.12-95.146667 564.906667 564.906667 0 0 1-68.693334-56.746667 930.56 930.56 0 0 0 258.133334-93.44 31.786667 31.786667 0 0 1 32 0.426667 896.469333 896.469333 0 0 0 254.72 93.013333z"
|
||||||
|
fill="#2189FF" p-id="9200"></path>
|
||||||
|
<path
|
||||||
|
d="M513.194667 1010.602667a63.488 63.488 0 0 1-16.085334-2.048A697.344 697.344 0 0 1 289.493333 908.8a472.149333 472.149333 0 0 1-213.888-395.477333V185.258667a64.384 64.384 0 0 1 64-64.469334 819.2 819.2 0 0 0 341.717334-98.986666 64 64 0 0 1 64 0.298666 792.789333 792.789333 0 0 0 338.901333 98.688 64.384 64.384 0 0 1 64 64.469334v327.850666a473.002667 473.002667 0 0 1-212.437333 394.581334 703.146667 703.146667 0 0 1-206.421334 100.821333h-0.341333a65.152 65.152 0 0 1-15.829333 2.090667z m0-64h0.341333a632.96 632.96 0 0 0 185.301333-90.965334 411.392 411.392 0 0 0 185.685334-342.528V185.258667c0-0.469333-0.213333-0.512-0.341334-0.512a855.552 855.552 0 0 1-371.2-107.392 882.090667 882.090667 0 0 1-373.333333 107.434666 0.64 0.64 0 0 0 0 0.469334v327.893333a410.581333 410.581333 0 0 0 186.922667 343.253333 640 640 0 0 0 186.496 90.197334z"
|
||||||
|
fill="#4D4D4D" p-id="9201"></path>
|
||||||
|
<path
|
||||||
|
d="M466.090667 682.666667a31.914667 31.914667 0 0 1-22.613334-9.386667l-138.88-138.666667A32 32 0 1 1 349.866667 489.344l116.224 116.224 255.104-255.104a32 32 0 0 1 45.269333 45.269333l-277.717333 277.717334a31.914667 31.914667 0 0 1-22.656 9.216z"
|
||||||
|
fill="#4D4D4D" p-id="9202"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
22
src/assets/icons/svgo.yml
Normal file
22
src/assets/icons/svgo.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# replace default config
|
||||||
|
|
||||||
|
# multipass: true
|
||||||
|
# full: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
|
||||||
|
# - name
|
||||||
|
#
|
||||||
|
# or:
|
||||||
|
# - name: false
|
||||||
|
# - name: true
|
||||||
|
#
|
||||||
|
# or:
|
||||||
|
# - name:
|
||||||
|
# param1: 1
|
||||||
|
# param2: 2
|
||||||
|
|
||||||
|
- removeAttrs:
|
||||||
|
attrs:
|
||||||
|
- 'fill'
|
||||||
|
- 'fill-rule'
|
0
src/assets/images/emoji/emoji.png
Normal file
0
src/assets/images/emoji/emoji.png
Normal file
0
src/assets/images/image.png
Normal file
0
src/assets/images/image.png
Normal file
0
src/assets/images/image1.png
Normal file
0
src/assets/images/image1.png
Normal file
30
src/assets/js/common.js
Normal file
30
src/assets/js/common.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 颜色数组
|
||||||
|
*/
|
||||||
|
export const colorArray = [
|
||||||
|
'#5A8DEE',
|
||||||
|
'#CD594B',
|
||||||
|
'#F8CE5E',
|
||||||
|
'#4B9E65'
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 博客url
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
export const BLOG_URL = 'https://pnkx.top/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片的文件类型
|
||||||
|
*/
|
||||||
|
export const IMAGE_TYPE = ['image/jpeg', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']
|
||||||
|
|
||||||
|
/**
|
||||||
|
* websocket消息类型
|
||||||
|
* @type {{}}
|
||||||
|
*/
|
||||||
|
export const WEBSOCKET_MESSAGE_TYPE = {
|
||||||
|
LOGIN: 'login',
|
||||||
|
LOG_OUT: 'log_out',
|
||||||
|
CHAT_MESSAGE: 'chat_message'
|
||||||
|
}
|
BIN
src/assets/main-bg.png
Normal file
BIN
src/assets/main-bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
216
src/components/Editor/index.vue
Normal file
216
src/components/Editor/index.vue
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editor" ref="editor" :style="styles"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Quill from 'quill'
|
||||||
|
import 'quill/dist/quill.core.css'
|
||||||
|
import 'quill/dist/quill.snow.css'
|
||||||
|
import 'quill/dist/quill.bubble.css'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Editor',
|
||||||
|
props: {
|
||||||
|
/* 编辑器的内容 */
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
/* 高度 */
|
||||||
|
height: {
|
||||||
|
type: Number | String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/* 最小高度 */
|
||||||
|
minHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Quill: null,
|
||||||
|
currentValue: '',
|
||||||
|
options: {
|
||||||
|
theme: 'snow',
|
||||||
|
bounds: document.body,
|
||||||
|
debug: 'warn',
|
||||||
|
modules: {
|
||||||
|
// 工具栏配置
|
||||||
|
toolbar: [
|
||||||
|
['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
|
||||||
|
['blockquote', 'code-block'], // 引用 代码块
|
||||||
|
[{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
|
||||||
|
[{ script: 'sub' }, { script: 'super' }], // 上标、下标
|
||||||
|
[{ indent: '-1' }, { indent: '+1' }], // 缩进
|
||||||
|
[{ size: ['small', false, 'large', 'huge'] }], // 字体大小
|
||||||
|
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||||||
|
[{ font: [] }],
|
||||||
|
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||||||
|
[{ align: [] }], // 对齐方式
|
||||||
|
['clean'], // 清除文本格式
|
||||||
|
[{ direction: 'rtl' }], // 文本方向
|
||||||
|
['link', 'image', 'video'] // 链接、图片、视频
|
||||||
|
]
|
||||||
|
},
|
||||||
|
placeholder: '请输入内容',
|
||||||
|
readOnly: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
styles() {
|
||||||
|
let style = {}
|
||||||
|
if (this.minHeight) {
|
||||||
|
style.minHeight = `${this.minHeight}px`
|
||||||
|
}
|
||||||
|
if (this.height) {
|
||||||
|
if (!isNaN(this.height)) {
|
||||||
|
style.height = `${this.height}px`
|
||||||
|
} else {
|
||||||
|
style.height = this.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
handler(val) {
|
||||||
|
if (val !== this.currentValue) {
|
||||||
|
this.currentValue = val === null ? '' : val
|
||||||
|
if (this.Quill) {
|
||||||
|
this.Quill.pasteHTML(this.currentValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.Quill = null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
const editor = this.$refs.editor
|
||||||
|
this.Quill = new Quill(editor, this.options)
|
||||||
|
this.Quill.pasteHTML(this.currentValue)
|
||||||
|
this.Quill.on('text-change', (delta, oldDelta, source) => {
|
||||||
|
const html = this.$refs.editor.children[0].innerHTML
|
||||||
|
const text = this.Quill.getText()
|
||||||
|
const quill = this.Quill
|
||||||
|
this.currentValue = html
|
||||||
|
this.$emit('input', html)
|
||||||
|
this.$emit('on-change', { html, text, quill })
|
||||||
|
})
|
||||||
|
this.Quill.on('text-change', (delta, oldDelta, source) => {
|
||||||
|
this.$emit('on-text-change', delta, oldDelta, source)
|
||||||
|
})
|
||||||
|
this.Quill.on('selection-change', (range, oldRange, source) => {
|
||||||
|
this.$emit('on-selection-change', range, oldRange, source)
|
||||||
|
})
|
||||||
|
this.Quill.on('editor-change', (eventName, ...args) => {
|
||||||
|
this.$emit('on-editor-change', eventName, ...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.editor, .ql-toolbar {
|
||||||
|
white-space: pre-wrap !important;
|
||||||
|
line-height: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quill-img {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
||||||
|
content: "请输入链接地址:";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||||
|
border-right: 0px;
|
||||||
|
content: "保存";
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
||||||
|
content: "请输入视频地址:";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||||
|
content: "14px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
||||||
|
content: "10px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
||||||
|
content: "18px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
||||||
|
content: "32px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||||
|
content: "文本";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||||
|
content: "标题1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||||
|
content: "标题2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||||
|
content: "标题3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||||
|
content: "标题4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||||
|
content: "标题5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||||
|
content: "标题6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||||
|
content: "标准字体";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
||||||
|
content: "衬线字体";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
||||||
|
content: "等宽字体";
|
||||||
|
}
|
||||||
|
</style>
|
900
src/components/Note/index.vue
Normal file
900
src/components/Note/index.vue
Normal file
@ -0,0 +1,900 @@
|
|||||||
|
<!--
|
||||||
|
* @File: index
|
||||||
|
* @Author: PHY
|
||||||
|
* @Date: 2021/12/30 17:43
|
||||||
|
* @Description: 笔记
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="note">
|
||||||
|
<div class="catalogue">
|
||||||
|
<div class="search">
|
||||||
|
<svg-icon @click="handleBack" icon-class="back"/>
|
||||||
|
<el-input v-model="searchCode" placeholder="搜索···" @input="handleSearch"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="el-breadcrumb">
|
||||||
|
<div class="el-breadcrumb-item" v-for="(item, index) in params">
|
||||||
|
<i class="el-icon-d-arrow-right"/>
|
||||||
|
<div class="menu-name" @click="handleBreadcrumbItem(index)">{{ item.breadcrumb }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list" v-loading="listLoading" @contextmenu.prevent.stop="handleEditNote($event, 'empty')">
|
||||||
|
<div class="no-data" v-if="list.length < 1">没有内容</div>
|
||||||
|
<div class="one"
|
||||||
|
v-else
|
||||||
|
:class="one.id === active.id ? 'one-active' : ''"
|
||||||
|
v-for="one in list"
|
||||||
|
@contextmenu.prevent.stop="handleEditNote($event, one)"
|
||||||
|
:key="one.id"
|
||||||
|
>
|
||||||
|
<div class="folder select-none" :draggable="true" @dragstart="drag($event, one)" @dragover.prevent
|
||||||
|
@drop="drop($event, one)" v-if="one.type === 'folder'" @click="handleOpen(one, false)"
|
||||||
|
@dblclick="handleOpen(one, true)"
|
||||||
|
>
|
||||||
|
<div class="name">
|
||||||
|
<svg-icon icon-class="文件夹"/>
|
||||||
|
{{ one.name }}
|
||||||
|
</div>
|
||||||
|
<div class="password">
|
||||||
|
<svg-icon class="password" v-if="one.password" icon-class="验证码"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="note select-none" :draggable="true" @dragstart="drag($event,one)"
|
||||||
|
v-if="one.type === 'note'" @click="handleOpen(one)"
|
||||||
|
>
|
||||||
|
<div class="name">
|
||||||
|
<svg-icon icon-class="编辑02"/>
|
||||||
|
{{ one.title }}
|
||||||
|
</div>
|
||||||
|
<div class="content">{{ one.content && one.content.replace(regex, '') }}</div>
|
||||||
|
<div class="create-time">{{ one.updateTime || one.createTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview" v-loading="loading">
|
||||||
|
<div class="note-edit">
|
||||||
|
<el-empty description="无预览" v-if="active.type === 'folder'"></el-empty>
|
||||||
|
<div class="note-title" v-else>
|
||||||
|
<el-input v-model="note.title" placeholder="请输入笔记标题"/>
|
||||||
|
<el-input v-model="note.order" placeholder="请输入排序"/>
|
||||||
|
</div>
|
||||||
|
<div class="note-content" v-if="active.type === 'note' && editor">
|
||||||
|
<editor :key="note.id" ref="editor" height="calc(100vh - 215px)" v-model="note.richText"></editor>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--新增文件夹-->
|
||||||
|
<el-dialog
|
||||||
|
:title="folderForm.id ? '编辑文件夹' : '新增文件夹'"
|
||||||
|
:visible.sync="folderVisible"
|
||||||
|
min-width="30%"
|
||||||
|
>
|
||||||
|
<el-form ref="folderForm" :rules="folderRules" :model="folderForm" label-width="8rem">
|
||||||
|
<el-form-item label="文件夹名称" prop="name">
|
||||||
|
<el-input autofocus="autofocus" v-model="folderForm.name" placeholder="请输入文件夹名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上级文件夹" prop="parentId">
|
||||||
|
<el-cascader
|
||||||
|
v-model="folderForm.parentId"
|
||||||
|
:options="treeList"
|
||||||
|
:show-all-levels="false"
|
||||||
|
:props="props"
|
||||||
|
placeholder="请选择上级文件夹"
|
||||||
|
></el-cascader>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input v-model="folderForm.order" placeholder="请输入排序" type="number"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="阅读密码">
|
||||||
|
<el-input type="password" v-model="folderForm.password" placeholder="请输入阅读密码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="folderVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="handleAddFolder" :loading="folderSaveLoading">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
<!--移动笔记弹框-->
|
||||||
|
<el-dialog
|
||||||
|
title="移动到"
|
||||||
|
:visible.sync="moveNoteVisible"
|
||||||
|
min-width="30%"
|
||||||
|
>
|
||||||
|
<div class="object" style="margin-bottom: 1rem;">
|
||||||
|
<div class="name">
|
||||||
|
<svg-icon v-if="source.type === 'folder'" icon-class="文件夹"/>
|
||||||
|
<svg-icon v-if="source.type === 'note'" icon-class="编辑02"/>
|
||||||
|
{{ source.name || source.title }}
|
||||||
|
</div>
|
||||||
|
<div class="password" style="width: 2rem;">
|
||||||
|
<svg-icon class="password" v-if="source.password" icon-class="验证码"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-cascader-panel :options="treeList"
|
||||||
|
@change="handleChangMoveTarget"
|
||||||
|
:props="props"
|
||||||
|
/>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="moveNoteVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="handleMoveNote" :loading="moveNoteLoading">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
<!--右键文件夹或笔记小窗口-->
|
||||||
|
<div class="right-click" :style="rightStyle" v-if="rightFlag" v-clickOutSide="handleCloseEditNote">
|
||||||
|
<div class="right-function"
|
||||||
|
v-for="item in rightFunctions"
|
||||||
|
:key="item.id"
|
||||||
|
@click="handleRightClick(item)"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
addFolder,
|
||||||
|
addNote,
|
||||||
|
delFolder,
|
||||||
|
delNote,
|
||||||
|
getFolder,
|
||||||
|
getNote,
|
||||||
|
listFolder,
|
||||||
|
treeList,
|
||||||
|
updateFolder,
|
||||||
|
updateNote
|
||||||
|
} from '@/api'
|
||||||
|
|
||||||
|
import {getLocal, setLocal} from '@/utils/local'
|
||||||
|
import Editor from "@/components/Editor/index.vue";
|
||||||
|
import {debounce} from "@/utils/utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Note',
|
||||||
|
components: {
|
||||||
|
Editor
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 右键窗口样式
|
||||||
|
rightStyle: '',
|
||||||
|
// 右键窗口标志
|
||||||
|
rightFlag: false,
|
||||||
|
// 右键功能
|
||||||
|
rightFunctions: [],
|
||||||
|
// 移动笔记弹框标志
|
||||||
|
moveNoteVisible: false,
|
||||||
|
// 移动笔记loading标志
|
||||||
|
moveNoteLoading: false,
|
||||||
|
// 来源
|
||||||
|
source: {},
|
||||||
|
// 目标
|
||||||
|
target: undefined,
|
||||||
|
// 文件列表加载标志
|
||||||
|
listLoading: false,
|
||||||
|
// 文件夹弹框标志
|
||||||
|
folderVisible: false,
|
||||||
|
// 文件夹保存按钮
|
||||||
|
folderSaveLoading: false,
|
||||||
|
// 笔记获取标志
|
||||||
|
loading: false,
|
||||||
|
// 文件夹表单
|
||||||
|
folderForm: {},
|
||||||
|
// 文件夹规则
|
||||||
|
folderRules: {
|
||||||
|
name: [
|
||||||
|
{required: true, message: '请输入文件夹名称', trigger: 'blur'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 树形文件夹
|
||||||
|
treeList: [],
|
||||||
|
// 树形对应
|
||||||
|
props: {
|
||||||
|
checkStrictly: true,
|
||||||
|
value: 'id',
|
||||||
|
label: 'name'
|
||||||
|
},
|
||||||
|
// 去除html正则
|
||||||
|
regex: /(<([^>]+)>)/ig,
|
||||||
|
// 搜索关键字
|
||||||
|
searchCode: '',
|
||||||
|
// 文件夹列表
|
||||||
|
list: [],
|
||||||
|
// 笔记
|
||||||
|
note: {
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
richText: ''
|
||||||
|
},
|
||||||
|
// 查询列表参数
|
||||||
|
params: getLocal('noteFolder') || [{parentId: 0, breadcrumb: '我的笔记'}],
|
||||||
|
// 选择ID
|
||||||
|
active: {},
|
||||||
|
// 搜索延时任务
|
||||||
|
searchTimer: undefined,
|
||||||
|
// 拖动目标对象
|
||||||
|
dragObject: {},
|
||||||
|
// 右键选中对象
|
||||||
|
rightObject: {},
|
||||||
|
// 首次获取
|
||||||
|
firstGet: true,
|
||||||
|
// 编辑器
|
||||||
|
editor: true,
|
||||||
|
// 笔记缓存
|
||||||
|
noteCache: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getFolderList()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
params(newDate) {
|
||||||
|
setLocal('noteFolder', newDate)
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
handler(newValue) {
|
||||||
|
if (this.firstGet || JSON.stringify(this.noteCache) === JSON.stringify(newValue)) return
|
||||||
|
debounce(() => {
|
||||||
|
// 保存笔记
|
||||||
|
this.handleSaveNote()
|
||||||
|
}, 2000)()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 点击面包屑
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
handleBreadcrumbItem(index) {
|
||||||
|
this.params = this.params.slice(0, index + 1)
|
||||||
|
this.getFolderList()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 关闭右键弹框
|
||||||
|
*/
|
||||||
|
handleCloseEditNote() {
|
||||||
|
this.rightFlag = false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 点击右键功能
|
||||||
|
*/
|
||||||
|
handleRightClick(item) {
|
||||||
|
switch (item.id) {
|
||||||
|
case 1:
|
||||||
|
this.handleAdd(false)
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
this.handleAdd(true)
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
this.handleEdit(this.rightObject)
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
this.loading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
this.source = this.rightObject
|
||||||
|
this.moveNoteVisible = true
|
||||||
|
break
|
||||||
|
case 6:
|
||||||
|
this.handleDeleteFolder(this.rightObject)
|
||||||
|
break
|
||||||
|
case 7:
|
||||||
|
this.handleDeleteNote(this.rightObject)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.rightFlag = false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 移动笔记或文件夹
|
||||||
|
*/
|
||||||
|
handleMoveNote() {
|
||||||
|
if (!this.target) {
|
||||||
|
this.$notify.warning('请选择移动目标文件夹')
|
||||||
|
} else if (this.source.folder === this.target) {
|
||||||
|
this.$notify.warning('请勿选择原来的文件夹')
|
||||||
|
} else {
|
||||||
|
this.moveNoteLoading = true
|
||||||
|
if (this.source.type === 'folder') {
|
||||||
|
this.source.parentId = this.target
|
||||||
|
updateFolder(this.source).then(res => {
|
||||||
|
this.moveNoteLoading = false
|
||||||
|
this.moveNoteVisible = false
|
||||||
|
this.getFolderList()
|
||||||
|
})
|
||||||
|
} else if (this.source.type === 'note') {
|
||||||
|
this.source.folder = this.target
|
||||||
|
updateNote(this.source).then(res => {
|
||||||
|
this.moveNoteLoading = false
|
||||||
|
this.moveNoteVisible = false
|
||||||
|
this.getFolderList(res.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 选中移动目标
|
||||||
|
*/
|
||||||
|
handleChangMoveTarget(target) {
|
||||||
|
this.target = target[target.length - 1]
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 右键编辑笔记
|
||||||
|
* @param event
|
||||||
|
* @param note
|
||||||
|
*/
|
||||||
|
handleEditNote(event, note) {
|
||||||
|
if (note === 'empty') {
|
||||||
|
this.rightFunctions = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '新建笔记'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '新建文件夹'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
this.active = note
|
||||||
|
if (note.type === 'folder') {
|
||||||
|
this.rightFunctions = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '新建笔记'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '新建文件夹'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '编辑'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: '移动到'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: '删除'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else if (note.type === 'note') {
|
||||||
|
this.rightFunctions = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '新建笔记'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '新建文件夹'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: '编辑'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: '移动到'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: '删除'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.rightObject = note
|
||||||
|
}
|
||||||
|
this.rightFlag = true
|
||||||
|
this.rightStyle = `top: calc(${event.y}px - ${this.rightFunctions.length * 3}rem); left: calc(${event.x}px - 15rem);`
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 拖动结束
|
||||||
|
* @param event
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
drop(event, data) {
|
||||||
|
if (data.name) {
|
||||||
|
if (this.dragObject.type === 'folder') {
|
||||||
|
// 移动文件夹
|
||||||
|
this.listLoading = true
|
||||||
|
this.dragObject.parentId = data.id
|
||||||
|
updateFolder(this.dragObject).then(res => {
|
||||||
|
this.listLoading = false
|
||||||
|
this.getFolderList()
|
||||||
|
})
|
||||||
|
} else if (this.dragObject.type === 'note') {
|
||||||
|
// 移动笔记
|
||||||
|
this.listLoading = true
|
||||||
|
this.dragObject.folder = data.id
|
||||||
|
updateNote(this.dragObject).then(res => {
|
||||||
|
this.listLoading = false
|
||||||
|
this.getFolderList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 拖动开始
|
||||||
|
* @param event
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
drag(event, data) {
|
||||||
|
this.dragObject = data
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 搜索
|
||||||
|
*/
|
||||||
|
handleSearch() {
|
||||||
|
if (this.searchTimer) {
|
||||||
|
clearTimeout(this.searchTimer)
|
||||||
|
this.searchTimer = setTimeout(() => {
|
||||||
|
if (this.searchCode) {
|
||||||
|
this.params.push(
|
||||||
|
{
|
||||||
|
name: this.searchCode
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.getFolderList()
|
||||||
|
} else {
|
||||||
|
this.params = [
|
||||||
|
{
|
||||||
|
parentId: 0,
|
||||||
|
breadcrumb: '我的笔记'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
this.getFolderList()
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
this.searchTimer = setTimeout(() => {
|
||||||
|
if (this.searchCode) {
|
||||||
|
this.params.push(
|
||||||
|
{
|
||||||
|
name: this.searchCode
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.getFolderList()
|
||||||
|
} else {
|
||||||
|
this.params = [
|
||||||
|
{
|
||||||
|
parentId: 0,
|
||||||
|
breadcrumb: '我的笔记'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
this.getFolderList()
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除笔记
|
||||||
|
*/
|
||||||
|
handleDeleteNote(note) {
|
||||||
|
this.$confirm(`确认删除《${note.title}》笔记?`, '删除', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
return delNote(note.id)
|
||||||
|
}).then(() => {
|
||||||
|
this.getFolderList()
|
||||||
|
this.$notify.success('删除成功')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 保存笔记
|
||||||
|
*/
|
||||||
|
handleSaveNote() {
|
||||||
|
const note = JSON.parse(JSON.stringify(this.note))
|
||||||
|
note.content = this.$refs.editor.value
|
||||||
|
updateNote(note).then(() => {
|
||||||
|
// 缓存笔记
|
||||||
|
this.noteCache = JSON.parse(JSON.stringify(this.note))
|
||||||
|
this.$notify.success('保存成功')
|
||||||
|
// 更新列表中的笔记
|
||||||
|
this.$set(this.list, this.list.findIndex(item => item.id === this.note.id), {type: 'note', ...this.note})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除文件夹
|
||||||
|
*/
|
||||||
|
handleDeleteFolder(folder) {
|
||||||
|
this.$confirm(`确认删除《${folder.name}》文件夹?`, '删除', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
return delFolder(folder.id)
|
||||||
|
}).then(() => {
|
||||||
|
this.getFolderList()
|
||||||
|
this.$notify.success('删除成功')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 编辑文件夹
|
||||||
|
*/
|
||||||
|
handleEdit(one) {
|
||||||
|
getFolder(one.id).then(res => {
|
||||||
|
this.folderForm = res.data
|
||||||
|
this.folderVisible = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 新增文件夹或者文件
|
||||||
|
*/
|
||||||
|
handleAdd(flag) {
|
||||||
|
if (flag) {
|
||||||
|
// 新增文件夹
|
||||||
|
this.folderForm = {}
|
||||||
|
this.folderForm.parentId = this.params[this.params.length - 1].parentId
|
||||||
|
this.folderVisible = true
|
||||||
|
} else {
|
||||||
|
// 新增笔记
|
||||||
|
addNote({
|
||||||
|
title: '无标题笔记',
|
||||||
|
content: '笔记内容···',
|
||||||
|
richText: '笔记内容···',
|
||||||
|
folder: this.params[this.params.length - 1].parentId
|
||||||
|
}).then(res => {
|
||||||
|
this.note = res.data
|
||||||
|
this.getFolderList(res.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 新增文件夹
|
||||||
|
*/
|
||||||
|
handleAddFolder() {
|
||||||
|
this.folderSaveLoading = true
|
||||||
|
this.$refs.folderForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (Array.isArray(this.folderForm.parentId)) {
|
||||||
|
this.folderForm.parentId = this.folderForm.parentId[this.folderForm.parentId.length - 1]
|
||||||
|
}
|
||||||
|
if (this.folderForm.id) {
|
||||||
|
updateFolder(this.folderForm).then(() => {
|
||||||
|
this.$notify.success('修改文件夹成功')
|
||||||
|
this.folderVisible = false
|
||||||
|
this.folderSaveLoading = false
|
||||||
|
this.getFolderList()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addFolder(this.folderForm).then(res => {
|
||||||
|
this.$notify.success('新增文件夹成功')
|
||||||
|
this.folderVisible = false
|
||||||
|
this.folderSaveLoading = false
|
||||||
|
this.getFolderList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 返回上级
|
||||||
|
*/
|
||||||
|
handleBack() {
|
||||||
|
if (this.params.length > 1) {
|
||||||
|
this.params.pop()
|
||||||
|
this.getFolderList()
|
||||||
|
} else {
|
||||||
|
this.params = [{parentId: 0, breadcrumb: '我的笔记'}]
|
||||||
|
this.getFolderList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开文件或文件夹
|
||||||
|
* @param one
|
||||||
|
* @param clickFlag
|
||||||
|
*/
|
||||||
|
handleOpen(one, clickFlag) {
|
||||||
|
// 如果点击的是当前激活的文件或文件夹,则不执行任何操作
|
||||||
|
if (!clickFlag && this.active.id === one.id) return
|
||||||
|
this.firstGet = true
|
||||||
|
this.active = one
|
||||||
|
if (one.type === 'folder') {
|
||||||
|
if (clickFlag) {
|
||||||
|
if (one.password) {
|
||||||
|
this.$prompt('请输入阅读密码', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
inputPattern: /\S/,
|
||||||
|
inputValidator: (value) => {
|
||||||
|
return one.password === value
|
||||||
|
},
|
||||||
|
inputErrorMessage: '阅读密码不正确'
|
||||||
|
}).then(({value}) => {
|
||||||
|
this.params.push({parentId: one.id, breadcrumb: one.name})
|
||||||
|
this.getFolderList()
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.params.push({parentId: one.id, breadcrumb: one.name})
|
||||||
|
this.getFolderList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (one.type === 'note') {
|
||||||
|
this.handleOpenNote(one.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取文件目录
|
||||||
|
*/
|
||||||
|
getFolderList(data) {
|
||||||
|
this.listLoading = true
|
||||||
|
listFolder(this.params[this.params.length - 1]).then(res => {
|
||||||
|
this.list = res.rows.map(item => {
|
||||||
|
if (item.title) {
|
||||||
|
item.type = 'note'
|
||||||
|
} else {
|
||||||
|
item.type = 'folder'
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
if (this.list[0]) {
|
||||||
|
if (data) {
|
||||||
|
this.active = data
|
||||||
|
} else {
|
||||||
|
this.active = this.list[0]
|
||||||
|
}
|
||||||
|
if (this.active.type === 'note') {
|
||||||
|
this.loading = true
|
||||||
|
this.handleOpenNote(this.list[0].id)
|
||||||
|
} else if (this.active.type === 'folder') {
|
||||||
|
this.note = {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.note = {}
|
||||||
|
}
|
||||||
|
this.listLoading = false
|
||||||
|
})
|
||||||
|
// 获取树形目录
|
||||||
|
treeList().then(res => {
|
||||||
|
this.treeList = res.data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开笔记
|
||||||
|
* @param id 笔记id
|
||||||
|
*/
|
||||||
|
handleOpenNote(id) {
|
||||||
|
this.firstGet = true
|
||||||
|
this.loading = true
|
||||||
|
this.editor = false
|
||||||
|
getNote(id).then(res => {
|
||||||
|
this.note = res.data
|
||||||
|
this.noteCache = JSON.parse(JSON.stringify(res.data))
|
||||||
|
this.loading = false
|
||||||
|
this.editor = true
|
||||||
|
setTimeout(() => this.firstGet = false, 1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.note {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 84px);
|
||||||
|
|
||||||
|
.catalogue {
|
||||||
|
width: 24rem;
|
||||||
|
border-right: 1px solid #55555529;
|
||||||
|
background-color: #55555509;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
.search {
|
||||||
|
margin: 1rem 2rem;
|
||||||
|
height: 4rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 2rem 1rem;
|
||||||
|
flex-flow: wrap;
|
||||||
|
|
||||||
|
.el-breadcrumb-item {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-name {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #555555DD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-active {
|
||||||
|
background-color: #55555525;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one {
|
||||||
|
&:hover {
|
||||||
|
background-color: #55555525;
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder, .note {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.edit {
|
||||||
|
display: inline-block !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
.svg-icon {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder {
|
||||||
|
height: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
height: 8rem;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.content, .create-time {
|
||||||
|
color: #555555DD;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2; /*截取第三行*/
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.note-edit {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #555555DD;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
.right {
|
||||||
|
font-weight: normal;
|
||||||
|
display: flex;
|
||||||
|
font-size: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .el-input:nth-child(1) {
|
||||||
|
flex: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .el-input:nth-child(2) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
.button {
|
||||||
|
width: 8rem;
|
||||||
|
height: 8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
position: fixed;
|
||||||
|
right: 4rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 9999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save {
|
||||||
|
bottom: 4rem;
|
||||||
|
background-color: #5A8DEE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-click {
|
||||||
|
position: absolute;
|
||||||
|
width: 15rem;
|
||||||
|
box-shadow: 2px 2px 5px 3px #999999DD;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
.right-function {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 1rem;
|
||||||
|
height: 3rem;
|
||||||
|
line-height: 3rem;
|
||||||
|
border-bottom: 1px solid #999999DD;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5A8DEE55;
|
||||||
|
color: #5A8DEE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
61
src/components/SvgIcon/index.vue
Normal file
61
src/components/SvgIcon/index.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners"/>
|
||||||
|
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||||
|
<use :xlink:href="iconName"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {isExternal} from '@/utils/validate'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SvgIcon',
|
||||||
|
props: {
|
||||||
|
iconClass: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isExternal() {
|
||||||
|
return isExternal(this.iconClass)
|
||||||
|
},
|
||||||
|
iconName() {
|
||||||
|
return `#icon-${this.iconClass}`
|
||||||
|
},
|
||||||
|
svgClass() {
|
||||||
|
if (this.className) {
|
||||||
|
return 'svg-icon ' + this.className
|
||||||
|
} else {
|
||||||
|
return 'svg-icon'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
styleExternalIcon() {
|
||||||
|
return {
|
||||||
|
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||||
|
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.svg-icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-external-icon {
|
||||||
|
background-color: currentColor;
|
||||||
|
mask-size: cover !important;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
25
src/directive/dom/clickOutSide.js
Normal file
25
src/directive/dom/clickOutSide.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export default {
|
||||||
|
// 初始化指令
|
||||||
|
bind(el, binding, vnode) {
|
||||||
|
function documentHandler(e) {
|
||||||
|
// 这里判断点击的元素是否是本身,是本身,则返回
|
||||||
|
if (el.contains(e.target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 判断指令中是否绑定了函数
|
||||||
|
if (binding.expression) {
|
||||||
|
// 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
|
||||||
|
binding.value(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
|
||||||
|
el.__vueClickOutside__ = documentHandler;
|
||||||
|
document.addEventListener('click', documentHandler);
|
||||||
|
},
|
||||||
|
update() {},
|
||||||
|
unbind(el, binding) {
|
||||||
|
// 解除事件监听
|
||||||
|
document.removeEventListener('click', el.__vueClickOutside__);
|
||||||
|
delete el.__vueClickOutside__;
|
||||||
|
}
|
||||||
|
}
|
11
src/directive/index.js
Normal file
11
src/directive/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import clickOutSide from './dom/clickOutSide'
|
||||||
|
|
||||||
|
const install = function (Vue) {
|
||||||
|
Vue.directive('clickOutSide', clickOutSide)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.Vue) {
|
||||||
|
Vue.use(install); // eslint-disable-line
|
||||||
|
}
|
||||||
|
|
||||||
|
export default install
|
17
src/main.js
Normal file
17
src/main.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import store from './store'
|
||||||
|
import ElementUI from 'element-ui'
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
|
import './assets/icons' // icon
|
||||||
|
import directive from './directive' //directive
|
||||||
|
|
||||||
|
Vue.use(ElementUI)
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
// 自定义指令
|
||||||
|
Vue.use(directive);
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
36
src/settings.js
Normal file
36
src/settings.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
module.exports = {
|
||||||
|
title: '若依管理系统',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
||||||
|
*/
|
||||||
|
sideTheme: 'theme-dark',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否系统布局配置
|
||||||
|
*/
|
||||||
|
showSettings: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示 tagsView
|
||||||
|
*/
|
||||||
|
tagsView: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否固定头部
|
||||||
|
*/
|
||||||
|
fixedHeader: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示logo
|
||||||
|
*/
|
||||||
|
sidebarLogo: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string | array} 'production' | ['production', 'development']
|
||||||
|
* @description Need show err logs component.
|
||||||
|
* The default is only used in the production env
|
||||||
|
* If you want to also use it in dev, you can pass ['production', 'development']
|
||||||
|
*/
|
||||||
|
errorLog: 'production'
|
||||||
|
}
|
32
src/store/index.js
Normal file
32
src/store/index.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import { getInfo } from '@/api'
|
||||||
|
import { getToken } from '@/utils/auth'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
// 用户信息
|
||||||
|
userInfo: {}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_USER_INFO: (state, userInfo) => {
|
||||||
|
state.userInfo = userInfo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// 获取用户信息
|
||||||
|
GetInfo ({ commit, state }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getInfo(getToken()).then(res => {
|
||||||
|
commit('SET_USER_INFO', res.user)
|
||||||
|
resolve(res)
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modules: {}
|
||||||
|
})
|
17
src/utils/auth.js
Normal file
17
src/utils/auth.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 保存到localStorage中
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const TokenKey = 'Admin-Token'
|
||||||
|
|
||||||
|
export function getToken() {
|
||||||
|
return localStorage.getItem(TokenKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setToken(token) {
|
||||||
|
return localStorage.setItem(TokenKey, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeToken() {
|
||||||
|
return localStorage.removeItem(TokenKey)
|
||||||
|
}
|
68
src/utils/compressImage.js
Normal file
68
src/utils/compressImage.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// 压缩图片
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
export function compressImage (file, config) {
|
||||||
|
const read = new FileReader();
|
||||||
|
read.readAsDataURL(file);
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
read.onload = function (e) {
|
||||||
|
let img = new Image();
|
||||||
|
img.src = e.target.result;
|
||||||
|
img.onload = function () {
|
||||||
|
// 默认按比例压缩
|
||||||
|
let w = 0;
|
||||||
|
let h = 0;
|
||||||
|
if (img.width > 100) {
|
||||||
|
w = config.width || img.width * config.proportion;
|
||||||
|
h = config.height || img.height * config.proportion;
|
||||||
|
} else {
|
||||||
|
w = img.width;
|
||||||
|
h = img.height;
|
||||||
|
}
|
||||||
|
// 生成canvas
|
||||||
|
let canvas = document.createElement('canvas');
|
||||||
|
let ctx = canvas.getContext('2d');
|
||||||
|
let base64;
|
||||||
|
// 创建属性节点
|
||||||
|
canvas.setAttribute('width', w);
|
||||||
|
canvas.setAttribute('height', h);
|
||||||
|
ctx.drawImage(this, 0, 0, w, h);
|
||||||
|
|
||||||
|
base64 = canvas.toDataURL(file['type'], config.quality);
|
||||||
|
|
||||||
|
// 回调函数返回file的值(将base64编码转成file)
|
||||||
|
// files = dataURLtoFile(base64) // 如果后台接收类型为base64的话这一步可以省略
|
||||||
|
|
||||||
|
// 回调函数返回file的值(将base64转为二进制)
|
||||||
|
let fileBinary = dataURLtoBlob(base64);
|
||||||
|
|
||||||
|
resolve(fileBinary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
// 将base64转为二进制
|
||||||
|
function dataURLtoBlob (dataurl) {
|
||||||
|
let arr = dataurl.split(',');
|
||||||
|
let mime = arr[0].match(/:(.*?);/)[1];
|
||||||
|
let bstr = atob(arr[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
let u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
return new Blob([u8arr], { type: mime })
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64转码(将base64编码转回file文件) 此方法我没用到
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
function dataURLtoFile (dataurl) {
|
||||||
|
let arr = dataurl.split(',');
|
||||||
|
let mime = arr[0].match(/:(.*?);/)[1];
|
||||||
|
let bstr = atob(arr[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
let u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
return new File([u8arr], { type: mime })
|
||||||
|
}
|
6
src/utils/errorCode.js
Normal file
6
src/utils/errorCode.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
'401': '认证失败,无法访问系统资源',
|
||||||
|
'403': '当前操作没有权限',
|
||||||
|
'404': '访问资源不存在',
|
||||||
|
'default': '系统未知错误,请反馈给管理员'
|
||||||
|
}
|
36
src/utils/local.js
Normal file
36
src/utils/local.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* local存
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
export function setLocal(key, value) {
|
||||||
|
if (!key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localStorage.setItem(key, JSON.stringify(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* local取
|
||||||
|
* @param key
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function getLocal(key) {
|
||||||
|
if (!key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return JSON.parse(localStorage.getItem(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* local清除
|
||||||
|
* @param key
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function removeLocal(key) {
|
||||||
|
if (!key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return localStorage.removeItem(key)
|
||||||
|
}
|
103
src/utils/request.js
Normal file
103
src/utils/request.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import {Message, Notification} from 'element-ui'
|
||||||
|
import {getToken} from '@/utils/auth'
|
||||||
|
import errorCode from '@/utils/errorCode'
|
||||||
|
|
||||||
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
|
// 创建axios实例
|
||||||
|
console.log('url', process.env.VUE_APP_BASE_API)
|
||||||
|
const service = axios.create({
|
||||||
|
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||||
|
baseURL: process.env.VUE_APP_BASE_API,
|
||||||
|
// 超时
|
||||||
|
timeout: 300000
|
||||||
|
})
|
||||||
|
// request拦截器
|
||||||
|
service.interceptors.request.use(config => {
|
||||||
|
// 是否需要设置 token
|
||||||
|
const isToken = (config.headers || {}).isToken === false
|
||||||
|
if (getToken() && !isToken) {
|
||||||
|
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||||
|
}
|
||||||
|
// get请求映射params参数
|
||||||
|
if (config.method === 'get' && config.params) {
|
||||||
|
let url = config.url + '?';
|
||||||
|
for (const propName of Object.keys(config.params)) {
|
||||||
|
const value = config.params[propName];
|
||||||
|
var part = encodeURIComponent(propName) + "=";
|
||||||
|
if (value !== null && typeof(value) !== "undefined") {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
let params = propName + '[' + key + ']';
|
||||||
|
var subPart = encodeURIComponent(params) + "=";
|
||||||
|
url += subPart + encodeURIComponent(value[key]) + "&";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url += part + encodeURIComponent(value) + "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url = url.slice(0, -1);
|
||||||
|
config.params = {};
|
||||||
|
config.url = url;
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
service.interceptors.response.use(res => {
|
||||||
|
// 未设置状态码则默认成功状态
|
||||||
|
const code = res.data.code || 200;
|
||||||
|
// 获取错误信息
|
||||||
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
|
if (code === 401) {
|
||||||
|
/*MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
|
||||||
|
confirmButtonText: '重新登录',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
store.dispatch('LogOut').then(() => {
|
||||||
|
location.href = '/index';
|
||||||
|
})
|
||||||
|
})*/
|
||||||
|
} else if (code === 500) {
|
||||||
|
Message({
|
||||||
|
message: '网络异常,请稍候再试',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
return Promise.reject(new Error(msg))
|
||||||
|
} else if (code !== 200) {
|
||||||
|
Notification.error({
|
||||||
|
title: '网络异常,请刷新当前页面'
|
||||||
|
})
|
||||||
|
return Promise.reject('error')
|
||||||
|
} else {
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log('err' + error)
|
||||||
|
let { message } = error;
|
||||||
|
if (message === "Network Error") {
|
||||||
|
message = "网络异常,请刷新当前页面";
|
||||||
|
}
|
||||||
|
else if (message.includes("timeout")) {
|
||||||
|
message = "请求超时,请刷新当前页面";
|
||||||
|
}
|
||||||
|
else if (message.includes("Request failed with status code")) {
|
||||||
|
message = "网络异常,请刷新当前页面";
|
||||||
|
}
|
||||||
|
Message({
|
||||||
|
message: '请求超时,请刷新当前页面',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default service
|
60
src/utils/scroll-to.js
Normal file
60
src/utils/scroll-to.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
Math.easeInOutQuad = function (t, b, c, d) {
|
||||||
|
t /= d / 2
|
||||||
|
if (t < 1) {
|
||||||
|
return c / 2 * t * t + b
|
||||||
|
}
|
||||||
|
t--
|
||||||
|
return -c / 2 * (t * (t - 2) - 1) + b
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||||
|
var requestAnimFrame = (function () {
|
||||||
|
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
|
||||||
|
window.setTimeout(callback, 1000 / 60)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||||
|
* @param {number} amount
|
||||||
|
*/
|
||||||
|
function move(amount) {
|
||||||
|
document.documentElement.scrollTop = amount
|
||||||
|
document.body.parentNode.scrollTop = amount
|
||||||
|
document.body.scrollTop = amount
|
||||||
|
}
|
||||||
|
|
||||||
|
function position() {
|
||||||
|
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} to
|
||||||
|
* @param {number} duration
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
export function scrollTo(to, duration, callback) {
|
||||||
|
const start = position()
|
||||||
|
const change = to - start
|
||||||
|
const increment = 20
|
||||||
|
let currentTime = 0
|
||||||
|
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||||
|
var animateScroll = function () {
|
||||||
|
// increment the time
|
||||||
|
currentTime += increment
|
||||||
|
// find the value with the quadratic in-out easing function
|
||||||
|
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||||
|
// move the document.body
|
||||||
|
move(val)
|
||||||
|
// do the animation unless its over
|
||||||
|
if (currentTime < duration) {
|
||||||
|
requestAnimFrame(animateScroll)
|
||||||
|
} else {
|
||||||
|
if (callback && typeof (callback) === 'function') {
|
||||||
|
// the animation is done so lets callback
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animateScroll()
|
||||||
|
}
|
18
src/utils/utils.js
Normal file
18
src/utils/utils.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 防抖
|
||||||
|
* @param fn 函数
|
||||||
|
* @param delay 等待时间
|
||||||
|
* @returns {function(...[*]=)}
|
||||||
|
*/
|
||||||
|
// 防抖定时函数
|
||||||
|
let debounceTimer = null;
|
||||||
|
export function debounce(fn, delay = 1000){
|
||||||
|
return () => {
|
||||||
|
if(debounceTimer){
|
||||||
|
clearTimeout(debounceTimer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
|
||||||
|
debounceTimer = setTimeout(fn,delay)
|
||||||
|
}else{
|
||||||
|
debounceTimer = setTimeout(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
src/utils/validate.js
Normal file
83
src/utils/validate.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isExternal(path) {
|
||||||
|
return /^(https?:|mailto:|tel:)/.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validUserName(str) {
|
||||||
|
const valid_map = ['admin', 'editor']
|
||||||
|
return valid_map.indexOf(str.trim()) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validURL(url) {
|
||||||
|
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||||
|
return reg.test(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validLowerCase(str) {
|
||||||
|
const reg = /^[a-z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validUpperCase(str) {
|
||||||
|
const reg = /^[A-Z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validAlphabets(str) {
|
||||||
|
const reg = /^[A-Za-z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} email
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function validEmail(email) {
|
||||||
|
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||||
|
return reg.test(email)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isString(str) {
|
||||||
|
if (typeof str === 'string' || str instanceof String) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} arg
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isArray(arg) {
|
||||||
|
if (typeof Array.isArray === 'undefined') {
|
||||||
|
return Object.prototype.toString.call(arg) === '[object Array]'
|
||||||
|
}
|
||||||
|
return Array.isArray(arg)
|
||||||
|
}
|
111
vue.config.js
Normal file
111
vue.config.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
'use strict'
|
||||||
|
const path = require('path')
|
||||||
|
const defaultSettings = require('./src/settings.js')
|
||||||
|
|
||||||
|
function resolve(dir) {
|
||||||
|
return path.join(__dirname, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = defaultSettings.title || 'pei你看雪博客管理系统' // 标题
|
||||||
|
|
||||||
|
const port = process.env.port || process.env.npm_config_port || 80 // 端口
|
||||||
|
|
||||||
|
// vue.config.js 配置说明
|
||||||
|
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
|
||||||
|
// 这里只列一部分,具体配置参考文档
|
||||||
|
module.exports = {
|
||||||
|
// 部署生产环境和开发环境下的URL。
|
||||||
|
// 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
|
||||||
|
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
|
||||||
|
publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
|
||||||
|
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
|
||||||
|
outputDir: 'dist',
|
||||||
|
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
|
||||||
|
assetsDir: 'static',
|
||||||
|
// 是否开启eslint保存检测,有效值:ture | false | 'error'
|
||||||
|
// lintOnSave: process.env.NODE_ENV === 'development',
|
||||||
|
lintOnSave: false,
|
||||||
|
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
||||||
|
productionSourceMap: false,
|
||||||
|
// webpack-dev-server 相关配置
|
||||||
|
devServer: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: port,
|
||||||
|
open: true,
|
||||||
|
proxy: {
|
||||||
|
// detail: https://cli.vuejs.org/config/#devserver-proxy
|
||||||
|
[process.env.VUE_APP_BASE_API]: {
|
||||||
|
target: `http://localhost:8068`,
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: {
|
||||||
|
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disableHostCheck: true
|
||||||
|
},
|
||||||
|
configureWebpack: {
|
||||||
|
name: name,
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve('src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chainWebpack(config) {
|
||||||
|
config.plugins.delete('preload') // TODO: need test
|
||||||
|
config.plugins.delete('prefetch') // TODO: need test
|
||||||
|
|
||||||
|
// set svg-sprite-loader
|
||||||
|
config.module
|
||||||
|
.rule('svg')
|
||||||
|
.exclude.add(resolve('src/assets/icons'))
|
||||||
|
.end()
|
||||||
|
config.module
|
||||||
|
.rule('icons')
|
||||||
|
.test(/\.svg$/)
|
||||||
|
.include.add(resolve('src/assets/icons'))
|
||||||
|
.end()
|
||||||
|
.use('svg-sprite-loader')
|
||||||
|
.loader('svg-sprite-loader')
|
||||||
|
.options({
|
||||||
|
symbolId: 'icon-[name]'
|
||||||
|
})
|
||||||
|
.end()
|
||||||
|
|
||||||
|
config
|
||||||
|
.when(process.env.NODE_ENV !== 'development',
|
||||||
|
config => {
|
||||||
|
config
|
||||||
|
.optimization.splitChunks({
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
libs: {
|
||||||
|
name: 'chunk-libs',
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
priority: 10,
|
||||||
|
chunks: 'initial' // only package third parties that are initially dependent
|
||||||
|
},
|
||||||
|
elementUI: {
|
||||||
|
name: 'chunk-elementUI', // split elementUI into a single package
|
||||||
|
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
||||||
|
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
|
||||||
|
},
|
||||||
|
commons: {
|
||||||
|
name: 'chunk-commons',
|
||||||
|
test: resolve('src/components'), // can customize your rules
|
||||||
|
minChunks: 3, // minimum common number
|
||||||
|
priority: 5,
|
||||||
|
reuseExistingChunk: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
config.optimization.runtimeChunk('single'),
|
||||||
|
{
|
||||||
|
from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
|
||||||
|
to: './', //到根目录下
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user