pref: code optimization. (#213)

* pref: code optimization.

* fix: theme configuration loading.

* pref: code optimization.
pull/214/head
Ryan Wang 2020-07-13 09:32:07 +08:00 committed by GitHub
parent e433a17a69
commit 4612ce592b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 2777 additions and 1690 deletions

261
package-lock.json generated
View File

@ -1007,6 +1007,16 @@
"minimist": "^1.2.0"
}
},
"@fullhuman/postcss-purgecss": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz",
"integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==",
"dev": true,
"requires": {
"postcss": "7.0.32",
"purgecss": "^2.3.0"
}
},
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@ -2712,6 +2722,31 @@
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
"dev": true
},
"acorn-node": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
"integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
"dev": true,
"requires": {
"acorn": "^7.0.0",
"acorn-walk": "^7.0.0",
"xtend": "^4.0.2"
},
"dependencies": {
"acorn": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==",
"dev": true
},
"acorn-walk": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
"dev": true
}
}
},
"acorn-walk": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
@ -4424,6 +4459,12 @@
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true
},
"caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@ -5616,6 +5657,12 @@
}
}
},
"css-unit-converter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==",
"dev": true
},
"css-what": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz",
@ -6034,6 +6081,12 @@
}
}
},
"defined": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
"dev": true
},
"del": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
@ -6123,6 +6176,17 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
"detective": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
"integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
"dev": true,
"requires": {
"acorn-node": "^1.6.1",
"defined": "^1.0.0",
"minimist": "^1.1.1"
}
},
"diff-sequences": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
@ -11577,6 +11641,12 @@
"integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
"dev": true
},
"normalize.css": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==",
"dev": true
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@ -12384,6 +12454,53 @@
"postcss": "^7.0.0"
}
},
"postcss-functions": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz",
"integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=",
"dev": true,
"requires": {
"glob": "^7.1.2",
"object-assign": "^4.1.1",
"postcss": "^6.0.9",
"postcss-value-parser": "^3.3.0"
},
"dependencies": {
"postcss": {
"version": "6.0.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"source-map": "^0.6.1",
"supports-color": "^5.4.0"
}
},
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"postcss-js": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz",
"integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==",
"dev": true,
"requires": {
"camelcase-css": "^2.0.1",
"postcss": "^7.0.18"
}
},
"postcss-load-config": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
@ -12592,6 +12709,16 @@
"postcss": "^7.0.6"
}
},
"postcss-nested": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz",
"integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==",
"dev": true,
"requires": {
"postcss": "^7.0.32",
"postcss-selector-parser": "^6.0.2"
}
},
"postcss-normalize-charset": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
@ -12917,6 +13044,12 @@
"react-is": "^16.8.4"
}
},
"pretty-hrtime": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
"dev": true
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -13061,6 +13194,26 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"purgecss": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/purgecss/-/purgecss-2.3.0.tgz",
"integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==",
"dev": true,
"requires": {
"commander": "^5.0.0",
"glob": "^7.0.0",
"postcss": "7.0.32",
"postcss-selector-parser": "^6.0.2"
},
"dependencies": {
"commander": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
"dev": true
}
}
},
"q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
@ -13264,6 +13417,24 @@
"util.promisify": "^1.0.0"
}
},
"reduce-css-calc": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz",
"integrity": "sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==",
"dev": true,
"requires": {
"css-unit-converter": "^1.1.1",
"postcss-value-parser": "^3.3.0"
},
"dependencies": {
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true
}
}
},
"regenerate": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
@ -14754,6 +14925,96 @@
}
}
},
"tailwindcss": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.4.6.tgz",
"integrity": "sha512-qV0qInUq1FWih39Bc5CWECdgObSzRrbjGD4ke4kAPSIq6WXrPhv0wwOcUWJgJ66ltT9j+XnSRYikG8WNRU/fTQ==",
"dev": true,
"requires": {
"@fullhuman/postcss-purgecss": "^2.1.2",
"autoprefixer": "^9.4.5",
"browserslist": "^4.12.0",
"bytes": "^3.0.0",
"chalk": "^4.0.0",
"color": "^3.1.2",
"detective": "^5.2.0",
"fs-extra": "^8.0.0",
"lodash": "^4.17.15",
"node-emoji": "^1.8.1",
"normalize.css": "^8.0.1",
"postcss": "^7.0.11",
"postcss-functions": "^3.0.0",
"postcss-js": "^2.0.0",
"postcss-nested": "^4.1.1",
"postcss-selector-parser": "^6.0.0",
"pretty-hrtime": "^1.0.3",
"reduce-css-calc": "^2.1.6",
"resolve": "^1.14.2"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"tapable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",

View File

@ -51,6 +51,7 @@
"less": "^3.11.3",
"less-loader": "^5.0.0",
"lint-staged": "^10.2.11",
"tailwindcss": "^1.4.6",
"vue-svg-component-runtime": "^1.0.1",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.6.11"
@ -131,11 +132,6 @@
"git add"
]
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: [
require('autoprefixer'),
require('tailwindcss')
]
}

View File

@ -1,6 +1,6 @@
<template>
<a-locale-provider :locale="locale">
<div id="app">
<div id="app" class="h-full">
<router-view />
</div>
</a-locale-provider>
@ -38,9 +38,3 @@ export default {
}
}
</script>
<style>
#app {
height: 100%;
}
</style>

View File

@ -29,7 +29,7 @@ logApi.clear = () => {
})
}
logApi.logType = {
logApi.logTypes = {
BLOG_INITIALIZED: {
value: 0,
text: '博客初始化'

View File

@ -0,0 +1,2 @@
@tailwind components;
@tailwind utilities;

View File

@ -24,12 +24,8 @@
type="flex"
align="middle"
>
<a-skeleton
active
:loading="skeletonLoading"
:paragraph="{ rows: 18 }"
>
<a-col :span="24">
<a-col :span="24">
<a-spin :spinning="loading">
<a-empty v-if="attachments.length==0" />
<div
v-else
@ -45,8 +41,8 @@
loading="lazy"
>
</div>
</a-col>
</a-skeleton>
</a-spin>
</a-col>
</a-row>
<a-divider />
<div class="page-wrapper">
@ -61,7 +57,7 @@
<div class="bottom-control">
<a-button
type="dashed"
style="marginRight: 8px"
class="mr-2"
v-if="isChooseAvatar"
@click="handleSelectGravatar"
>使用 Gravatar</a-button>
@ -123,7 +119,7 @@ export default {
data() {
return {
uploadVisible: false,
skeletonLoading: true,
loading: true,
pagination: {
page: 1,
size: 12,
@ -143,29 +139,30 @@ export default {
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
this.loadAttachments()
this.handleListAttachments()
}
}
},
methods: {
loadSkeleton() {
this.skeletonLoading = true
setTimeout(() => {
this.skeletonLoading = false
}, 500)
},
handleShowUploadModal() {
this.uploadVisible = true
},
loadAttachments() {
handleListAttachments() {
this.loading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
attachmentApi.query(this.queryParam).then(response => {
this.attachments = response.data.data.content
this.pagination.total = response.data.data.total
})
attachmentApi
.query(this.queryParam)
.then(response => {
this.attachments = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
@ -179,11 +176,10 @@ export default {
handlePaginationChange(page, pageSize) {
this.pagination.page = page
this.pagination.size = pageSize
this.loadAttachments()
this.handleListAttachments()
},
onUploadClose() {
this.$refs.upload.handleClearFileList()
this.loadSkeleton()
this.handlePaginationChange(1, this.pagination.size)
},
handleJudgeMediaType(attachment) {

View File

@ -1,9 +1,9 @@
<template>
<div :class="prefixCls">
<div style="float: left">
<div class="float-left">
<slot name="extra">{{ extra }}</slot>
</div>
<div style="float: right">
<div class="float-right">
<slot></slot>
</div>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div
class="footer"
style="padding: 0 16px;margin: 48px 0 0;text-align: center;"
class="footer text-center"
style="padding: 0 16px;margin: 48px 0 0;"
>
<div
class="copyright"
@ -9,7 +9,7 @@
>
Proudly power by
<router-link :to="{ name:'About' }">
<a href="javascript:void(0);">Halo</a>
Halo
</router-link>
</div>
</div>
@ -17,12 +17,6 @@
<script>
export default {
name: 'GlobalFooter',
data() {
return {}
}
name: 'GlobalFooter'
}
</script>
<style lang="less" scoped>
</style>

View File

@ -7,7 +7,7 @@
<a-layout-header
v-if="visible"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
:style="{ padding: '0' }"
class="p-0"
>
<div
v-if="mode === 'sidemenu'"

View File

@ -10,7 +10,7 @@
:visible="layoutSetting"
>
<div class="setting-drawer-index-content">
<div :style="{ marginBottom: '24px' }">
<div class="mb-6">
<h3 class="setting-drawer-index-title">整体风格设置</h3>
<div class="setting-drawer-index-blockChecbox">
<a-tooltip>
@ -53,9 +53,9 @@
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<div class="mb-6">
<h3 class="setting-drawer-index-title">主题色</h3>
<div style="height: 20px">
<div class="h-5">
<a-tooltip
class="setting-drawer-theme-color-colorBlock"
v-for="(item, index) in colorList"
@ -75,7 +75,7 @@
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<div class="mb-6">
<h3 class="setting-drawer-index-title">导航模式</h3>
<div class="setting-drawer-index-blockChecbox">
@ -113,7 +113,7 @@
</div>
</div>
<a-divider />
<div :style="{ marginTop: '24px' }">
<div class="mt-6">
<a-list :split="false">
<a-list-item>
<a-tooltip slot="actions">

View File

@ -40,6 +40,3 @@ export default {
}
}
</script>
<style scoped>
</style>

View File

@ -9,75 +9,79 @@
title="待审核评论"
>
<template slot="content">
<a-spin :spinning="loading">
<div class="custom-tab-wrapper">
<a-tabs>
<a-tab-pane
tab="文章"
key="1"
<div class="custom-tab-wrapper">
<a-tabs
v-model="activeKey"
@change="handleTabsChanged"
>
<a-tab-pane
tab="文章"
key="post"
>
<a-list
:loading="postCommentsLoading"
:dataSource="converttedPostComments"
>
<a-list :dataSource="converttedPostComments">
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta>
<a-avatar
style="background-color: white"
slot="avatar"
:src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'"
size="large"
/>
<template slot="title">
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane
tab="页面"
key="2"
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta>
<a-avatar
class="bg-white"
slot="avatar"
:src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'"
size="large"
/>
<template slot="title">
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane
tab="页面"
key="sheet"
>
<a-list
:loading="sheetCommentsLoading"
:dataSource="converttedSheetComments"
>
<a-list :dataSource="converttedSheetComments">
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta>
<a-avatar
style="background-color: white"
slot="avatar"
:src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'"
size="large"
/>
<template slot="title">
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
</a-tabs>
</div>
</a-spin>
<a-list-item
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta>
<a-avatar
class="bg-white"
slot="avatar"
:src="'//cn.gravatar.com/avatar/' + item.gravatarMd5 + '&d=mm'"
size="large"
/>
<template slot="title">
<a
:href="item.authorUrl"
target="_blank"
>{{ item.author }}</a><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
</a-tabs>
</div>
</template>
<span
@click="fetchComment"
class="header-comment"
>
<span class="header-comment">
<a-badge
dot
v-if="postComments.length > 0 || sheetComments.length > 0"
@ -99,15 +103,14 @@ export default {
name: 'HeaderComment',
data() {
return {
loading: false,
activeKey: 'post',
visible: false,
postComments: [],
sheetComments: []
postCommentsLoading: false,
sheetComments: [],
sheetCommentsLoading: false
}
},
created() {
this.getComment()
},
computed: {
converttedPostComments() {
return this.postComments.map(comment => {
@ -122,25 +125,58 @@ export default {
})
}
},
methods: {
fetchComment() {
if (!this.visible) {
this.loading = true
this.getComment()
} else {
this.loading = false
created() {
this.handleListPostAuditingComments(false)
this.handleListSheetAuditingComments(false)
},
watch: {
visible(value) {
if (value) {
if (this.activeKey === 'post') {
this.handleListPostAuditingComments(false)
} else if (this.activeKey === 'sheet') {
this.handleListSheetAuditingComments(false)
}
}
this.visible = !this.visible
}
},
methods: {
handleListPostAuditingComments(enableLoading = true) {
if (enableLoading) {
this.postCommentsLoading = true
}
commentApi
.latestComment('posts', 5, 'AUDITING')
.then(response => {
this.postComments = response.data.data
})
.finally(() => {
setTimeout(() => {
this.postCommentsLoading = false
}, 200)
})
},
getComment() {
commentApi.latestComment('posts', 5, 'AUDITING').then(response => {
this.postComments = response.data.data
this.loading = false
})
commentApi.latestComment('sheets', 5, 'AUDITING').then(response => {
this.sheetComments = response.data.data
this.loading = false
})
handleListSheetAuditingComments(enableLoading = true) {
if (enableLoading) {
this.sheetCommentsLoading = true
}
commentApi
.latestComment('sheets', 5, 'AUDITING')
.then(response => {
this.sheetComments = response.data.data
})
.finally(() => {
setTimeout(() => {
this.sheetCommentsLoading = false
}, 200)
})
},
handleTabsChanged(activeKey) {
if (activeKey === 'post') {
this.handleListPostAuditingComments()
} else if (activeKey === 'sheet') {
this.handleListSheetAuditingComments()
}
}
}
}

View File

@ -133,5 +133,3 @@ export default {
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -49,14 +49,9 @@ export default {
formdata.append('file', $file)
attachmentApi.upload(formdata).then(response => {
var responseObject = response.data
if (responseObject.status === 200) {
var HaloEditor = this.$refs.md
HaloEditor.$img2Url(pos, encodeURI(responseObject.data.path))
this.$message.success('图片上传成功!')
} else {
this.$message.error('图片上传失败:' + responseObject.message)
}
var HaloEditor = this.$refs.md
HaloEditor.$img2Url(pos, encodeURI(responseObject.data.path))
this.$message.success('图片上传成功!')
})
},
handleSaveDraft() {

View File

@ -842,7 +842,7 @@ body {
.vue-codemirror-wrap {
.CodeMirror {
height: 560px;
height: 700px;
}
.CodeMirror-gutters {
@ -875,4 +875,21 @@ body {
.ant-input-group-addon {
line-height: initial !important;
}
.theme-screenshot {
width: 100%;
margin: 0 auto;
position: relative;
padding-bottom: 56%;
overflow: hidden;
img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
}

View File

@ -10,7 +10,3 @@ export default {
name: 'BlankLayout'
}
</script>
<style scoped>
</style>

View File

@ -6,6 +6,7 @@ import Contextmenu from 'vue-contextmenujs'
import store from './store/'
import './logger'
import '@/assets/css/tailwind.css'
import './core/lazy_use'
import './permission'
import '@/utils/filter' // global filter

View File

@ -7,7 +7,7 @@
>
<a-col
:span="24"
style="padding-bottom: 12px;"
class="pb-3"
>
<a-card
:bordered="false"
@ -76,21 +76,16 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="handleResetParam()"
>重置</a-button>
<a-button @click="handleResetParam()"></a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div
class="table-operator"
style="margin-bottom: 0;"
>
<div class="table-operator mb-0">
<a-button
type="primary"
icon="cloud-upload"
@ -146,7 +141,7 @@
loading="lazy"
/>
</div>
<a-card-meta style="padding: 0.8rem;">
<a-card-meta class="p-3">
<ellipsis
:length="isMobile() ? 12 : 16"
tooltip
@ -194,7 +189,7 @@
v-if="selectAttachment"
:attachment="selectAttachment"
:addToPhoto="true"
@delete="loadAttachments()"
@delete="handleListAttachments()"
/>
</page-view>
</template>
@ -259,9 +254,9 @@ export default {
}
},
created() {
this.loadAttachments()
this.loadMediaTypes()
this.loadTypes()
this.handleListAttachments()
this.handleListMediaTypes()
this.handleListTypes()
},
destroyed: function() {
if (this.drawerVisible) {
@ -276,11 +271,11 @@ export default {
},
methods: {
...mapGetters(['color']),
loadAttachments() {
handleListAttachments() {
this.listLoading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
this.listLoading = true
attachmentApi
.query(this.queryParam)
.then(response => {
@ -293,7 +288,7 @@ export default {
}, 200)
})
},
loadMediaTypes() {
handleListMediaTypes() {
this.mediaTypesLoading = true
attachmentApi
.getMediaTypes()
@ -306,7 +301,7 @@ export default {
}, 200)
})
},
loadTypes() {
handleListTypes() {
this.typesLoading = true
attachmentApi
.getTypes()
@ -371,15 +366,15 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${size}`)
this.pagination.page = page
this.pagination.size = size
this.loadAttachments()
this.handleListAttachments()
},
handleResetParam() {
this.queryParam.keyword = null
this.queryParam.mediaType = null
this.queryParam.attachmentType = null
this.handlePaginationChange(1, this.pagination.size)
this.loadMediaTypes()
this.loadTypes()
this.handleListMediaTypes()
this.handleListTypes()
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
@ -387,8 +382,8 @@ export default {
onUploadClose() {
this.$refs.upload.handleClearFileList()
this.handlePaginationChange(1, this.pagination.size)
this.loadMediaTypes()
this.loadTypes()
this.handleListMediaTypes()
this.handleListTypes()
},
handleJudgeMediaType(attachment) {
var mediaType = attachment.mediaType
@ -448,11 +443,15 @@ export default {
title: '确定要批量删除选中的附件吗?',
content: '一旦删除不可恢复,请谨慎操作',
onOk() {
attachmentApi.deleteInBatch(that.batchSelectedAttachments).then(res => {
that.handleCancelMultipleSelection()
that.loadAttachments()
that.$message.success('删除成功')
})
attachmentApi
.deleteInBatch(that.batchSelectedAttachments)
.then(res => {
that.handleCancelMultipleSelection()
that.$message.success('删除成功')
})
.finally(() => {
that.handleListAttachments()
})
},
onCancel() {}
})

View File

@ -12,129 +12,116 @@
align="middle"
>
<a-col :span="24">
<a-skeleton
active
:loading="detailLoading"
:paragraph="{rows: 8}"
>
<div class="attach-detail-img">
<div v-show="nonsupportPreviewVisible"></div>
<a
:href="attachment.path"
target="_blank"
<div class="attach-detail-img">
<div v-show="nonsupportPreviewVisible"></div>
<a
:href="attachment.path"
target="_blank"
>
<img
:src="attachment.path"
v-show="photoPreviewVisible"
class="w-full"
loading="lazy"
>
<img
:src="attachment.path"
v-show="photoPreviewVisible"
style="width: 100%;"
loading="lazy"
>
</a>
<d-player
ref="player"
:options="videoOptions"
v-show="videoPreviewVisible"
class="video-player-box"
style="width: 100%;"
>
</d-player>
</div>
</a-skeleton>
</a>
<d-player
ref="player"
:options="videoOptions"
v-show="videoPreviewVisible"
class="video-player-box w-full"
>
</d-player>
</div>
</a-col>
<a-divider />
<a-col :span="24">
<a-skeleton
active
:loading="detailLoading"
:paragraph="{rows: 8}"
>
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
v-model="attachment.name"
@blur="doUpdateAttachment"
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
v-model="attachment.name"
@blur="doUpdateAttachment"
/>
</template>
<template
slot="description"
v-else
>{{ attachment.name }}</template>
<span slot="title">
附件名
<a href="javascript:void(0);">
<a-icon
type="edit"
@click="editable = !editable"
/>
</template>
<template
slot="description"
v-else
>{{ attachment.name }}</template>
<span slot="title">
附件名
<a href="javascript:void(0);">
<a-icon
type="edit"
@click="handleEditName"
/>
</a>
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.mediaType">
<span slot="title">附件类型</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.typeProperty">
<span slot="title">存储位置</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template slot="description">
{{ attachment.size | fileSizeFormat }}
</template>
<span slot="title">附件大小</span>
</a-list-item-meta>
</a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta :description="attachment.height+'x'+attachment.width">
<span slot="title">图片尺寸</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template slot="description">
{{ attachment.createTime | moment }}
</template>
<span slot="title">上传日期</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.path">
<span slot="title">
普通链接
<a
href="javascript:void(0);"
@click="handleCopyNormalLink"
>
<a-icon type="copy" />
</a>
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta>
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
<span slot="title">
Markdown 格式
<a
href="javascript:void(0);"
@click="handleCopyMarkdownLink"
>
<a-icon type="copy" />
</a>
</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-skeleton>
</a>
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.mediaType">
<span slot="title">附件类型</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.typeProperty">
<span slot="title">存储位置</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template slot="description">
{{ attachment.size | fileSizeFormat }}
</template>
<span slot="title">附件大小</span>
</a-list-item-meta>
</a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta :description="attachment.height+'x'+attachment.width">
<span slot="title">图片尺寸</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template slot="description">
{{ attachment.createTime | moment }}
</template>
<span slot="title">上传日期</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="attachment.path">
<span slot="title">
普通链接
<a
href="javascript:void(0);"
@click="handleCopyNormalLink"
>
<a-icon type="copy" />
</a>
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta>
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
<span slot="title">
Markdown 格式
<a
href="javascript:void(0);"
@click="handleCopyMarkdownLink"
>
<a-icon type="copy" />
</a>
</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-col>
</a-row>
<a-divider class="divider-transparent" />
@ -148,7 +135,7 @@
>
<a-button
type="dashed"
style="marginRight: 8px"
class="mr-2"
>添加到图库</a-button>
</a-popconfirm>
<a-popconfirm
@ -180,7 +167,6 @@ export default {
},
data() {
return {
detailLoading: true,
editable: false,
photo: {},
photoPreviewVisible: false,
@ -220,27 +206,13 @@ export default {
this.player = this.$refs.player
},
watch: {
visible: function(newValue, oldValue) {
this.$log.debug('old value', oldValue)
this.$log.debug('new value', newValue)
if (newValue) {
this.loadSkeleton()
}
},
attachment: function(newValue, oldValue) {
if (newValue) {
var attachment = newValue
this.handleJudgeMediaType(attachment)
this.handleJudgeMediaType(newValue)
}
}
},
methods: {
loadSkeleton() {
this.detailLoading = true
setTimeout(() => {
this.detailLoading = false
}, 500)
},
handleDeleteAttachment() {
attachmentApi.delete(this.attachment.id).then(response => {
this.$message.success('删除成功!')
@ -248,9 +220,6 @@ export default {
this.onClose()
})
},
handleEditName() {
this.editable = !this.editable
},
doUpdateAttachment() {
if (!this.attachment.name) {
this.$notification['error']({

View File

@ -24,12 +24,8 @@
type="flex"
align="middle"
>
<a-skeleton
active
:loading="skeletonLoading"
:paragraph="{ rows: 18 }"
>
<a-col :span="24">
<a-col :span="24">
<a-spin :spinning="loading">
<a-empty v-if="formattedDatas.length==0" />
<div
v-else
@ -46,8 +42,8 @@
loading="lazy"
>
</div>
</a-col>
</a-skeleton>
</a-spin>
</a-col>
</a-row>
<a-divider />
<div class="page-wrapper">
@ -63,12 +59,12 @@
v-model="detailVisible"
v-if="selectedAttachment"
:attachment="selectedAttachment"
@delete="handleDelete"
@delete="loadAttachments"
/>
<a-divider class="divider-transparent" />
<div class="bottom-control">
<a-button
@click="handleShowUploadModal"
@click="uploadVisible = true"
type="primary"
>上传附件</a-button>
</div>
@ -117,7 +113,7 @@ export default {
detailVisible: false,
attachmentDrawerVisible: false,
uploadVisible: false,
skeletonLoading: true,
loading: true,
pagination: {
page: 1,
size: 12,
@ -144,23 +140,13 @@ export default {
}
},
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
visible(value) {
if (value) {
this.loadAttachments()
}
}
},
methods: {
loadSkeleton() {
this.skeletonLoading = true
setTimeout(() => {
this.skeletonLoading = false
}, 500)
},
handleShowUploadModal() {
this.uploadVisible = true
},
handleShowDetailDrawer(attachment) {
this.selectedAttachment = attachment
this.$log.debug('Show detail of', attachment)
@ -208,13 +194,21 @@ export default {
return false
},
loadAttachments() {
this.loading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
attachmentApi.query(this.queryParam).then(response => {
this.attachments = response.data.data.content
this.pagination.total = response.data.data.total
})
attachmentApi
.query(this.queryParam)
.then(response => {
this.attachments = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
@ -226,12 +220,8 @@ export default {
},
onUploadClose() {
this.$refs.upload.handleClearFileList()
this.loadSkeleton()
this.handlePaginationChange(1, this.pagination.size)
},
handleDelete() {
this.loadAttachments()
},
handleJudgeMediaType(attachment) {
var mediaType = attachment.mediaType
//

View File

@ -12,104 +12,101 @@
align="middle"
>
<a-col :span="24">
<a-skeleton
active
:loading="detailLoading"
:paragraph="{rows: 8}"
>
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta :description="comment.author">
<span slot="title">评论者昵称</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="comment.email">
<span slot="title">评论者邮箱</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="comment.ipAddress">
<span slot="title">评论者 IP</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<a
slot="description"
target="_blank"
:href="comment.authorUrl"
>{{ comment.authorUrl }}</a>
<span slot="title">评论者网址</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<span slot="description">
<a-badge :status="comment.statusProperty.status" :text="comment.statusProperty.text"/>
</span>
<span slot="title">评论状态</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<a
slot="description"
target="_blank"
:href="comment.post.fullPath"
v-if="this.type=='posts'"
>{{ comment.post.title }}</a>
<a
slot="description"
target="_blank"
:href="comment.sheet.fullPath"
v-else-if="this.type=='sheets'"
>{{ comment.sheet.title }}</a>
<span
slot="title"
v-if="this.type=='posts'"
>评论文章</span>
<span
slot="title"
v-else-if="this.type=='sheets'"
>评论页面</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="comment.content"
/>
</template>
<span
slot="description"
v-html="comment.content"
v-else
></span>
<span slot="title">评论内容</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-skeleton>
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta :description="comment.author">
<span slot="title">评论者昵称</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="comment.email">
<span slot="title">评论者邮箱</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta :description="comment.ipAddress">
<span slot="title">评论者 IP</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<a
slot="description"
target="_blank"
:href="comment.authorUrl"
>{{ comment.authorUrl }}</a>
<span slot="title">评论者网址</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<span slot="description">
<a-badge
:status="comment.statusProperty.status"
:text="comment.statusProperty.text"
/>
</span>
<span slot="title">评论状态</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<a
slot="description"
target="_blank"
:href="comment.post.fullPath"
v-if="this.type=='posts'"
>{{ comment.post.title }}</a>
<a
slot="description"
target="_blank"
:href="comment.sheet.fullPath"
v-else-if="this.type=='sheets'"
>{{ comment.sheet.title }}</a>
<span
slot="title"
v-if="this.type=='posts'"
>评论文章</span>
<span
slot="title"
v-else-if="this.type=='sheets'"
>评论页面</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="comment.content"
/>
</template>
<span
slot="description"
v-html="comment.content"
v-else
></span>
<span slot="title">评论内容</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-col>
</a-row>
<a-divider class="divider-transparent" />
<div class="bottom-control">
<a-button
type="dashed"
style="marginRight: 8px"
class="mr-2"
@click="handleEditComment"
v-if="!editable"
>编辑</a-button>
<a-button
type="primary"
style="marginRight: 8px"
class="mr-2"
@click="handleUpdateComment"
v-if="editable"
>保存</a-button>
@ -132,7 +129,6 @@ export default {
components: {},
data() {
return {
detailLoading: true,
editable: false,
commentStatus: commentApi.commentStatus,
keys: ['blog_url']
@ -161,22 +157,7 @@ export default {
}
}
},
watch: {
visible: function(newValue, oldValue) {
this.$log.debug('old value', oldValue)
this.$log.debug('new value', newValue)
if (newValue) {
this.loadSkeleton()
}
}
},
methods: {
loadSkeleton() {
this.detailLoading = true
setTimeout(() => {
this.detailLoading = false
}, 500)
},
handleEditComment() {
this.editable = true
},

View File

@ -45,12 +45,10 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="handleResetParam()"
>重置</a-button>
<a-button @click="handleResetParam()"></a-button>
</span>
</a-col>
</a-row>
@ -100,7 +98,7 @@
</a-button>
</a-dropdown>
</div>
<div style="margin-top:15px">
<div class="mt-4">
<!-- Mobile -->
<a-list
v-if="isMobile()"
@ -558,7 +556,7 @@ export default {
}
},
created() {
this.loadComments()
this.handleListComments()
},
computed: {
formattedComments() {
@ -570,7 +568,7 @@ export default {
}
},
methods: {
loadComments() {
handleListComments() {
this.loading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
@ -592,16 +590,24 @@ export default {
this.handlePaginationChange(1, this.pagination.size)
},
handleEditStatusClick(commentId, status) {
commentApi.updateStatus(this.type, commentId, status).then(response => {
this.$message.success('操作成功!')
this.loadComments()
})
commentApi
.updateStatus(this.type, commentId, status)
.then(response => {
this.$message.success('操作成功!')
})
.finally(() => {
this.handleListComments()
})
},
handleDeleteClick(commentId) {
commentApi.delete(this.type, commentId).then(response => {
this.$message.success('删除成功!')
this.loadComments()
})
commentApi
.delete(this.type, commentId)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.handleListComments()
})
},
handleReplyAndPassClick(comment) {
this.handleReplyClick(comment)
@ -625,19 +631,23 @@ export default {
})
return
}
commentApi.create(this.type, this.replyComment).then(response => {
this.$message.success('回复成功!')
this.replyComment = {}
this.selectedComment = {}
this.replyCommentVisible = false
this.loadComments()
})
commentApi
.create(this.type, this.replyComment)
.then(response => {
this.$message.success('回复成功!')
this.replyComment = {}
this.selectedComment = {}
this.replyCommentVisible = false
})
.finally(() => {
this.handleListComments()
})
},
handlePaginationChange(page, pageSize) {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadComments()
this.handleListComments()
},
handleResetParam() {
this.queryParam.keyword = null
@ -650,22 +660,30 @@ export default {
this.$message.info('请至少选择一项!')
return
}
commentApi.updateStatusInBatch(this.type, this.selectedRowKeys, status).then(response => {
this.$log.debug(`commentIds: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = []
this.loadComments()
})
commentApi
.updateStatusInBatch(this.type, this.selectedRowKeys, status)
.then(response => {
this.$log.debug(`commentIds: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = []
})
.finally(() => {
this.handleListComments()
})
},
handleDeleteMore() {
if (this.selectedRowKeys.length <= 0) {
this.$message.info('请至少选择一项!')
return
}
commentApi.deleteInBatch(this.type, this.selectedRowKeys).then(response => {
this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = []
this.loadComments()
})
commentApi
.deleteInBatch(this.type, this.selectedRowKeys)
.then(response => {
this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = []
})
.finally(() => {
this.handleListComments()
})
},
handleClearRowKeys() {
this.selectedRowKeys = []

View File

@ -28,16 +28,18 @@
</a-col>
<a-divider />
<a-col :span="24">
<a-empty v-if="comments.length == 0" />
<TargetCommentTree
v-else
v-for="(comment, index) in comments"
:key="index"
:comment="comment"
@reply="handleCommentReply"
@delete="handleCommentDelete"
@editStatus="handleEditStatusClick"
/>
<a-spin :spinning="loading">
<a-empty v-if="comments.length == 0" />
<TargetCommentTree
v-else
v-for="(comment, index) in comments"
:key="index"
:comment="comment"
@reply="handleCommentReply"
@delete="handleCommentDelete"
@editStatus="handleEditStatusClick"
/>
</a-spin>
</a-col>
</a-row>
<a-divider />
@ -120,6 +122,7 @@ export default {
data() {
return {
comments: [],
loading: false,
selectedComment: {},
replyComment: {},
replyCommentVisible: false,
@ -170,24 +173,32 @@ export default {
this.$log.debug('old value', oldValue)
this.$log.debug('new value', newValue)
if (newValue) {
this.loadComments()
this.handleListComments()
}
}
},
methods: {
loadComments() {
handleListComments() {
this.loading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
commentApi.commentTree(this.target, this.id, this.queryParam).then(response => {
this.comments = response.data.data.content
this.pagination.total = response.data.data.total
})
commentApi
.commentTree(this.target, this.id, this.queryParam)
.then(response => {
this.comments = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handlePaginationChange(page, pageSize) {
this.pagination.page = page
this.pagination.size = pageSize
this.loadComments()
this.handleListComments()
},
handleCommentReply(comment) {
this.selectedComment = comment
@ -207,26 +218,38 @@ export default {
})
return
}
commentApi.create(this.target, this.replyComment).then(response => {
this.$message.success('回复成功!')
this.replyComment = {}
this.selectedComment = {}
this.replyCommentVisible = false
this.commentVisible = false
this.loadComments()
})
commentApi
.create(this.target, this.replyComment)
.then(response => {
this.$message.success('回复成功!')
this.replyComment = {}
this.selectedComment = {}
this.replyCommentVisible = false
this.commentVisible = false
})
.finally(() => {
this.handleListComments()
})
},
handleEditStatusClick(comment, status) {
commentApi.updateStatus(this.target, comment.id, status).then(response => {
this.$message.success('操作成功!')
this.loadComments()
})
commentApi
.updateStatus(this.target, comment.id, status)
.then(response => {
this.$message.success('操作成功!')
})
.finally(() => {
this.handleListComments()
})
},
handleCommentDelete(comment) {
commentApi.delete(this.target, comment.id).then(response => {
this.$message.success('删除成功!')
this.loadComments()
})
commentApi
.delete(this.target, comment.id)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.handleListComments()
})
},
onReplyClose() {
this.replyComment = {}

View File

@ -7,10 +7,9 @@
:md="12"
:sm="12"
:xs="12"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<analysis-card
:loading="statisticsLoading"
title="文章"
:number="statisticsData.postCount"
>
@ -18,7 +17,14 @@
:to="{ name:'PostEdit' }"
slot="action"
>
<a-icon type="plus" />
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="plus"
/>
</router-link>
</analysis-card>
</a-col>
@ -28,10 +34,9 @@
:md="12"
:sm="12"
:xs="12"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<analysis-card
:loading="statisticsLoading"
title="评论"
:number="statisticsData.commentCount"
>
@ -39,7 +44,14 @@
:to="{ name:'Comments' }"
slot="action"
>
<a-icon type="unordered-list" />
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="unordered-list"
/>
</router-link>
</analysis-card>
</a-col>
@ -49,10 +61,9 @@
:md="12"
:sm="12"
:xs="12"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<analysis-card
:loading="statisticsLoading"
title="阅读量"
:number="statisticsData.visitCount"
>
@ -61,7 +72,14 @@
文章阅读共 {{ statisticsData.visitCount }}
</template>
<a href="javascript:void(0);">
<a-icon type="info-circle-o" />
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="info-circle-o"
/>
</a>
</a-tooltip>
</analysis-card>
@ -72,17 +90,23 @@
:md="12"
:sm="12"
:xs="12"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<analysis-card
:loading="statisticsLoading"
title="建立天数"
:number="statisticsData.establishDays"
>
<a-tooltip slot="action">
<template slot="title">博客建立于 {{ statisticsData.birthday | moment }}</template>
<a href="javascript:void(0);">
<a-icon type="info-circle-o" />
<a-icon
v-if="statisticsLoading"
type="loading"
/>
<a-icon
v-else
type="info-circle-o"
/>
</a>
</a-tooltip>
</analysis-card>
@ -95,10 +119,9 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
:loading="activityLoading"
:bordered="false"
title="新动态"
:bodyStyle="{ padding: 0 }"
@ -109,7 +132,10 @@
key="1"
tab="最近文章"
>
<a-list :dataSource="latestPosts">
<a-list
:loading="activityLoading"
:dataSource="latestPosts"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
@ -178,11 +204,10 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
:bordered="false"
:loading="writeLoading"
:bodyStyle="{ padding: '16px' }"
>
<template slot="title">
@ -218,10 +243,9 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
:loading="logLoading"
:bordered="false"
:bodyStyle="{ padding: '16px' }"
>
@ -239,7 +263,10 @@
</a>
</a-tooltip>
</template>
<a-list :dataSource="formattedLogDatas">
<a-list
:dataSource="formattedLogDatas"
:loading="logLoading"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
@ -287,9 +314,8 @@ export default {
},
data() {
return {
logType: logApi.logType,
logTypes: logApi.logTypes,
activityLoading: false,
writeLoading: false,
logLoading: false,
statisticsLoading: true,
logListDrawerVisible: false,
@ -303,14 +329,14 @@ export default {
}
},
created() {
this.getStatistics()
this.listLatestPosts()
this.listLatestLogs()
this.handleLoadStatistics()
this.handleListLatestPosts()
this.handleListLatestLogs()
},
computed: {
formattedLogDatas() {
return this.latestLogs.map(log => {
log.type = this.logType[log.type].text
log.type = this.logTypes[log.type].text
return log
})
}
@ -323,7 +349,7 @@ export default {
beforeRouteEnter(to, from, next) {
next(vm => {
vm.interval = setInterval(() => {
vm.getStatistics()
vm.handleLoadStatistics()
}, 5000)
})
},
@ -339,7 +365,7 @@ export default {
next()
},
methods: {
async listLatestPosts() {
handleListLatestPosts() {
this.activityLoading = true
postApi
.listLatest(5)
@ -352,9 +378,8 @@ export default {
}, 200)
})
},
async listLatestLogs() {
handleListLatestLogs() {
this.logLoading = true
this.writeLoading = true
logApi
.listLatest(5)
.then(response => {
@ -363,11 +388,10 @@ export default {
.finally(() => {
setTimeout(() => {
this.logLoading = false
this.writeLoading = false
}, 200)
})
},
async getStatistics() {
handleLoadStatistics() {
statisticsApi
.statistics()
.then(response => {
@ -402,6 +426,7 @@ export default {
},
handleLogListClose() {
this.logListDrawerVisible = false
this.handleListLatestLogs()
}
}
}

View File

@ -1,6 +1,5 @@
<template>
<a-card
:loading="loading"
:body-style="{ padding: '24px' }"
:bordered="false"
>
@ -44,11 +43,6 @@ export default {
type: Number,
required: false,
default: 0
},
loading: {
type: Boolean,
required: false,
default: false
}
},
data() {

View File

@ -13,27 +13,24 @@
align="middle"
>
<a-col :span="24">
<a-skeleton
active
<a-list
:loading="loading"
:paragraph="{rows: 18}"
:dataSource="formattedLogsDatas"
>
<a-list :dataSource="formattedLogsDatas">
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-list-item-meta :description="item.createTime | timeAgo">
<span slot="title">{{ item.type }}</span>
</a-list-item-meta>
<ellipsis
:length="35"
tooltip
>{{ item.content }}</ellipsis>
</a-list-item>
</a-list>
</a-skeleton>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<a-list-item-meta :description="item.createTime | timeAgo">
<span slot="title">{{ item.type }}</span>
</a-list-item-meta>
<ellipsis
:length="35"
tooltip
>{{ item.content }}</ellipsis>
</a-list-item>
</a-list>
<div class="page-wrapper">
<a-pagination
@ -72,7 +69,7 @@ export default {
mixins: [mixin, mixinDevice],
data() {
return {
logType: logApi.logType,
logTypes: logApi.logTypes,
loading: true,
logs: [],
pagination: {
@ -98,46 +95,51 @@ export default {
computed: {
formattedLogsDatas() {
return this.logs.map(log => {
log.type = this.logType[log.type].text
log.type = this.logTypes[log.type].text
return log
})
}
},
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
this.loadLogs()
visible(value) {
if (value) {
this.handleListLogs()
}
}
},
methods: {
loadSkeleton() {
handleListLogs() {
this.loading = true
setTimeout(() => {
this.loading = false
}, 500)
},
loadLogs() {
this.logQueryParam.page = this.pagination.page - 1
this.logQueryParam.size = this.pagination.size
this.logQueryParam.sort = this.pagination.sort
logApi.pageBy(this.logQueryParam).then(response => {
this.logs = response.data.data.content
this.pagination.total = response.data.data.total
})
logApi
.pageBy(this.logQueryParam)
.then(response => {
this.logs = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleClearLogs() {
logApi.clear().then(response => {
this.$message.success('清除成功!')
this.loadLogs()
})
logApi
.clear()
.then(response => {
this.$message.success('清除成功!')
})
.finally(() => {
this.handleListLogs()
})
},
handlePaginationChange(page, pageSize) {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadLogs()
this.handleListLogs()
},
onClose() {
this.$emit('close', false)

View File

@ -103,15 +103,21 @@ export default {
}
},
created() {
this.loadComments()
this.handleListTargetComments()
},
methods: {
loadComments() {
handleListTargetComments() {
this.loading = true
commentApi.latestComment(this.type, 5, 'PUBLISHED').then(response => {
this.comments = response.data.data
this.loading = false
})
commentApi
.latestComment(this.type, 5, 'PUBLISHED')
.then(response => {
this.comments = response.data.data
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handlePostPreview(postId) {
postApi.preview(postId).then(response => {

View File

@ -7,7 +7,7 @@
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="mb-3"
>
<a-card
:title="title"
@ -104,7 +104,7 @@
>返回添加</a-button>
</a-button-group>
<a
:style="{ marginLeft: '8px'}"
class="ml-2"
@click="form.moreField = !form.moreField"
>
更多选项
@ -120,7 +120,7 @@
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
title="所有菜单"

View File

@ -1,13 +1,50 @@
<template>
<div>
<a-row :gutter="12">
<a-col
:xl="6"
:lg="6"
:md="6"
:sm="24"
:xs="24"
class="pb-3"
>
<a-card :bodyStyle="{ padding: '16px' }">
<template slot="title">
<a-select
class="w-full"
@change="onSelectTheme"
v-model="selectedTheme.id"
:loading="themesLoading"
>
<a-select-option
v-for="(theme,index) in themes"
:key="index"
:value="theme.id"
>{{ theme.name }}
<a-icon
v-if="theme.activated"
type="check"
/>
</a-select-option>
</a-select>
</template>
<a-spin :spinning="filesLoading">
<theme-file
v-if="files"
:files="files"
@listenToSelect="handleSelectFile"
/>
</a-spin>
</a-card>
</a-col>
<a-col
:xl="18"
:lg="18"
:md="18"
:sm="24"
:xs="24"
:style="{'padding-bottom':'12px'}"
class="pb-3"
>
<a-card :bodyStyle="{ padding: '16px' }">
<a-form layout="vertical">
@ -28,40 +65,6 @@
</a-form>
</a-card>
</a-col>
<a-col
:xl="6"
:lg="6"
:md="6"
:sm="24"
:xs="24"
:style="{'padding-bottom':'12px'}"
>
<a-card :bodyStyle="{ padding: '16px' }">
<template slot="title">
<a-select
style="width: 100%"
@change="onSelectTheme"
v-model="selectedTheme.id"
>
<a-select-option
v-for="(theme,index) in themes"
:key="index"
:value="theme.id"
>{{ theme.name }}
<a-icon
v-if="theme.activated"
type="check"
/>
</a-select-option>
</a-select>
</template>
<theme-file
v-if="files"
:files="files"
@listenToSelect="handleSelectFile"
/>
</a-card>
</a-col>
</a-row>
</div>
</template>
@ -85,40 +88,68 @@ export default {
lineNumbers: true,
line: true
},
files: null,
files: [],
filesLoading: false,
file: {},
content: '',
themes: [],
themesLoading: false,
selectedTheme: {},
saving: false
}
},
created() {
this.loadActivatedTheme()
this.loadFiles()
this.loadThemes()
this.handleGetActivatedTheme()
this.handleListThemeFiles()
this.handleListThemes()
},
methods: {
loadActivatedTheme() {
handleGetActivatedTheme() {
themeApi.getActivatedTheme().then(response => {
this.selectedTheme = response.data.data
})
},
loadFiles() {
themeApi.listFilesActivated().then(response => {
this.files = response.data.data
})
handleListThemeFiles() {
this.filesLoading = true
themeApi
.listFilesActivated()
.then(response => {
this.files = response.data.data
})
.finally(() => {
setTimeout(() => {
this.filesLoading = false
}, 200)
})
},
loadThemes() {
themeApi.listAll().then(response => {
this.themes = response.data.data
})
handleListThemes() {
this.themesLoading = true
themeApi
.listAll()
.then(response => {
this.themes = response.data.data
})
.finally(() => {
setTimeout(() => {
this.themesLoading = false
}, 200)
})
},
onSelectTheme(themeId) {
this.files = null
themeApi.listFiles(themeId).then(response => {
this.files = response.data.data
})
this.files = []
this.filesLoading = true
themeApi
.listFiles(themeId)
.then(response => {
this.files = response.data.data
this.content = ''
this.file = {}
})
.finally(() => {
setTimeout(() => {
this.filesLoading = false
}, 200)
})
},
handleSelectFile(file) {
const _this = this

View File

@ -21,7 +21,7 @@
:title="item.name"
:bodyStyle="{ padding: 0 }"
>
<div class="theme-thumb">
<div class="theme-screenshot">
<img
:alt="item.name"
:src="item.screenshots || '/images/placeholder.jpg'"
@ -106,13 +106,13 @@
</a-col>
</a-row>
<ThemeSetting
<ThemeSettingDrawer
:theme="selectedTheme"
v-if="themeSettingVisible"
v-model="themeSettingVisible"
@close="onThemeSettingsClose"
/>
<div class="upload-button">
<div style="position: fixed;bottom: 30px;right: 30px;">
<a-dropdown
placement="topLeft"
:trigger="['click']"
@ -289,11 +289,11 @@
</template>
<script>
import ThemeSetting from './components/ThemeSetting'
import ThemeSettingDrawer from './components/ThemeSettingDrawer'
import themeApi from '@/api/theme'
export default {
components: {
ThemeSetting
ThemeSettingDrawer
},
data() {
return {
@ -323,7 +323,7 @@ export default {
}
},
created() {
this.loadThemes()
this.handleListThemes()
},
destroyed: function() {
if (this.themeSettingVisible) {
@ -337,7 +337,7 @@ export default {
next()
},
methods: {
loadThemes() {
handleListThemes() {
this.themeLoading = true
themeApi
.listAll()
@ -350,12 +350,15 @@ export default {
}, 200)
})
},
activeTheme(themeId) {
themeApi.active(themeId).then(response => {
this.$message.success('设置成功!')
this.loadThemes()
})
themeApi
.active(themeId)
.then(response => {
this.$message.success('设置成功!')
})
.finally(() => {
this.handleListThemes()
})
},
handleUpdateTheme(themeId) {
const hide = this.$message.loading('更新中...', 0)
@ -363,17 +366,21 @@ export default {
.update(themeId)
.then(response => {
this.$message.success('更新成功!')
this.loadThemes()
})
.finally(() => {
hide()
this.handleListThemes()
})
},
handleDeleteTheme(themeId) {
themeApi.delete(themeId).then(response => {
this.$message.success('删除成功!')
this.loadThemes()
})
themeApi
.delete(themeId)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.handleListThemes()
})
},
handleUploadSuccess() {
if (this.uploadThemeVisible) {
@ -385,7 +392,7 @@ export default {
if (this.fetchBranches) {
this.fetchBranches = false
}
this.loadThemes()
this.handleListThemes()
},
handleEditClick(theme) {
this.settingDrawer(theme)
@ -412,28 +419,42 @@ export default {
this.releases = response.data.data
})
.finally(() => {
this.fetchButtonLoading = false
setTimeout(() => {
this.fetchButtonLoading = false
}, 200)
})
},
handleBranchFetching() {
themeApi.fetchingBranch(this.fetchingUrl, this.branches[this.selectedBranch].branch).then(response => {
this.$message.success('拉取成功')
this.uploadThemeVisible = false
this.loadThemes()
})
themeApi
.fetchingBranch(this.fetchingUrl, this.branches[this.selectedBranch].branch)
.then(response => {
this.$message.success('拉取成功')
this.uploadThemeVisible = false
})
.finally(() => {
this.handleListThemes()
})
},
handleReleaseFetching() {
themeApi.fetchingRelease(this.fetchingUrl, this.releases[this.selectedBranch].branch).then(response => {
this.$message.success('拉取成功')
this.uploadThemeVisible = false
this.loadThemes()
})
themeApi
.fetchingRelease(this.fetchingUrl, this.releases[this.selectedBranch].branch)
.then(response => {
this.$message.success('拉取成功')
this.uploadThemeVisible = false
})
.finally(() => {
this.handleListThemes()
})
},
handleReload() {
themeApi.reload().then(response => {
this.loadThemes()
this.$message.success('刷新成功!')
})
themeApi
.reload()
.then(response => {
this.$message.success('刷新成功!')
})
.finally(() => {
this.handleListThemes()
})
},
handleShowUpdateNewThemeModal(item) {
this.prepareUpdateTheme = item
@ -444,13 +465,13 @@ export default {
this.themeSettingVisible = true
},
handleConfirmDelete(item) {
const that = this
const _this = this
this.$confirm({
title: '提示',
maskClosable: true,
content: '确定删除【' + item.name + '】主题?',
onOk() {
that.handleDeleteTheme(item.id)
_this.handleDeleteTheme(item.id)
},
onCancel() {}
})
@ -483,7 +504,7 @@ export default {
if (this.selectedBranch) {
this.selectedBranch = null
}
this.loadThemes()
this.handleListThemes()
},
onThemeSettingsClose() {
this.themeSettingVisible = false
@ -492,41 +513,3 @@ export default {
}
}
</script>
<style lang="less">
@keyframes scaleDraw {
0% {
transform: scale(1);
}
25% {
transform: scale(1.3);
}
50% {
transform: scale(1);
}
75% {
transform: scale(1.3);
}
}
.upload-button {
-webkit-animation: scaleDraw 4s ease-in-out infinite;
position: fixed;
bottom: 30px;
right: 30px;
}
.theme-thumb {
width: 100%;
margin: 0 auto;
position: relative;
padding-bottom: 56%;
overflow: hidden;
img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<a-drawer
:title="selectedTheme.name + ' 主题设置'"
:title="`${theme.name} 主题设置`"
width="100%"
placement="right"
closable
@ -20,38 +20,32 @@
:xs="24"
v-if="!viewMode"
>
<a-skeleton
active
:loading="settingLoading"
:paragraph="{rows: 10}"
>
<a-card :bordered="false">
<img
:alt="selectedTheme.name"
:src="selectedTheme.screenshots"
slot="cover"
>
<a-card-meta :description="selectedTheme.description">
<template slot="title">
<a
:href="selectedTheme.author.website"
target="_blank"
>{{ selectedTheme.author.name }}</a>
</template>
<a-avatar
v-if="selectedTheme.logo"
:src="selectedTheme.logo"
size="large"
slot="avatar"
/>
<a-avatar
v-else
size="large"
slot="avatar"
>{{ selectedTheme.author.name }}</a-avatar>
</a-card-meta>
</a-card>
</a-skeleton>
<a-card :bordered="false">
<img
:alt="theme.name"
:src="theme.screenshots"
slot="cover"
>
<a-card-meta :description="theme.description">
<template slot="title">
<a
:href="author.website"
target="_blank"
>{{ author.name }}</a>
</template>
<a-avatar
v-if="theme.logo"
:src="theme.logo"
size="large"
slot="avatar"
/>
<a-avatar
v-else
size="large"
slot="avatar"
>{{ author.name }}</a-avatar>
</a-card-meta>
</a-card>
</a-col>
<a-col
:xl="formColValue"
@ -61,19 +55,15 @@
:xs="24"
style="padding-bottom: 50px;"
>
<a-skeleton
active
:loading="settingLoading"
:paragraph="{rows: 20}"
>
<a-spin :spinning="settingLoading">
<div class="card-container">
<a-tabs
type="card"
defaultActiveKey="0"
v-if="themeConfiguration.length>0"
v-if="themeConfigurations.length>0"
>
<a-tab-pane
v-for="(group, index) in themeConfiguration"
v-for="(group, index) in themeConfigurations"
:key="index.toString()"
:tab="group.label"
>
@ -172,10 +162,10 @@
<a-alert
message="当前主题暂无设置选项"
banner
v-else
v-if="themeConfigurations.length <=0 && !settingLoading"
/>
</div>
</a-skeleton>
</a-spin>
</a-col>
<a-col
@ -212,21 +202,21 @@
/>
<footer-tool-bar
v-if="themeConfiguration.length>0"
:style="{ width : '100%'}"
v-if="themeConfigurations.length>0"
class="w-full"
>
<a-button
v-if="!this.isMobile() && theme.activated && viewMode"
type="primary"
@click="toggleViewMode"
style="marginRight: 8px"
class="mr-2"
ghost
>普通模式</a-button>
<a-button
v-else-if="!this.isMobile() && theme.activated && !viewMode"
type="dashed"
@click="toggleViewMode"
style="marginRight: 8px"
class="mr-2"
>预览模式</a-button>
<a-button
type="primary"
@ -253,10 +243,9 @@ export default {
data() {
return {
attachmentDrawerVisible: false,
selectedTheme: this.theme,
themeConfiguration: [],
themeConfigurations: [],
themeSettings: [],
settingLoading: true,
settingLoading: false,
selectedField: '',
wrapperCol: {
xl: { span: 12 },
@ -277,52 +266,55 @@ export default {
props: {
theme: {
type: Object,
required: true
required: true,
default: () => {}
},
visible: {
type: Boolean,
required: false,
default: true
default: false
}
},
created() {
this.loadSkeleton()
this.initData()
},
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
visible(value) {
if (value) {
this.handleFetchConfiguration()
}
}
},
computed: {
...mapGetters(['options'])
...mapGetters(['options']),
author() {
if (this.theme.author) {
return this.theme.author
}
return {}
}
},
methods: {
loadSkeleton() {
async handleFetchConfiguration() {
this.settingLoading = true
setTimeout(() => {
this.settingLoading = false
}, 500)
await themeApi.fetchConfiguration(this.theme.id).then(response => {
this.themeConfigurations = response.data.data
})
this.handleFetchSettings()
},
initData() {
this.settingLoading = true
themeApi.fetchConfiguration(this.selectedTheme.id).then(response => {
this.themeConfiguration = response.data.data
themeApi.fetchSettings(this.selectedTheme.id).then(response => {
handleFetchSettings() {
themeApi
.fetchSettings(this.theme.id)
.then(response => {
this.themeSettings = response.data.data
})
.finally(() => {
setTimeout(() => {
this.settingLoading = false
}, 300)
}, 200)
})
})
},
handleSaveSettings() {
this.saving = true
themeApi
.saveSettings(this.selectedTheme.id, this.themeSettings)
.saveSettings(this.theme.id, this.themeSettings)
.then(response => {
this.$message.success('保存成功!')
if (this.viewMode) {

View File

@ -7,7 +7,7 @@
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
:title="title"
@ -96,7 +96,7 @@
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '1rem' }"
class="pb-3"
>
<a-card
title="分类列表"
@ -187,7 +187,7 @@
<span
slot="postCount"
slot-scope="text,record"
style="cursor: pointer;"
class="cursor-pointer"
@click="handleQueryCategoryPosts(record)"
>
<a-badge

View File

@ -2,7 +2,7 @@
<div>
<a-row :gutter="12">
<a-col :span="24">
<div style="margin-bottom: 16px">
<div class="mb-4">
<a-input
v-model="postToStage.title"
size="large"
@ -32,7 +32,7 @@
:categoryIds="selectedCategoryIds"
:metas="selectedMetas"
:visible="postSettingVisible"
@close="onPostSettingsClose"
@close="postSettingVisible = false"
@onRefreshPost="onRefreshPostFromSetting"
@onRefreshTagIds="onRefreshTagIdsFromSetting"
@onRefreshCategoryIds="onRefreshCategoryIdsFromSetting"
@ -45,23 +45,23 @@
<footer-tool-bar :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
<a-button
type="danger"
class="mr-2"
@click="handleSaveDraft(false)"
:loading="draftSaving"
>保存草稿</a-button>
<a-button
@click="handlePreview"
style="margin-left: 8px;"
class="mr-2"
:loading="previewSaving"
>预览</a-button>
<a-button
type="primary"
@click="handleShowPostSetting"
style="margin-left: 8px;"
@click="postSettingVisible = true"
class="mr-2"
>发布</a-button>
<a-button
type="dashed"
@click="attachmentDrawerVisible = true"
style="margin-left: 8px;"
>附件库</a-button>
</footer-tool-bar>
</div>
@ -194,7 +194,9 @@ export default {
this.$message.success('保存草稿成功!')
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
} else {
postApi
@ -205,7 +207,9 @@ export default {
this.postToStage = response.data.data
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
}
} else {
@ -218,13 +222,12 @@ export default {
this.postToStage = response.data.data
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
}
},
handleShowPostSetting() {
this.postSettingVisible = true
},
handlePreview() {
this.postToStage.status = 'DRAFT'
if (!this.postToStage.title) {
@ -241,7 +244,9 @@ export default {
window.open(response.data, '_blank')
})
.finally(() => {
this.previewSaving = false
setTimeout(() => {
this.previewSaving = false
}, 200)
})
})
} else {
@ -255,7 +260,9 @@ export default {
window.open(response.data, '_blank')
})
.finally(() => {
this.previewSaving = false
setTimeout(() => {
this.previewSaving = false
}, 200)
})
})
}
@ -263,10 +270,6 @@ export default {
onContentChange(val) {
this.postToStage.originalContent = val
},
//
onPostSettingsClose() {
this.postSettingVisible = false
},
onRefreshPostFromSetting(post) {
this.postToStage = post
},

View File

@ -64,12 +64,10 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="handleResetParam()"
>重置</a-button>
<a-button @click="handleResetParam()"></a-button>
</span>
</a-col>
</a-row>
@ -130,14 +128,13 @@
</a>
</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px;">
<a-button class="ml-2">
批量操作
<a-icon type="down" />
</a-button>
</a-dropdown>
</div>
<div style="margin-top:15px">
<div class="mt-4">
<!-- Mobile -->
<a-list
v-if="isMobile()"
@ -242,7 +239,7 @@
v-if="item.status=='PUBLISHED' || item.status == 'INTIMATE'"
:href="item.fullPath"
target="_blank"
style="text-decoration: none;"
class="no-underline"
>
<a-tooltip
placement="top"
@ -252,7 +249,7 @@
<a
v-else-if="item.status=='DRAFT'"
href="javascript:void(0)"
style="text-decoration: none;"
class="no-underline"
@click="handlePreview(item.id)"
>
<a-tooltip
@ -263,7 +260,7 @@
<a
v-else
href="javascript:void(0);"
style="text-decoration: none;"
class="no-underline"
disabled
>
{{ item.title }}
@ -323,7 +320,7 @@
v-if="record.status=='PUBLISHED' || record.status == 'INTIMATE'"
:href="record.fullPath"
target="_blank"
style="text-decoration: none;"
class="no-underline"
>
<a-tooltip
placement="top"
@ -333,7 +330,7 @@
<a
v-else-if="record.status=='DRAFT'"
href="javascript:void(0)"
style="text-decoration: none;"
class="no-underline"
@click="handlePreview(record.id)"
>
<a-tooltip
@ -344,7 +341,7 @@
<a
v-else
href="javascript:void(0);"
style="text-decoration: none;"
class="no-underline"
disabled
>
{{ text }}
@ -625,8 +622,8 @@ export default {
}
},
created() {
this.loadPosts()
this.loadCategories()
this.handleListPosts()
this.handleListCategories()
},
destroyed: function() {
if (this.postSettingVisible) {
@ -662,8 +659,10 @@ export default {
}
},
methods: {
loadPosts() {
this.postsLoading = true
handleListPosts(enableLoading = true) {
if (enableLoading) {
this.postsLoading = true
}
// Set from pagination
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
@ -680,7 +679,7 @@ export default {
}, 200)
})
},
loadCategories() {
handleListCategories() {
this.categoriesLoading = true
categoryApi
.listAll(true)
@ -712,7 +711,7 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadPosts()
this.handleListPosts()
},
handleResetParam() {
this.queryParam.keyword = null
@ -726,38 +725,54 @@ export default {
this.handlePaginationChange(1, this.pagination.size)
},
handleEditStatusClick(postId, status) {
postApi.updateStatus(postId, status).then(response => {
this.$message.success('操作成功!')
this.loadPosts()
})
postApi
.updateStatus(postId, status)
.then(response => {
this.$message.success('操作成功!')
})
.finally(() => {
this.handleListPosts()
})
},
handleDeleteClick(postId) {
postApi.delete(postId).then(response => {
this.$message.success('删除成功!')
this.loadPosts()
})
postApi
.delete(postId)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.handleListPosts()
})
},
handleEditStatusMore(status) {
if (this.selectedRowKeys.length <= 0) {
this.$message.info('请至少选择一项!')
return
}
postApi.updateStatusInBatch(this.selectedRowKeys, status).then(response => {
this.$log.debug(`postId: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = []
this.loadPosts()
})
postApi
.updateStatusInBatch(this.selectedRowKeys, status)
.then(response => {
this.$log.debug(`postId: ${this.selectedRowKeys}, status: ${status}`)
this.selectedRowKeys = []
})
.finally(() => {
this.handleListPosts()
})
},
handleDeleteMore() {
if (this.selectedRowKeys.length <= 0) {
this.$message.info('请至少选择一项!')
return
}
postApi.deleteInBatch(this.selectedRowKeys).then(response => {
this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = []
this.loadPosts()
})
postApi
.deleteInBatch(this.selectedRowKeys)
.then(response => {
this.$log.debug(`delete: ${this.selectedRowKeys}`)
this.selectedRowKeys = []
})
.finally(() => {
this.handleListPosts()
})
},
handleShowPostSettings(post) {
postApi.get(post.id).then(response => {
@ -787,14 +802,14 @@ export default {
this.postSettingVisible = false
this.selectedPost = {}
setTimeout(() => {
this.loadPosts()
this.handleListPosts(false)
}, 500)
},
onPostCommentsClose() {
this.postCommentVisible = false
this.selectedPost = {}
setTimeout(() => {
this.loadPosts()
this.handleListPosts(false)
}, 500)
},
onRefreshPostFromSetting(post) {

View File

@ -7,7 +7,7 @@
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
:title="title"
@ -75,7 +75,7 @@
>
<a-button
type="danger"
style="float:right"
class="float-right"
>删除</a-button>
</a-popconfirm>
</a-form-model-item>
@ -88,7 +88,7 @@
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
title="所有标签"

View File

@ -8,191 +8,185 @@
@close="onClose"
:visible="visible"
>
<a-skeleton
active
:loading="settingLoading"
:paragraph="{ rows: 24 }"
>
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item
label="文章标题:"
v-if="needTitle"
<div class="post-setting-drawer-content">
<div class="mb-4">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item
label="文章标题:"
v-if="needTitle"
>
<a-input v-model="selectedPost.title" />
</a-form-item>
<a-form-item label="文章别名:">
<template slot="help">
<span v-if="options.post_permalink_type === 'DEFAULT'">{{ options.blog_url }}/{{ options.archives_prefix }}/{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'DATE'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_date }}{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'DAY'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_day }}{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p={{ selectedPost.id?selectedPost.id:'${id}' }}</span>
</template>
<a-input v-model="selectedPost.slug" />
</a-form-item>
<a-form-item label="发表时间:">
<a-date-picker
showTime
:defaultValue="pickerDefaultValue"
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择文章发表时间"
@change="onPostDateChange"
@ok="onPostDateOk"
/>
</a-form-item>
<a-form-item label="开启评论:">
<a-radio-group
v-model="selectedPost.disallowComment"
:defaultValue="false"
>
<a-input v-model="selectedPost.title" />
</a-form-item>
<a-form-item label="文章别名:">
<template slot="help">
<span v-if="options.post_permalink_type === 'DEFAULT'">{{ options.blog_url }}/{{ options.archives_prefix }}/{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'DATE'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_date }}{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'DAY'">{{ options.blog_url }}{{ selectedPost.createTime?selectedPost.createTime:new Date() | moment_post_day }}{{ selectedPost.slug?selectedPost.slug:'${slug}' }}{{ (options.path_suffix?options.path_suffix:'') }}</span>
<span v-else-if="options.post_permalink_type === 'ID'">{{ options.blog_url }}/?p={{ selectedPost.id?selectedPost.id:'${id}' }}</span>
</template>
<a-input v-model="selectedPost.slug" />
</a-form-item>
<a-form-item label="发表时间:">
<a-date-picker
showTime
:defaultValue="pickerDefaultValue"
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择文章发表时间"
@change="onPostDateChange"
@ok="onPostDateOk"
/>
</a-form-item>
<a-form-item label="开启评论:">
<a-radio-group
v-model="selectedPost.disallowComment"
:defaultValue="false"
>
<a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否置顶:">
<a-radio-group
v-model="selectedPost.topPriority"
:defaultValue="0"
>
<a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="自定义模板:"
v-if="customTpls.length > 0"
<a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否置顶:">
<a-radio-group
v-model="selectedPost.topPriority"
:defaultValue="0"
>
<a-select v-model="selectedPost.template">
<a-select-option
key=""
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="自定义模板:"
v-if="customTpls.length > 0"
>
<a-select v-model="selectedPost.template">
<a-select-option
key=""
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">分类目录</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<category-tree
v-model="selectedCategoryIds"
:categories="categories"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<category-select-tree
:categories="categories"
v-model="categoryToCreate.parentId"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<a-input
placeholder="分类名称"
v-model="categoryToCreate.name"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<a-input
placeholder="分类路径"
v-model="categoryToCreate.slug"
/>
</a-form-item>
<a-form-item>
<a-button
type="primary"
style="marginRight: 8px"
v-if="categoryFormVisible"
@click="handlerCreateCategory"
>保存</a-button>
<a-button
type="dashed"
style="marginRight: 8px"
v-if="!categoryFormVisible"
@click="toggleCategoryForm"
>新增</a-button>
<a-button
v-if="categoryFormVisible"
@click="toggleCategoryForm"
>取消</a-button>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">标签</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<TagSelect v-model="selectedTagIds" />
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">摘要</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="selectedPost.summary"
placeholder="如不填写,会从文章中自动截取"
/>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">封面图</h3>
<div class="post-setting-drawer-item">
<div class="post-thumb">
<img
class="img"
:src="selectedPost.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible=true"
>
<a-form layout="vertial">
<a-form-item>
<a-input
v-model="selectedPost.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>
<a-button
class="post-thumb-remove"
type="dashed"
@click="handleRemoveThumb"
>移除</a-button>
</div>
</div>
</div>
<a-divider class="divider-transparent" />
</div>
</a-skeleton>
<a-divider />
<div class="mb-4">
<h3 class="post-setting-drawer-title">分类目录</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<category-tree
v-model="selectedCategoryIds"
:categories="categories"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<category-select-tree
:categories="categories"
v-model="categoryToCreate.parentId"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<a-input
placeholder="分类名称"
v-model="categoryToCreate.name"
/>
</a-form-item>
<a-form-item v-if="categoryFormVisible">
<a-input
placeholder="分类路径"
v-model="categoryToCreate.slug"
/>
</a-form-item>
<a-form-item>
<a-button
type="primary"
class="mr-2"
v-if="categoryFormVisible"
@click="handlerCreateCategory"
>保存</a-button>
<a-button
type="dashed"
class="mr-2"
v-if="!categoryFormVisible"
@click="categoryFormVisible = true"
>新增</a-button>
<a-button
v-if="categoryFormVisible"
@click="categoryFormVisible = false"
>取消</a-button>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div class="mb-4">
<h3 class="post-setting-drawer-title">标签</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<TagSelect v-model="selectedTagIds" />
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div class="mb-4">
<h3 class="post-setting-drawer-title">摘要</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="selectedPost.summary"
placeholder="如不填写,会从文章中自动截取"
/>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div class="mb-4">
<h3 class="post-setting-drawer-title">封面图</h3>
<div class="post-setting-drawer-item">
<div class="post-thumb">
<img
class="img"
:src="selectedPost.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible=true"
>
<a-form layout="vertial">
<a-form-item>
<a-input
v-model="selectedPost.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>
<a-button
class="post-thumb-remove"
type="dashed"
@click="selectedPost.thumbnail = null"
>移除</a-button>
</div>
</div>
</div>
<a-divider class="divider-transparent" />
</div>
<AttachmentSelectDrawer
v-model="thumbDrawerVisible"
@listenToSelect="handleSelectPostThumb"
@ -205,11 +199,11 @@
placement="right"
closable
destroyOnClose
@close="onAdvancedClose"
@close="advancedVisible = false"
:visible="advancedVisible"
>
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<div class="mb-4">
<h3 class="post-setting-drawer-title">加密设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
@ -223,7 +217,7 @@
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<div class="mb-4">
<h3 class="post-setting-drawer-title">SEO 设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
@ -245,7 +239,7 @@
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<div class="mb-4">
<h3 class="post-setting-drawer-title">元数据</h3>
<a-form layout="vertical">
<a-form-item
@ -285,12 +279,12 @@
<div class="bottom-control">
<a-button
style="marginRight: 8px"
class="mr-2"
type="dashed"
@click="advancedVisible = true"
>高级</a-button>
<a-button
style="marginRight: 8px"
class="mr-2"
@click="handleDraftClick"
v-if="saveDraftButton"
:loading="draftSaving"
@ -326,7 +320,6 @@ export default {
thumbDrawerVisible: false,
categoryFormVisible: false,
advancedVisible: false,
settingLoading: true,
selectedPost: this.post,
selectedTagIds: this.tagIds,
selectedCategoryIds: this.categoryIds,
@ -394,10 +387,9 @@ export default {
},
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
this.loadCategories()
this.loadPresetMetasField()
this.loadCustomTpls()
this.handleListCategories()
this.handleListPresetMetasField()
this.handleListCustomTpls()
}
}
},
@ -417,18 +409,12 @@ export default {
...mapGetters(['options'])
},
methods: {
loadSkeleton() {
this.settingLoading = true
setTimeout(() => {
this.settingLoading = false
}, 500)
},
loadCategories() {
handleListCategories() {
categoryApi.listAll().then(response => {
this.categories = response.data.data
})
},
loadPresetMetasField() {
handleListPresetMetasField() {
if (this.metas.length <= 0) {
themeApi.getActivatedTheme().then(response => {
const fields = response.data.data.postMetaField
@ -443,7 +429,7 @@ export default {
})
}
},
loadCustomTpls() {
handleListCustomTpls() {
themeApi.customPostTpls().then(response => {
this.customTpls = response.data.data
})
@ -452,9 +438,6 @@ export default {
this.selectedPost.thumbnail = encodeURI(data.path)
this.thumbDrawerVisible = false
},
handleRemoveThumb() {
this.selectedPost.thumbnail = null
},
handlerCreateCategory() {
if (!this.categoryToCreate.name) {
this.$notification['error']({
@ -463,14 +446,15 @@ export default {
})
return
}
categoryApi.create(this.categoryToCreate).then(response => {
this.loadCategories()
this.categoryToCreate = {}
this.toggleCategoryForm()
})
},
toggleCategoryForm() {
this.categoryFormVisible = !this.categoryFormVisible
categoryApi
.create(this.categoryToCreate)
.then(response => {
this.categoryToCreate = {}
this.categoryFormVisible = false
})
.finally(() => {
this.handleListCategories()
})
},
handleDraftClick() {
this.selectedPost.status = 'DRAFT'
@ -519,8 +503,10 @@ export default {
this.$router.push({ name: 'PostList' })
})
.finally(() => {
this.saving = false
this.draftSaving = false
setTimeout(() => {
this.saving = false
this.draftSaving = false
}, 200)
})
} else {
// Create the post
@ -540,17 +526,16 @@ export default {
this.selectedPost = response.data.data
})
.finally(() => {
this.saving = false
this.draftSaving = false
setTimeout(() => {
this.saving = false
this.draftSaving = false
}, 200)
})
}
},
onClose() {
this.$emit('close', false)
},
onAdvancedClose() {
this.advancedVisible = false
},
onPostDateChange(value, dateString) {
this.selectedPost.createTime = value.valueOf()
},

View File

@ -2,7 +2,7 @@
<div>
<a-select
v-model="selectedTagNames"
style="width: 100%"
class="w-full"
allowClear
mode="tags"
placeholder="选择或输入标签"
@ -41,7 +41,7 @@ export default {
}
},
created() {
this.loadTags()
this.handleListTags()
},
watch: {
tags(newValue, oldValue) {
@ -68,7 +68,7 @@ export default {
}
},
methods: {
loadTags(callback) {
handleListTags(callback) {
tagApi.listAll(true).then(response => {
this.tags = response.data.data
if (callback) {
@ -93,7 +93,7 @@ export default {
axios.all(createPromises).then(
axios.spread(() => {
this.loadTags(() => {
this.handleListTags(() => {
this.$log.debug('Tag name map', this.tagNameMap)
// Get all tag id
const tagIds = this.selectedTagNames.map(tagName => this.tagNameMap[tagName].id)

View File

@ -2,7 +2,7 @@
<div>
<a-row :gutter="12">
<a-col :span="24">
<div style="margin-bottom: 16px">
<div class="mb-4">
<a-input
v-model="sheetToStage.title"
size="large"
@ -30,7 +30,7 @@
:sheet="sheetToStage"
:metas="selectedMetas"
:visible="sheetSettingVisible"
@close="onSheetSettingsClose"
@close="sheetSettingVisible = false"
@onRefreshSheet="onRefreshSheetFromSetting"
@onRefreshSheetMetas="onRefreshSheetMetasFromSetting"
@onSaved="onSaved"
@ -40,22 +40,22 @@
<footer-tool-bar :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
<a-button
type="danger"
class="mr-2"
@click="handleSaveDraft(false)"
:loading="draftSaving"
>保存草稿</a-button>
<a-button
@click="handlePreview"
style="margin-left: 8px;"
class="mr-2"
:loading="previewSaving"
>预览</a-button>
<a-button
type="primary"
style="margin-left: 8px;"
@click="handleShowSheetSetting"
class="mr-2"
@click="sheetSettingVisible = true"
>发布</a-button>
<a-button
type="dashed"
style="margin-left: 8px;"
@click="attachmentDrawerVisible = true"
>附件库</a-button>
</footer-tool-bar>
@ -184,7 +184,9 @@ export default {
this.$message.success('保存草稿成功!')
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
} else {
sheetApi
@ -195,7 +197,9 @@ export default {
this.sheetToStage = response.data.data
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
}
} else {
@ -207,13 +211,12 @@ export default {
this.sheetToStage = response.data.data
})
.finally(() => {
this.draftSaving = false
setTimeout(() => {
this.draftSaving = false
}, 200)
})
}
},
handleShowSheetSetting() {
this.sheetSettingVisible = true
},
handlePreview() {
this.sheetToStage.status = 'DRAFT'
if (!this.sheetToStage.title) {
@ -229,7 +232,9 @@ export default {
window.open(response.data, '_blank')
})
.finally(() => {
this.previewSaving = false
setTimeout(() => {
this.previewSaving = false
}, 200)
})
})
} else {
@ -242,7 +247,9 @@ export default {
window.open(response.data, '_blank')
})
.finally(() => {
this.previewSaving = false
setTimeout(() => {
this.previewSaving = false
}, 200)
})
})
}
@ -250,9 +257,6 @@ export default {
onContentChange(val) {
this.sheetToStage.originalContent = val
},
onSheetSettingsClose() {
this.sheetSettingVisible = false
},
onRefreshSheetFromSetting(sheet) {
this.sheetToStage = sheet
},

View File

@ -107,7 +107,7 @@
v-if="item.status=='PUBLISHED'"
:href="item.fullPath"
target="_blank"
style="text-decoration: none;"
class="no-underline"
>
<a-tooltip
placement="top"
@ -117,7 +117,7 @@
<a
v-else-if="item.status=='DRAFT'"
href="javascript:void(0)"
style="text-decoration: none;"
class="no-underline"
@click="handlePreview(item.id)"
>
<a-tooltip
@ -128,7 +128,7 @@
<a
v-else
href="javascript:void(0);"
style="text-decoration: none;"
class="no-underline"
disabled
>
{{ item.title }}
@ -160,7 +160,7 @@
v-if="record.status=='PUBLISHED'"
:href="record.fullPath"
target="_blank"
style="text-decoration: none;"
class="no-underline"
>
<a-tooltip
placement="top"
@ -170,7 +170,7 @@
<a
v-else-if="record.status=='DRAFT'"
href="javascript:void(0)"
style="text-decoration: none;"
class="no-underline"
@click="handlePreview(record.id)"
>
<a-tooltip
@ -181,7 +181,7 @@
<a
v-else
href="javascript:void(0);"
style="text-decoration: none;"
class="no-underline"
disabled
>
{{ text }}
@ -202,7 +202,7 @@
slot="commentCount"
slot-scope="text,record"
@click="handleShowSheetComments(record)"
style="cursor: pointer;"
class="cursor-pointer"
>
<a-badge
:count="record.commentCount"
@ -421,7 +421,7 @@ export default {
}
},
created() {
this.loadSheets()
this.handleListSheets()
},
destroyed: function() {
if (this.sheetSettingVisible) {
@ -435,8 +435,10 @@ export default {
next()
},
methods: {
loadSheets() {
this.loading = true
handleListSheets(enableLoading = true) {
if (enableLoading) {
this.loading = true
}
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
@ -456,16 +458,24 @@ export default {
this.$router.push({ name: 'SheetEdit', query: { sheetId: sheet.id } })
},
handleEditStatusClick(sheetId, status) {
sheetApi.updateStatus(sheetId, status).then(response => {
this.$message.success('操作成功!')
this.loadSheets()
})
sheetApi
.updateStatus(sheetId, status)
.then(response => {
this.$message.success('操作成功!')
})
.finally(() => {
this.handleListSheets()
})
},
handleDeleteClick(sheetId) {
sheetApi.delete(sheetId).then(response => {
this.$message.success('删除成功!')
this.loadSheets()
})
sheetApi
.delete(sheetId)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.handleListSheets()
})
},
handleSheetToMenu(sheet) {
this.menu['name'] = sheet.title
@ -497,20 +507,20 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadSheets()
this.handleListSheets()
},
onSheetSettingsClose() {
this.sheetSettingVisible = false
this.selectedSheet = {}
setTimeout(() => {
this.loadSheets()
this.handleListSheets(false)
}, 500)
},
onSheetCommentsClose() {
this.sheetCommentVisible = false
this.selectedSheet = {}
setTimeout(() => {
this.loadSheets()
this.handleListSheets(false)
}, 500)
},
onRefreshSheetFromSetting(sheet) {

View File

@ -8,115 +8,112 @@
@close="onClose"
:visible="visible"
>
<a-skeleton
active
:loading="settingLoading"
:paragraph="{ rows: 18 }"
>
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item
label="页面标题:"
v-if="needTitle"
<div class="post-setting-drawer-content">
<div class="mb-4">
<h3 class="post-setting-drawer-title">基本设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item
label="页面标题:"
v-if="needTitle"
>
<a-input v-model="selectedSheet.title" />
</a-form-item>
<a-form-item
label="页面别名:"
:help="options.blog_url+'/'+options.sheet_prefix+'/'+ (selectedSheet.slug ? selectedSheet.slug : '{slug}')+(options.path_suffix?options.path_suffix:'')"
>
<a-input v-model="selectedSheet.slug" />
</a-form-item>
<a-form-item label="发表时间:">
<a-date-picker
showTime
:defaultValue="pickerDefaultValue"
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择页面发表时间"
@change="onSheetDateChange"
@ok="onSheetDateOk"
/>
</a-form-item>
<a-form-item label="开启评论:">
<a-radio-group
v-model="selectedSheet.disallowComment"
:defaultValue="false"
>
<a-input v-model="selectedSheet.title" />
</a-form-item>
<a-form-item
label="页面别名:"
:help="options.blog_url+'/'+options.sheet_prefix+'/'+ (selectedSheet.slug ? selectedSheet.slug : '{slug}')+(options.path_suffix?options.path_suffix:'')"
<a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="自定义模板:"
v-if="customTpls.length>0"
>
<a-select
v-model="selectedSheet.template"
:loading="customTplsLoading"
>
<a-input v-model="selectedSheet.slug" />
</a-form-item>
<a-form-item label="发表时间:">
<a-date-picker
showTime
:defaultValue="pickerDefaultValue"
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择页面发表时间"
@change="onSheetDateChange"
@ok="onSheetDateOk"
/>
</a-form-item>
<a-form-item label="开启评论:">
<a-radio-group
v-model="selectedSheet.disallowComment"
:defaultValue="false"
>
<a-radio :value="false">开启</a-radio>
<a-radio :value="true">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
label="自定义模板:"
v-if="customTpls.length>0"
>
<a-select v-model="selectedSheet.template">
<a-select-option
key=""
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<a-select-option
key=""
value=""
></a-select-option>
<a-select-option
v-for="tpl in customTpls"
:key="tpl"
:value="tpl"
>{{ tpl }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<a-divider />
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">摘要</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<div class="mb-4">
<h3 class="post-setting-drawer-title">摘要</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
<a-form-item>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="selectedSheet.summary"
placeholder="不填写则会自动生成"
/>
</a-form-item>
</a-form>
</div>
</div>
<a-divider />
<div class="mb-4">
<h3 class="post-setting-drawer-title">封面图</h3>
<div class="post-setting-drawer-item">
<div class="sheet-thumb">
<img
class="img"
:src="selectedSheet.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible = true"
>
<a-form layout="vertial">
<a-form-item>
<a-input
type="textarea"
:autoSize="{ minRows: 5 }"
v-model="selectedSheet.summary"
placeholder="不填写则会自动生成"
/>
v-model="selectedSheet.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>
<a-button
class="sheet-thumb-remove"
type="dashed"
@click="selectedSheet.thumbnail = null"
>移除</a-button>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<h3 class="post-setting-drawer-title">封面图</h3>
<div class="post-setting-drawer-item">
<div class="sheet-thumb">
<img
class="img"
:src="selectedSheet.thumbnail || '/images/placeholder.jpg'"
@click="thumbDrawerVisible = true"
>
<a-form layout="vertial">
<a-form-item>
<a-input
v-model="selectedSheet.thumbnail"
placeholder="点击封面图选择图片,或者输入外部链接"
></a-input>
</a-form-item>
</a-form>
<a-button
class="sheet-thumb-remove"
type="dashed"
@click="handlerRemoveThumb"
>移除</a-button>
</div>
</div>
</div>
<a-divider class="divider-transparent" />
</div>
</a-skeleton>
<a-divider class="divider-transparent" />
</div>
<AttachmentSelectDrawer
v-model="thumbDrawerVisible"
@listenToSelect="handleSelectSheetThumb"
@ -129,11 +126,11 @@
placement="right"
closable
destroyOnClose
@close="onAdvancedClose"
@close="advancedVisible = false"
:visible="advancedVisible"
>
<div class="post-setting-drawer-content">
<div :style="{ marginBottom: '16px' }">
<div class="mb-4">
<h3 class="post-setting-drawer-title">SEO 设置</h3>
<div class="post-setting-drawer-item">
<a-form layout="vertical">
@ -155,7 +152,7 @@
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '16px' }">
<div class="mb-4">
<h3 class="post-setting-drawer-title">元数据</h3>
<a-form layout="vertical">
<a-form-item
@ -195,13 +192,13 @@
<div class="bottom-control">
<a-button
style="marginRight: 8px"
class="mr-2"
type="dashed"
@click="advancedVisible = true"
>高级</a-button>
<a-button
v-if="saveDraftButton"
style="marginRight: 8px"
class="mr-2"
@click="handleDraftClick"
:loading="draftSaving"
>保存草稿</a-button>
@ -226,7 +223,7 @@ export default {
return {
thumbDrawerVisible: false,
advancedVisible: false,
settingLoading: true,
customTplsLoading: false,
selectedSheet: this.sheet,
customTpls: [],
saving: false,
@ -258,10 +255,6 @@ export default {
default: true
}
},
created() {
this.loadSkeleton()
this.loadCustomTpls()
},
watch: {
sheet(val) {
this.selectedSheet = val
@ -274,8 +267,8 @@ export default {
},
visible: function(newValue, oldValue) {
if (newValue) {
this.loadSkeleton()
this.loadPresetMetasField()
this.handleListCustomTpls()
this.handleListPresetMetasField()
}
}
},
@ -293,13 +286,7 @@ export default {
...mapGetters(['options'])
},
methods: {
loadSkeleton() {
this.settingLoading = true
setTimeout(() => {
this.settingLoading = false
}, 500)
},
loadPresetMetasField() {
handleListPresetMetasField() {
if (this.metas.length <= 0) {
themeApi.getActivatedTheme().then(response => {
const fields = response.data.data.sheetMetaField
@ -314,18 +301,23 @@ export default {
})
}
},
loadCustomTpls() {
themeApi.customSheetTpls().then(response => {
this.customTpls = response.data.data
})
handleListCustomTpls() {
this.customTplsLoading = true
themeApi
.customSheetTpls()
.then(response => {
this.customTpls = response.data.data
})
.finally(() => {
setTimeout(() => {
this.customTplsLoading = false
}, 200)
})
},
handleSelectSheetThumb(data) {
this.selectedSheet.thumbnail = encodeURI(data.path)
this.thumbDrawerVisible = false
},
handlerRemoveThumb() {
this.selectedSheet.thumbnail = null
},
handlePublishClick() {
this.selectedSheet.status = 'PUBLISHED'
this.createOrUpdateSheet()
@ -364,8 +356,10 @@ export default {
this.$router.push({ name: 'SheetList', query: { activeKey: 'custom' } })
})
.finally(() => {
this.saving = false
this.draftSaving = false
setTimeout(() => {
this.saving = false
this.draftSaving = false
}, 200)
})
} else {
sheetApi
@ -384,17 +378,16 @@ export default {
this.selectedSheet = response.data.data
})
.finally(() => {
this.saving = false
this.draftSaving = false
setTimeout(() => {
this.saving = false
this.draftSaving = false
}, 200)
})
}
},
onClose() {
this.$emit('close', false)
},
onAdvancedClose() {
this.advancedVisible = false
},
onSheetDateChange(value, dateString) {
this.selectedSheet.createTime = value.valueOf()
},

View File

@ -45,12 +45,10 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="resetParam()"
>重置</a-button>
<a-button @click="resetParam()"></a-button>
</span>
</a-col>
</a-row>
@ -64,7 +62,7 @@
>写日志</a-button>
</div>
<a-divider />
<div style="margin-top:15px">
<div class="mt-4">
<a-empty v-if="!listLoading && journals.length==0" />
<a-list
v-else
@ -169,7 +167,7 @@
<a-modal
v-model="optionFormVisible"
title="页面设置"
:afterClose="onOptionFormClose"
:afterClose="() => optionFormVisible = false"
>
<template slot="footer">
<a-button
@ -288,15 +286,15 @@ export default {
}
},
created() {
this.loadJournals()
this.loadFormOptions()
this.hanldeListJournals()
this.hanldeListOptions()
},
computed: {
...mapGetters(['user'])
},
methods: {
...mapActions(['loadOptions']),
loadJournals() {
hanldeListJournals() {
this.listLoading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
@ -313,7 +311,7 @@ export default {
}, 200)
})
},
loadFormOptions() {
hanldeListOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
@ -333,10 +331,14 @@ export default {
this.visible = true
},
handleDelete(id) {
journalApi.delete(id).then(response => {
this.$message.success('删除成功!')
this.loadJournals()
})
journalApi
.delete(id)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.hanldeListJournals()
})
},
handleShowJournalComments(journal) {
this.journal = journal
@ -354,17 +356,25 @@ export default {
}
if (this.journal.id) {
journalApi.update(this.journal.id, this.journal).then(response => {
this.$message.success('更新成功!')
this.loadJournals()
this.isPublic = true
})
journalApi
.update(this.journal.id, this.journal)
.then(response => {
this.$message.success('更新成功!')
this.isPublic = true
})
.finally(() => {
this.hanldeListJournals()
})
} else {
journalApi.create(this.journal).then(response => {
this.$message.success('发表成功!')
this.loadJournals()
this.isPublic = true
})
journalApi
.create(this.journal)
.then(response => {
this.$message.success('发表成功!')
this.isPublic = true
})
.finally(() => {
this.hanldeListJournals()
})
}
this.visible = false
},
@ -372,7 +382,7 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadJournals()
this.hanldeListJournals()
},
onJournalCommentsClose() {
this.journal = {}
@ -384,15 +394,16 @@ export default {
this.handlePaginationChange(1, this.pagination.size)
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
this.optionFormVisible = false
})
},
onOptionFormClose() {
this.optionFormVisible = false
optionApi
.save(this.options)
.then(response => {
this.$message.success('保存成功!')
this.optionFormVisible = false
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
})
}
}
}

View File

@ -7,7 +7,7 @@
:md="10"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
:title="title"
@ -105,7 +105,7 @@
:md="14"
:sm="24"
:xs="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
title="所有友情链接"

View File

@ -7,7 +7,7 @@
>
<a-col
:span="24"
style="padding-bottom: 12px;"
class="pb-3"
>
<a-card
:bordered="false"
@ -48,21 +48,16 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="resetParam()"
>重置</a-button>
<a-button @click="resetParam()"></a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div
class="table-operator"
style="margin-bottom: 0;"
>
<div class="table-operator mb-0">
<a-button
type="primary"
icon="plus"
@ -93,7 +88,7 @@
loading="lazy"
>
</div>
<a-card-meta style="padding: 0.8rem;">
<a-card-meta class="p-3">
<ellipsis
:length="isMobile()?12:16"
tooltip
@ -128,7 +123,7 @@
<a-modal
v-model="optionFormVisible"
title="页面设置"
:afterClose="onOptionFormClose"
:afterClose="() => optionFormVisible = false"
>
<template slot="footer">
<a-button
@ -165,137 +160,125 @@
align="middle"
>
<a-col :span="24">
<a-skeleton
active
:loading="drawerLoading"
:paragraph="{rows: 8}"
>
<div class="photo-detail-img">
<img
:src="photo.url || '/images/placeholder.jpg'"
@click="showThumbDrawer"
style="width: 100%;"
>
</div>
</a-skeleton>
<div class="photo-detail-img">
<img
:src="photo.url || '/images/placeholder.jpg'"
@click="showThumbDrawer"
class="w-full"
>
</div>
</a-col>
<a-divider style="margin: 24px 0 12px 0;" />
<a-col :span="24">
<a-skeleton
active
:loading="drawerLoading"
:paragraph="{rows: 8}"
>
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.thumbnail" />
</template>
<template
slot="description"
v-else
>{{ photo.thumbnail }}</template>
<span slot="title">
缩略图地址
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.name" />
</template>
<template
slot="description"
v-else
>{{ photo.name }}</template>
<span slot="title">
图片名称
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-date-picker
v-model="photo.takeTime"
style="width:100%"
/>
</template>
<span
slot="description"
v-else
>{{ photo.takeTime | moment }}</span>
<span slot="title">拍摄日期</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.location" />
</template>
<span
slot="description"
v-else
>{{ photo.location || '无' }}</span>
<span slot="title">拍摄地点</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-auto-complete
:dataSource="teams"
v-model="photo.team"
allowClear
style="width:100%"
/>
</template>
<span
slot="description"
v-else
>{{ photo.team || '无' }}</span>
<span slot="title">分组</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
v-model="photo.description"
type="textarea"
:autoSize="{ minRows: 5 }"
/>
</template>
<span
slot="description"
v-else
>{{ photo.description || '无' }}</span>
<span slot="title">描述</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-skeleton>
<a-list itemLayout="horizontal">
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.thumbnail" />
</template>
<template
slot="description"
v-else
>{{ photo.thumbnail }}</template>
<span slot="title">
缩略图地址
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.name" />
</template>
<template
slot="description"
v-else
>{{ photo.name }}</template>
<span slot="title">
图片名称
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-date-picker
v-model="photo.takeTime"
style="width:100%"
/>
</template>
<span
slot="description"
v-else
>{{ photo.takeTime | moment }}</span>
<span slot="title">拍摄日期</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input v-model="photo.location" />
</template>
<span
slot="description"
v-else
>{{ photo.location || '无' }}</span>
<span slot="title">拍摄地点</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-auto-complete
:dataSource="teams"
v-model="photo.team"
allowClear
style="width:100%"
/>
</template>
<span
slot="description"
v-else
>{{ photo.team || '无' }}</span>
<span slot="title">分组</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta>
<template
slot="description"
v-if="editable"
>
<a-input
v-model="photo.description"
type="textarea"
:autoSize="{ minRows: 5 }"
/>
</template>
<span
slot="description"
v-else
>{{ photo.description || '无' }}</span>
<span slot="title">描述</span>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-col>
</a-row>
<AttachmentSelectDrawer
@ -307,13 +290,13 @@
<div class="bottom-control">
<a-button
type="dashed"
style="marginRight: 8px"
@click="handleEditClick"
class="mr-2"
@click="editable = true"
v-if="!editable"
>编辑</a-button>
<a-button
type="primary"
style="marginRight: 8px"
class="mr-2"
@click="handleCreateOrUpdate"
v-else
>保存</a-button>
@ -341,7 +324,6 @@ export default {
data() {
return {
drawerVisible: false,
drawerLoading: false,
listLoading: true,
thumDrawerVisible: false,
optionFormVisible: false,
@ -366,24 +348,30 @@ export default {
}
},
created() {
this.loadPhotos()
this.loadTeams()
this.loadFormOptions()
this.hanldeListPhotos()
this.hanldeListPhotoTeams()
this.hanldeListOptions()
},
methods: {
...mapActions(['loadOptions']),
loadPhotos() {
hanldeListPhotos() {
this.listLoading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
photoApi.query(this.queryParam).then(response => {
this.photos = response.data.data.content
this.pagination.total = response.data.data.total
this.listLoading = false
})
photoApi
.query(this.queryParam)
.then(response => {
this.photos = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.listLoading = false
}, 200)
})
},
loadFormOptions() {
hanldeListOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
@ -391,25 +379,33 @@ export default {
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
},
loadTeams() {
hanldeListPhotoTeams() {
photoApi.listTeams().then(response => {
this.teams = response.data.data
})
},
handleCreateOrUpdate() {
if (this.photo.id) {
photoApi.update(this.photo.id, this.photo).then(response => {
this.$message.success('照片更新成功!')
this.loadPhotos()
this.loadTeams()
})
photoApi
.update(this.photo.id, this.photo)
.then(response => {
this.$message.success('照片更新成功!')
})
.finally(() => {
this.hanldeListPhotos()
this.hanldeListPhotoTeams()
})
} else {
photoApi.create(this.photo).then(response => {
this.$message.success('照片添加成功!')
this.loadPhotos()
this.loadTeams()
this.photo = response.data.data
})
photoApi
.create(this.photo)
.then(response => {
this.photo = response.data.data
this.$message.success('照片添加成功!')
})
.finally(() => {
this.hanldeListPhotos()
this.hanldeListPhotoTeams()
})
}
this.editable = false
},
@ -421,22 +417,23 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${size}`)
this.pagination.page = page
this.pagination.size = size
this.loadPhotos()
this.hanldeListPhotos()
},
handleAddClick() {
this.editable = true
this.drawerVisible = true
},
handleEditClick() {
this.editable = true
},
handleDeletePhoto() {
photoApi.delete(this.photo.id).then(response => {
this.$message.success('删除成功!')
this.onDrawerClose()
this.loadPhotos()
this.loadTeams()
})
photoApi
.delete(this.photo.id)
.then(response => {
this.$message.success('删除成功!')
this.onDrawerClose()
})
.finally(() => {
this.hanldeListPhotos()
this.hanldeListPhotoTeams()
})
},
showThumbDrawer() {
this.thumDrawerVisible = true
@ -450,7 +447,7 @@ export default {
this.queryParam.keyword = null
this.queryParam.team = null
this.handlePaginationChange(1, this.pagination.size)
this.loadTeams()
this.hanldeListPhotoTeams()
},
onDrawerClose() {
this.drawerVisible = false
@ -458,15 +455,16 @@ export default {
this.editable = false
},
handleSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.$message.success('保存成功!')
this.optionFormVisible = false
})
},
onOptionFormClose() {
this.optionFormVisible = false
optionApi
.save(this.options)
.then(response => {
this.$message.success('保存成功!')
this.optionFormVisible = false
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
})
}
}
}

View File

@ -40,7 +40,7 @@
></a-button>
</a-popover>
<ul style="margin: 0;padding: 0;list-style: none;">
<ul class="m-0 p-0 list-none">
<li>版本{{ environments.version }}</li>
<li>数据库{{ environments.database }}</li>
<li>运行模式{{ environments.mode }}</li>
@ -50,19 +50,19 @@
<a
href="https://github.com/halo-dev"
target="_blank"
style="margin-right: 10px;"
class="mr-3"
>开源组织
<a-icon type="link" /></a>
<a
href="https://halo.run"
target="_blank"
style="margin-right: 10px;"
class="mr-3"
>用户文档
<a-icon type="link" /></a>
<a
href="https://bbs.halo.run"
target="_blank"
style="margin-right: 10px;"
class="mr-3"
>在线社区
<a-icon type="link" /></a>
</a-card>
@ -104,6 +104,7 @@
ok-text="查看更多"
@cancel="versionContentVisible=false"
@ok="handleOpenVersionUrl"
:width="620"
>
<div v-html="versionContent"></div>
</a-modal>

View File

@ -4,7 +4,7 @@
type="flex"
justify="center"
align="middle"
style="height: 100vh;"
class="h-screen"
>
<a-col
:xl="8"
@ -312,10 +312,12 @@ export default {
this.$message.success('安装成功!')
setTimeout(() => {
this.$router.push({ name: 'Login' })
}, 300)
}, 200)
})
.finally(() => {
this.installing = false
setTimeout(() => {
this.installing = false
}, 200)
})
},
handleInstall() {

View File

@ -130,7 +130,7 @@
shape="circle"
:icon="`${advancedOptions?'setting':'thunderbolt'}`"
size="large"
@click="handleAdvancedOptions()"
@click="advancedOptions = !advancedOptions"
></a-button>
</a-tooltip>
</div>
@ -170,14 +170,11 @@ export default {
}
},
created() {
this.loadFormOptions()
this.hanldeListOptions()
},
methods: {
...mapActions(['loadUser', 'loadOptions']),
handleAdvancedOptions() {
this.advancedOptions = !this.advancedOptions
},
loadFormOptions() {
hanldeListOptions() {
optionApi.listAll().then(response => {
this.options = response.data.data
})
@ -186,12 +183,16 @@ export default {
this.options = val
},
onSaveOptions() {
optionApi.save(this.options).then(response => {
this.loadFormOptions()
this.loadOptions()
this.loadUser()
this.$message.success('保存成功!')
})
optionApi
.save(this.options)
.then(response => {
this.$message.success('保存成功!')
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
this.loadUser()
})
}
}
}

View File

@ -9,7 +9,7 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="pb-3"
>
<a-card
:bordered="false"
@ -21,7 +21,7 @@
<p style="min-height: 50px;">点击进入开发者选项页面</p>
<a-button
type="primary"
style="float:right"
class="float-right"
@click="handleToDeveloperOptions()"
>进入</a-button>
</a-card>
@ -55,7 +55,7 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
:bordered="false"
@ -66,7 +66,7 @@
</div>
<p style="min-height: 50px;">支持备份全站数据和数据导出支持下载到本地</p>
<a-dropdown style="float:right">
<a-dropdown class="float-right">
<a-menu slot="overlay">
<a-menu-item
key="1"
@ -81,7 +81,7 @@
数据导出
</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 备份
<a-button class="ml-2"> 备份
<a-icon type="down" />
</a-button>
</a-dropdown>
@ -93,7 +93,7 @@
:md="12"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="pb-3"
>
<a-card
:bordered="false"
@ -105,8 +105,8 @@
<p style="min-height: 50px;">支持 Hexo/Jekyll 文章导入并解析元数据</p>
<a-button
type="primary"
style="float:right"
@click="handleImportMarkdown"
class="float-right"
@click="markdownUpload = true"
>导入</a-button>
</a-card>
</a-col>
@ -152,9 +152,6 @@ export default {
...mapGetters(['options'])
},
methods: {
handleImportMarkdown() {
this.markdownUpload = true
},
handleChange(info) {
const status = info.file.status
if (status !== 'uploading') {

View File

@ -22,6 +22,7 @@
itemLayout="vertical"
size="small"
:dataSource="backups"
:loading="loading"
>
<a-list-item
slot="renderItem"
@ -49,13 +50,6 @@
<p slot="description">{{ backup.updateTime | timeAgo }}/{{ backup.fileSize | fileSizeFormat }}</p>
</a-list-item-meta>
</a-list-item>
<div
v-if="loading"
class="loading-container"
style="position: absolute;bottom: 40px; width: 100%;text-align: center;"
>
<a-spin />
</div>
</a-list>
</a-col>
</a-row>
@ -64,7 +58,7 @@
<a-button
type="primary"
icon="download"
style="marginRight: 8px"
class="mr-2"
:loading="backuping"
@click="handleBackupClick"
>备份</a-button>
@ -72,7 +66,7 @@
type="dashed"
icon="reload"
:loading="loading"
@click="handleBackupRefreshClick"
@click="handleListBackups"
>刷新</a-button>
</div>
</a-drawer>
@ -105,19 +99,23 @@ export default {
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.listBackups()
this.handleListBackups()
}
}
},
methods: {
listBackups() {
handleListBackups() {
this.loading = true
backupApi
.listWorkDirBackups()
.then(response => {
this.backups = response.data.data
})
.finally(() => (this.loading = false))
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleBackupClick() {
this.backuping = true
@ -125,10 +123,12 @@ export default {
.backupWorkDir()
.then(response => {
this.$message.success('备份成功!')
this.listBackups()
})
.finally(() => {
this.backuping = false
setTimeout(() => {
this.backuping = false
}, 200)
this.handleListBackups()
})
},
handleBackupDeleteClick(filename) {
@ -137,12 +137,13 @@ export default {
.deleteWorkDirBackup(filename)
.then(response => {
this.$message.success('删除成功!')
this.listBackups()
})
.finally(() => (this.deleting = false))
},
handleBackupRefreshClick() {
this.listBackups()
.finally(() => {
setTimeout(() => {
this.deleting = false
}, 200)
this.handleListBackups()
})
},
onClose() {
this.$emit('close', false)

View File

@ -22,6 +22,7 @@
itemLayout="vertical"
size="small"
:dataSource="files"
:loading="loading"
>
<a-list-item
slot="renderItem"
@ -49,13 +50,6 @@
<p slot="description">{{ file.updateTime | timeAgo }}/{{ file.fileSize | fileSizeFormat }}</p>
</a-list-item-meta>
</a-list-item>
<div
v-if="loading"
class="loading-container"
style="position: absolute;bottom: 40px; width: 100%;text-align: center;"
>
<a-spin />
</div>
</a-list>
</a-col>
</a-row>
@ -64,7 +58,7 @@
<a-button
type="primary"
icon="download"
style="marginRight: 8px"
class="mr-2"
:loading="backuping"
@click="handleExportClick"
>备份</a-button>
@ -72,7 +66,7 @@
type="dashed"
icon="reload"
:loading="loading"
@click="handleFilesRefreshClick"
@click="handleListBackups"
>刷新</a-button>
</div>
</a-drawer>
@ -105,19 +99,23 @@ export default {
watch: {
visible: function(newValue, oldValue) {
if (newValue) {
this.listFiles()
this.handleListBackups()
}
}
},
methods: {
listFiles() {
handleListBackups() {
this.loading = true
backupApi
.listExportedData()
.then(response => {
this.files = response.data.data
})
.finally(() => (this.loading = false))
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleExportClick() {
this.backuping = true
@ -125,10 +123,12 @@ export default {
.exportData()
.then(response => {
this.$message.success('导出成功!')
this.listFiles()
})
.finally(() => {
this.backuping = false
setTimeout(() => {
this.backuping = false
}, 200)
this.handleListBackups()
})
},
handleFileDeleteClick(filename) {
@ -137,12 +137,13 @@ export default {
.deleteExportedData(filename)
.then(response => {
this.$message.success('删除成功!')
this.listFiles()
})
.finally(() => (this.deleting = false))
},
handleFilesRefreshClick() {
this.listFiles()
.finally(() => {
setTimeout(() => {
this.deleting = false
}, 200)
this.handleListBackups()
})
},
onClose() {
this.$emit('close', false)

View File

@ -7,16 +7,12 @@
/>
<a-form layout="vertical">
<a-form-item>
<a-skeleton
active
:loading="loading"
:paragraph="{rows: 12}"
>
<a-spin :spinning="loading">
<codemirror
v-model="content"
:options="codemirrorOptions"
></codemirror>
</a-skeleton>
</a-spin>
</a-form-item>
<a-form-item>
<a-popconfirm
@ -27,7 +23,7 @@
>
<a-button
type="primary"
style="margin-right: 8px;"
class="mr-2"
>保存</a-button>
</a-popconfirm>
<a-popconfirm
@ -69,16 +65,26 @@ export default {
methods: {
loadConfig() {
this.loading = true
adminApi.getApplicationConfig().then(response => {
this.content = response.data.data
this.loading = false
})
adminApi
.getApplicationConfig()
.then(response => {
this.content = response.data.data
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleUpdateConfig() {
adminApi.updateApplicationConfig(this.content).then(response => {
this.$message.success(`配置保存成功!`)
this.loadConfig()
})
adminApi
.updateApplicationConfig(this.content)
.then(response => {
this.$message.success(`配置保存成功!`)
})
.finally(() => {
this.loadConfig()
})
},
handleRestartApplication() {
adminApi.restartApplication().then(response => {

View File

@ -7,7 +7,7 @@
:md="24"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
title="服务器"
@ -15,7 +15,7 @@
hoverable
:bodyStyle="{ padding: 0 }"
>
<table style="width:100%">
<table class="w-full">
<tbody class="ant-table-tbody">
<tr>
<td>系统</td>
@ -52,7 +52,7 @@
:md="24"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
title="使用情况"
@ -60,10 +60,10 @@
hoverable
:bodyStyle="{ padding: 0 }"
>
<table style="width:100%">
<table class="w-full">
<tbody class="ant-table-tbody">
<tr>
<td>CPU </td>
<td>CPU 核心</td>
<td>{{ system.cpu.count }} </td>
</tr>
<tr>
@ -97,7 +97,7 @@
:md="24"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
title="环境"
@ -105,7 +105,7 @@
hoverable
:bodyStyle="{ padding: 0 }"
>
<table style="width:100%">
<table class="w-full">
<tbody class="ant-table-tbody">
<tr>
<td>Java 名称</td>
@ -137,7 +137,7 @@
:md="24"
:sm="24"
:xs="24"
:style="{ marginBottom: '12px' }"
class="mb-3"
>
<a-card
title="应用"
@ -145,7 +145,7 @@
hoverable
:bodyStyle="{ padding: 0 }"
>
<table style="width:100%">
<table class="w-full">
<tbody class="ant-table-tbody">
<tr>
<td>端口</td>

View File

@ -45,12 +45,10 @@
<span class="table-page-search-submitButtons">
<a-button
type="primary"
class="mr-2"
@click="handleQuery()"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="handleResetParam()"
>重置</a-button>
<a-button @click="handleResetParam()"></a-button>
</span>
</a-col>
</a-row>
@ -63,7 +61,7 @@
@click="formVisible=true"
>新增</a-button>
</div>
<div style="margin-top:15px">
<div class="mt-4">
<a-table
:rowKey="option => option.id"
:columns="columns"
@ -244,30 +242,40 @@ export default {
}
},
created() {
this.loadOptionsList()
this.hanldeListOptions()
},
methods: {
...mapActions(['loadOptions']),
loadOptionsList() {
hanldeListOptions() {
this.loading = true
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
this.queryParam.sort = this.pagination.sort
optionApi.query(this.queryParam).then(response => {
this.options = response.data.data.content
this.pagination.total = response.data.data.total
this.loading = false
})
optionApi
.query(this.queryParam)
.then(response => {
this.options = response.data.data.content
this.pagination.total = response.data.data.total
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleQuery() {
this.handlePaginationChange(1, this.pagination.size)
},
handleDeleteOption(id) {
optionApi.delete(id).then(response => {
this.$message.success('删除成功!')
this.loadOptionsList()
this.loadOptions()
})
optionApi
.delete(id)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
})
},
handleEditOption(option) {
this.optionToStage = option
@ -277,7 +285,7 @@ export default {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page
this.pagination.size = pageSize
this.loadOptionsList()
this.hanldeListOptions()
},
handleResetParam() {
this.queryParam.keyword = null
@ -304,22 +312,30 @@ export default {
return
}
if (this.optionToStage.id) {
optionApi.update(this.optionToStage.id, this.optionToStage).then(response => {
this.$message.success('更新成功!')
this.loadOptionsList()
this.loadOptions()
this.optionToStage = {}
this.formVisible = false
})
optionApi
.update(this.optionToStage.id, this.optionToStage)
.then(response => {
this.$message.success('更新成功!')
this.optionToStage = {}
this.formVisible = false
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
})
} else {
this.optionToStage.type = this.optionType.CUSTOM.value
optionApi.create(this.optionToStage).then(response => {
this.$message.success('保存成功!')
this.loadOptionsList()
this.loadOptions()
this.optionToStage = {}
this.formVisible = false
})
optionApi
.create(this.optionToStage)
.then(response => {
this.$message.success('保存成功!')
this.optionToStage = {}
this.formVisible = false
})
.finally(() => {
this.hanldeListOptions()
this.loadOptions()
})
}
}
}

View File

@ -1,22 +1,19 @@
<template>
<a-form layout="vertical">
<a-form-item>
<a-skeleton
active
:loading="loading"
:paragraph="{rows: 12}"
>
<a-spin :spinning="loading">
<codemirror
v-model="logContent"
:options="codemirrorOptions"
></codemirror>
</a-skeleton>
</a-spin>
</a-form-item>
<a-form-item>
<a-select
defaultValue="200"
v-model="logLines"
@change="handleLoadLogsLines"
style="margin-right: 8px;width: 100px"
@change="handleLinesChange"
>
<a-select-option value="200">200 </a-select-option>
<a-select-option value="500">500 </a-select-option>
@ -25,8 +22,8 @@
</a-select>
<a-button
type="primary"
style="margin-right: 8px;"
@click="loadLogs()"
class="mr-2"
@click="handleLoadLogsLines()"
>刷新</a-button>
<a-button
type="dashed"
@ -54,24 +51,30 @@ export default {
line: true
},
logContent: '',
loading: true,
loading: false,
logLines: 200
}
},
created() {
this.loadLogs()
this.handleLoadLogsLines()
},
updated() {
//
this.$el.querySelector('.CodeMirror-scroll').scrollTop = this.$el.querySelector('.CodeMirror-scroll').scrollHeight
},
methods: {
loadLogs() {
handleLoadLogsLines() {
this.loading = true
adminApi.getLogFiles(this.logLines).then(response => {
this.logContent = response.data.data
this.loading = false
})
adminApi
.getLogFiles(this.logLines)
.then(response => {
this.logContent = response.data.data
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleDownloadLogFile() {
const hide = this.$message.loading('下载中...', 0)
@ -95,9 +98,6 @@ export default {
.finally(() => {
hide()
})
},
handleLinesChange(value) {
this.logLines = value
}
}
}

View File

@ -18,14 +18,13 @@
</a-button>
<a-button
icon="sync"
@click="loadStaticList"
@click="handleListStatics"
:loading="loading"
:disabled="loading"
>
刷新
</a-button>
</div>
<div style="margin-top:15px">
<div class="mt-4">
<a-table
:rowKey="record => record.id"
:columns="columns"
@ -91,9 +90,7 @@
<a href="javascript:;">删除</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item
key="3"
>
<a-menu-item key="3">
<a
href="javascript:;"
@click="handleShowRenameModal(record)"
@ -268,7 +265,7 @@ export default {
}
},
created() {
this.loadStaticList()
this.handleListStatics()
this.CodeMirror = require('codemirror')
this.CodeMirror.modeURL = 'codemirror/mode/%N/%N.js'
},
@ -277,23 +274,33 @@ export default {
sortedStatics() {
const data = this.statics.slice(0)
return data.sort(function(a, b) {
return b.createTime - a.createTime
return a.isFile - b.isFile
})
}
},
methods: {
loadStaticList() {
handleListStatics() {
this.loading = true
staticApi.list().then(response => {
this.statics = response.data.data
this.loading = false
})
staticApi
.list()
.then(response => {
this.statics = response.data.data
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleDelete(path) {
staticApi.delete(path).then(response => {
this.$message.success(`删除成功!`)
this.loadStaticList()
})
staticApi
.delete(path)
.then(response => {
this.$message.success(`删除成功!`)
})
.finally(() => {
this.handleListStatics()
})
},
handleUpload(file) {
this.selectedFile = file
@ -303,10 +310,9 @@ export default {
this.selectedFile = file
this.createFolderModal = true
const that = this
Vue.nextTick()
.then(() => {
that.$refs.createFoldeInput.focus()
})
Vue.nextTick().then(() => {
that.$refs.createFoldeInput.focus()
})
},
handleShowRenameModal(file) {
this.selectedFile = file
@ -314,17 +320,16 @@ export default {
this.renameFile = file.isFile
this.renameModal = true
const that = this
Vue.nextTick()
.then(() => {
const inputRef = that.$refs.renameModalInput
const tmp = inputRef.value.split('.')
inputRef.focus()
if (tmp.length <= 1) {
inputRef.$el.setSelectionRange(0, inputRef.value.length)
} else {
inputRef.$el.setSelectionRange(0, inputRef.value.length - tmp.pop().length - 1)
}
})
Vue.nextTick().then(() => {
const inputRef = that.$refs.renameModalInput
const tmp = inputRef.value.split('.')
inputRef.focus()
if (tmp.length <= 1) {
inputRef.$el.setSelectionRange(0, inputRef.value.length)
} else {
inputRef.$el.setSelectionRange(0, inputRef.value.length - tmp.pop().length - 1)
}
})
},
handleShowEditModal(file) {
this.selectedFile = file
@ -337,28 +342,35 @@ export default {
this.$message.error(`不支持编辑 "${postfix}" 类型的文件`)
} else {
this.editModal = true
Vue.nextTick()
.then(() => {
const editor = this.$refs.editor.editor
editor.setOption('mode', info.mime)
this.CodeMirror.autoLoadMode(editor, info.mode)
})
Vue.nextTick().then(() => {
const editor = this.$refs.editor.editor
editor.setOption('mode', info.mime)
this.CodeMirror.autoLoadMode(editor, info.mode)
})
}
})
},
handleCreateFolder() {
staticApi.createFolder(this.selectedFile.relativePath, this.createFolderName).then(response => {
this.$message.success(`创建文件夹成功!`)
this.createFolderModal = false
this.loadStaticList()
})
staticApi
.createFolder(this.selectedFile.relativePath, this.createFolderName)
.then(response => {
this.$message.success(`创建文件夹成功!`)
this.createFolderModal = false
})
.finally(() => {
this.handleListStatics()
})
},
handleRename() {
staticApi.rename(this.selectedFile.relativePath, this.renameName).then(response => {
this.$message.success(`重命名成功!`)
this.renameModal = false
this.loadStaticList()
})
staticApi
.rename(this.selectedFile.relativePath, this.renameName)
.then(response => {
this.$message.success(`重命名成功!`)
this.renameModal = false
})
.finally(() => {
this.handleListStatics()
})
},
handleEditSave() {
staticApi.save(this.selectedFile.relativePath, this.$refs.editor.editor.getValue()).then(response => {
@ -377,7 +389,7 @@ export default {
onUploadClose() {
this.$refs.upload.handleClearFileList()
this.selectedFile = {}
this.loadStaticList()
this.handleListStatics()
},
handleEditClose() {
this.editModal = false

View File

@ -27,7 +27,7 @@
刷新
</a-button>
</div>
<div style="margin-top:15px">
<div class="mt-4">
<a-table
:rowKey="record => record.id"
:columns="columns"

View File

@ -277,7 +277,7 @@ export default {
.finally(() => {
setTimeout(() => {
this.landing = false
}, 500)
}, 200)
})
},
loginSuccess() {

View File

@ -4,7 +4,7 @@
<a-col
:lg="10"
:md="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
:bordered="false"
@ -63,7 +63,7 @@
<a-col
:lg="14"
:md="24"
:style="{ 'padding-bottom': '12px' }"
class="pb-3"
>
<a-card
:bodyStyle="{ padding: '0' }"
@ -258,7 +258,7 @@
:help="`MFAKey:${mfaParam.mfaKey}`"
>
<template slot="extra">
<span style="color:red">* 建议保存此二维码或 MFAKey验证设备丢失将无法找回只能通过重置密码关闭二步验证</span>
<span class="text-red-600">* 建议保存此二维码或 MFAKey验证设备丢失将无法找回只能通过重置密码关闭二步验证</span>
</template>
<img
width="100%"
@ -332,7 +332,7 @@ export default {
}
},
created() {
this.getStatistics()
this.handleLoadStatistics()
},
watch: {
mfaType(value) {
@ -346,7 +346,7 @@ export default {
},
methods: {
...mapMutations({ setUser: 'SET_USER' }),
getStatistics() {
handleLoadStatistics() {
this.statisticsLoading = true
statisticsApi
.statisticsWithUser()
@ -356,8 +356,10 @@ export default {
this.mfaParam.mfaType = this.user.mfaType && this.user.mfaType
})
.finally(() => {
this.statisticsLoading = false
}, 200)
setTimeout(() => {
this.statisticsLoading = false
}, 200)
})
},
handleUpdatePassword() {
// Check confirm password

727
tailwind.config.js Normal file
View File

@ -0,0 +1,727 @@
module.exports = {
purge: [],
target: 'relaxed',
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px'
},
colors: {
transparent: 'transparent',
current: 'currentColor',
black: '#000',
white: '#fff',
gray: {
100: '#f7fafc',
200: '#edf2f7',
300: '#e2e8f0',
400: '#cbd5e0',
500: '#a0aec0',
600: '#718096',
700: '#4a5568',
800: '#2d3748',
900: '#1a202c'
},
red: {
100: '#fff5f5',
200: '#fed7d7',
300: '#feb2b2',
400: '#fc8181',
500: '#f56565',
600: '#e53e3e',
700: '#c53030',
800: '#9b2c2c',
900: '#742a2a'
},
orange: {
100: '#fffaf0',
200: '#feebc8',
300: '#fbd38d',
400: '#f6ad55',
500: '#ed8936',
600: '#dd6b20',
700: '#c05621',
800: '#9c4221',
900: '#7b341e'
},
yellow: {
100: '#fffff0',
200: '#fefcbf',
300: '#faf089',
400: '#f6e05e',
500: '#ecc94b',
600: '#d69e2e',
700: '#b7791f',
800: '#975a16',
900: '#744210'
},
green: {
100: '#f0fff4',
200: '#c6f6d5',
300: '#9ae6b4',
400: '#68d391',
500: '#48bb78',
600: '#38a169',
700: '#2f855a',
800: '#276749',
900: '#22543d'
},
teal: {
100: '#e6fffa',
200: '#b2f5ea',
300: '#81e6d9',
400: '#4fd1c5',
500: '#38b2ac',
600: '#319795',
700: '#2c7a7b',
800: '#285e61',
900: '#234e52'
},
blue: {
100: '#ebf8ff',
200: '#bee3f8',
300: '#90cdf4',
400: '#63b3ed',
500: '#4299e1',
600: '#3182ce',
700: '#2b6cb0',
800: '#2c5282',
900: '#2a4365'
},
indigo: {
100: '#ebf4ff',
200: '#c3dafe',
300: '#a3bffa',
400: '#7f9cf5',
500: '#667eea',
600: '#5a67d8',
700: '#4c51bf',
800: '#434190',
900: '#3c366b'
},
purple: {
100: '#faf5ff',
200: '#e9d8fd',
300: '#d6bcfa',
400: '#b794f4',
500: '#9f7aea',
600: '#805ad5',
700: '#6b46c1',
800: '#553c9a',
900: '#44337a'
},
pink: {
100: '#fff5f7',
200: '#fed7e2',
300: '#fbb6ce',
400: '#f687b3',
500: '#ed64a6',
600: '#d53f8c',
700: '#b83280',
800: '#97266d',
900: '#702459'
}
},
spacing: {
px: '1px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
'40': '10rem',
'48': '12rem',
'56': '14rem',
'64': '16rem'
},
backgroundColor: theme => theme('colors'),
backgroundOpacity: theme => theme('opacity'),
backgroundPosition: {
bottom: 'bottom',
center: 'center',
left: 'left',
'left-bottom': 'left bottom',
'left-top': 'left top',
right: 'right',
'right-bottom': 'right bottom',
'right-top': 'right top',
top: 'top'
},
backgroundSize: {
auto: 'auto',
cover: 'cover',
contain: 'contain'
},
borderColor: theme => ({
...theme('colors'),
default: theme('colors.gray.300', 'currentColor')
}),
borderOpacity: theme => theme('opacity'),
borderRadius: {
none: '0',
sm: '0.125rem',
default: '0.25rem',
md: '0.375rem',
lg: '0.5rem',
full: '9999px'
},
borderWidth: {
default: '1px',
'0': '0',
'2': '2px',
'4': '4px',
'8': '8px'
},
boxShadow: {
xs: '0 0 0 1px rgba(0, 0, 0, 0.05)',
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
none: 'none'
},
container: {},
cursor: {
auto: 'auto',
default: 'default',
pointer: 'pointer',
wait: 'wait',
text: 'text',
move: 'move',
'not-allowed': 'not-allowed'
},
divideColor: theme => theme('borderColor'),
divideOpacity: theme => theme('borderOpacity'),
divideWidth: theme => theme('borderWidth'),
fill: {
current: 'currentColor'
},
flex: {
'1': '1 1 0%',
auto: '1 1 auto',
initial: '0 1 auto',
none: 'none'
},
flexGrow: {
'0': '0',
default: '1'
},
flexShrink: {
'0': '0',
default: '1'
},
fontFamily: {
sans: [
'system-ui',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'"Noto Sans"',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
'"Noto Color Emoji"'
],
serif: ['Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'],
mono: ['Menlo', 'Monaco', 'Consolas', '"Liberation Mono"', '"Courier New"', 'monospace']
},
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
'5xl': '3rem',
'6xl': '4rem'
},
fontWeight: {
hairline: '100',
thin: '200',
light: '300',
normal: '400',
medium: '500',
semibold: '600',
bold: '700',
extrabold: '800',
black: '900'
},
height: theme => ({
auto: 'auto',
...theme('spacing'),
full: '100%',
screen: '100vh'
}),
inset: {
'0': '0',
auto: 'auto'
},
letterSpacing: {
tighter: '-0.05em',
tight: '-0.025em',
normal: '0',
wide: '0.025em',
wider: '0.05em',
widest: '0.1em'
},
lineHeight: {
none: '1',
tight: '1.25',
snug: '1.375',
normal: '1.5',
relaxed: '1.625',
loose: '2',
'3': '.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'7': '1.75rem',
'8': '2rem',
'9': '2.25rem',
'10': '2.5rem'
},
listStyleType: {
none: 'none',
disc: 'disc',
decimal: 'decimal'
},
margin: (theme, { negative }) => ({
auto: 'auto',
...theme('spacing'),
...negative(theme('spacing'))
}),
maxHeight: {
full: '100%',
screen: '100vh'
},
maxWidth: (theme, { breakpoints }) => ({
none: 'none',
xs: '20rem',
sm: '24rem',
md: '28rem',
lg: '32rem',
xl: '36rem',
'2xl': '42rem',
'3xl': '48rem',
'4xl': '56rem',
'5xl': '64rem',
'6xl': '72rem',
full: '100%',
...breakpoints(theme('screens'))
}),
minHeight: {
'0': '0',
full: '100%',
screen: '100vh'
},
minWidth: {
'0': '0',
full: '100%'
},
objectPosition: {
bottom: 'bottom',
center: 'center',
left: 'left',
'left-bottom': 'left bottom',
'left-top': 'left top',
right: 'right',
'right-bottom': 'right bottom',
'right-top': 'right top',
top: 'top'
},
opacity: {
'0': '0',
'25': '0.25',
'50': '0.5',
'75': '0.75',
'100': '1'
},
order: {
first: '-9999',
last: '9999',
none: '0',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9',
'10': '10',
'11': '11',
'12': '12'
},
padding: theme => theme('spacing'),
placeholderColor: theme => theme('colors'),
placeholderOpacity: theme => theme('opacity'),
space: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing'))
}),
stroke: {
current: 'currentColor'
},
strokeWidth: {
'0': '0',
'1': '1',
'2': '2'
},
textColor: theme => theme('colors'),
textOpacity: theme => theme('opacity'),
width: theme => ({
auto: 'auto',
...theme('spacing'),
'1/2': '50%',
'1/3': '33.333333%',
'2/3': '66.666667%',
'1/4': '25%',
'2/4': '50%',
'3/4': '75%',
'1/5': '20%',
'2/5': '40%',
'3/5': '60%',
'4/5': '80%',
'1/6': '16.666667%',
'2/6': '33.333333%',
'3/6': '50%',
'4/6': '66.666667%',
'5/6': '83.333333%',
'1/12': '8.333333%',
'2/12': '16.666667%',
'3/12': '25%',
'4/12': '33.333333%',
'5/12': '41.666667%',
'6/12': '50%',
'7/12': '58.333333%',
'8/12': '66.666667%',
'9/12': '75%',
'10/12': '83.333333%',
'11/12': '91.666667%',
full: '100%',
screen: '100vw'
}),
zIndex: {
auto: 'auto',
'0': '0',
'10': '10',
'20': '20',
'30': '30',
'40': '40',
'50': '50'
},
gap: theme => theme('spacing'),
gridTemplateColumns: {
none: 'none',
'1': 'repeat(1, minmax(0, 1fr))',
'2': 'repeat(2, minmax(0, 1fr))',
'3': 'repeat(3, minmax(0, 1fr))',
'4': 'repeat(4, minmax(0, 1fr))',
'5': 'repeat(5, minmax(0, 1fr))',
'6': 'repeat(6, minmax(0, 1fr))',
'7': 'repeat(7, minmax(0, 1fr))',
'8': 'repeat(8, minmax(0, 1fr))',
'9': 'repeat(9, minmax(0, 1fr))',
'10': 'repeat(10, minmax(0, 1fr))',
'11': 'repeat(11, minmax(0, 1fr))',
'12': 'repeat(12, minmax(0, 1fr))'
},
gridColumn: {
auto: 'auto',
'span-1': 'span 1 / span 1',
'span-2': 'span 2 / span 2',
'span-3': 'span 3 / span 3',
'span-4': 'span 4 / span 4',
'span-5': 'span 5 / span 5',
'span-6': 'span 6 / span 6',
'span-7': 'span 7 / span 7',
'span-8': 'span 8 / span 8',
'span-9': 'span 9 / span 9',
'span-10': 'span 10 / span 10',
'span-11': 'span 11 / span 11',
'span-12': 'span 12 / span 12'
},
gridColumnStart: {
auto: 'auto',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9',
'10': '10',
'11': '11',
'12': '12',
'13': '13'
},
gridColumnEnd: {
auto: 'auto',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9',
'10': '10',
'11': '11',
'12': '12',
'13': '13'
},
gridTemplateRows: {
none: 'none',
'1': 'repeat(1, minmax(0, 1fr))',
'2': 'repeat(2, minmax(0, 1fr))',
'3': 'repeat(3, minmax(0, 1fr))',
'4': 'repeat(4, minmax(0, 1fr))',
'5': 'repeat(5, minmax(0, 1fr))',
'6': 'repeat(6, minmax(0, 1fr))'
},
gridRow: {
auto: 'auto',
'span-1': 'span 1 / span 1',
'span-2': 'span 2 / span 2',
'span-3': 'span 3 / span 3',
'span-4': 'span 4 / span 4',
'span-5': 'span 5 / span 5',
'span-6': 'span 6 / span 6'
},
gridRowStart: {
auto: 'auto',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7'
},
gridRowEnd: {
auto: 'auto',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7'
},
transformOrigin: {
center: 'center',
top: 'top',
'top-right': 'top right',
right: 'right',
'bottom-right': 'bottom right',
bottom: 'bottom',
'bottom-left': 'bottom left',
left: 'left',
'top-left': 'top left'
},
scale: {
'0': '0',
'50': '.5',
'75': '.75',
'90': '.9',
'95': '.95',
'100': '1',
'105': '1.05',
'110': '1.1',
'125': '1.25',
'150': '1.5'
},
rotate: {
'-180': '-180deg',
'-90': '-90deg',
'-45': '-45deg',
'0': '0',
'45': '45deg',
'90': '90deg',
'180': '180deg'
},
translate: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
'-full': '-100%',
'-1/2': '-50%',
'1/2': '50%',
full: '100%'
}),
skew: {
'-12': '-12deg',
'-6': '-6deg',
'-3': '-3deg',
'0': '0',
'3': '3deg',
'6': '6deg',
'12': '12deg'
},
transitionProperty: {
none: 'none',
all: 'all',
default: 'background-color, border-color, color, fill, stroke, opacity, box-shadow, transform',
colors: 'background-color, border-color, color, fill, stroke',
opacity: 'opacity',
shadow: 'box-shadow',
transform: 'transform'
},
transitionTimingFunction: {
linear: 'linear',
in: 'cubic-bezier(0.4, 0, 1, 1)',
out: 'cubic-bezier(0, 0, 0.2, 1)',
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)'
},
transitionDuration: {
'75': '75ms',
'100': '100ms',
'150': '150ms',
'200': '200ms',
'300': '300ms',
'500': '500ms',
'700': '700ms',
'1000': '1000ms'
},
transitionDelay: {
'75': '75ms',
'100': '100ms',
'150': '150ms',
'200': '200ms',
'300': '300ms',
'500': '500ms',
'700': '700ms',
'1000': '1000ms'
}
},
variants: {
accessibility: ['responsive', 'focus'],
alignContent: ['responsive'],
alignItems: ['responsive'],
alignSelf: ['responsive'],
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColor: ['responsive', 'hover', 'focus'],
backgroundOpacity: ['responsive', 'hover', 'focus'],
backgroundPosition: ['responsive'],
backgroundRepeat: ['responsive'],
backgroundSize: ['responsive'],
borderCollapse: ['responsive'],
borderColor: ['responsive', 'hover', 'focus'],
borderOpacity: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
borderStyle: ['responsive'],
borderWidth: ['responsive'],
boxShadow: ['responsive', 'hover', 'focus'],
boxSizing: ['responsive'],
cursor: ['responsive'],
display: ['responsive'],
divideColor: ['responsive'],
divideOpacity: ['responsive'],
divideWidth: ['responsive'],
fill: ['responsive'],
flex: ['responsive'],
flexDirection: ['responsive'],
flexGrow: ['responsive'],
flexShrink: ['responsive'],
flexWrap: ['responsive'],
float: ['responsive'],
clear: ['responsive'],
fontFamily: ['responsive'],
fontSize: ['responsive'],
fontSmoothing: ['responsive'],
fontStyle: ['responsive'],
fontWeight: ['responsive', 'hover', 'focus'],
height: ['responsive'],
inset: ['responsive'],
justifyContent: ['responsive'],
letterSpacing: ['responsive'],
lineHeight: ['responsive'],
listStylePosition: ['responsive'],
listStyleType: ['responsive'],
margin: ['responsive'],
maxHeight: ['responsive'],
maxWidth: ['responsive'],
minHeight: ['responsive'],
minWidth: ['responsive'],
objectFit: ['responsive'],
objectPosition: ['responsive'],
opacity: ['responsive', 'hover', 'focus'],
order: ['responsive'],
outline: ['responsive', 'focus'],
overflow: ['responsive'],
padding: ['responsive'],
placeholderColor: ['responsive', 'focus'],
placeholderOpacity: ['responsive', 'focus'],
pointerEvents: ['responsive'],
position: ['responsive'],
resize: ['responsive'],
space: ['responsive'],
stroke: ['responsive'],
strokeWidth: ['responsive'],
tableLayout: ['responsive'],
textAlign: ['responsive'],
textColor: ['responsive', 'hover', 'focus'],
textOpacity: ['responsive', 'hover', 'focus'],
textDecoration: ['responsive', 'hover', 'focus'],
textTransform: ['responsive'],
userSelect: ['responsive'],
verticalAlign: ['responsive'],
visibility: ['responsive'],
whitespace: ['responsive'],
width: ['responsive'],
wordBreak: ['responsive'],
zIndex: ['responsive'],
gap: ['responsive'],
gridAutoFlow: ['responsive'],
gridTemplateColumns: ['responsive'],
gridColumn: ['responsive'],
gridColumnStart: ['responsive'],
gridColumnEnd: ['responsive'],
gridTemplateRows: ['responsive'],
gridRow: ['responsive'],
gridRowStart: ['responsive'],
gridRowEnd: ['responsive'],
transform: ['responsive'],
transformOrigin: ['responsive'],
scale: ['responsive', 'hover', 'focus'],
rotate: ['responsive', 'hover', 'focus'],
translate: ['responsive', 'hover', 'focus'],
skew: ['responsive', 'hover', 'focus'],
transitionProperty: ['responsive'],
transitionTimingFunction: ['responsive'],
transitionDuration: ['responsive'],
transitionDelay: ['responsive']
},
corePlugins: {},
plugins: []
}