Ryan Wang 2020-11-02 22:35:48 +08:00 committed by GitHub
parent 1083a90aa9
commit 1de3f79944
14 changed files with 1348 additions and 599 deletions

161
package-lock.json generated
View File

@ -6,21 +6,21 @@
"dependencies": { "dependencies": {
"@ant-design/colors": { "@ant-design/colors": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz", "resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-3.2.2.tgz?cache=0&sync_timestamp=1596611369344&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcolors%2Fdownload%2F%40ant-design%2Fcolors-3.2.2.tgz",
"integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==", "integrity": "sha1-WtQ9YZ6RHzSI66wwPWBuZqhCOQM=",
"requires": { "requires": {
"tinycolor2": "^1.4.1" "tinycolor2": "^1.4.1"
} }
}, },
"@ant-design/icons": { "@ant-design/icons": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz", "resolved": "https://registry.npm.taobao.org/@ant-design/icons/download/@ant-design/icons-2.1.1.tgz?cache=0&sync_timestamp=1596529338545&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Ficons%2Fdownload%2F%40ant-design%2Ficons-2.1.1.tgz",
"integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w==" "integrity": "sha1-e5wI3/1PXUHbZn2dvl4BB9C9mko="
}, },
"@ant-design/icons-vue": { "@ant-design/icons-vue": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/@ant-design/icons-vue/download/@ant-design/icons-vue-2.0.0.tgz?cache=0&sync_timestamp=1598869253063&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Ficons-vue%2Fdownload%2F%40ant-design%2Ficons-vue-2.0.0.tgz",
"integrity": "sha512-2c0QQE5hL4N48k5NkPG5sdpMl9YnvyNhf0U7YkdZYDlLnspoRU7vIA0UK9eHBs6OpFLcJB6o8eJrIl2ajBskPg==", "integrity": "sha1-A1f1AQpATp80qHpLQbKgjfaR284=",
"requires": { "requires": {
"@ant-design/colors": "^3.1.0", "@ant-design/colors": "^3.1.0",
"babel-runtime": "^6.26.0" "babel-runtime": "^6.26.0"
@ -1342,18 +1342,18 @@
"dev": true "dev": true
}, },
"@simonwep/pickr": { "@simonwep/pickr": {
"version": "1.7.2", "version": "1.7.4",
"resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.7.2.tgz", "resolved": "https://registry.npm.taobao.org/@simonwep/pickr/download/@simonwep/pickr-1.7.4.tgz",
"integrity": "sha512-XHmQKS1k1gUvB4kOj9W3DTpT5SQWDovSm3KuOtmlkHmezkF/WzyraPIk+vwroxESdugfqanJZFvgdj9lzHcEKw==", "integrity": "sha1-sU/NlFiQOIuHDNbbTWx41THyUUE=",
"requires": { "requires": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"nanopop": "^1.3.0" "nanopop": "^2.1.0"
}, },
"dependencies": { "dependencies": {
"core-js": { "core-js": {
"version": "3.6.5", "version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-3.6.5.tgz?cache=0&sync_timestamp=1589682726446&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" "integrity": "sha1-c5XcJzrzf7LlDpvT2f6EEoUjHRo="
} }
} }
}, },
@ -2464,8 +2464,8 @@
}, },
"loader-utils": { "loader-utils": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -2604,9 +2604,9 @@
} }
}, },
"vue-loader-v16": { "vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8", "version": "npm:vue-loader@16.0.0-beta.9",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz", "resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.0.0-beta.9.tgz?cache=0&sync_timestamp=1603783106162&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.0.0-beta.9.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==", "integrity": "sha1-UlEsthwpaCfJnA1UOYvvhL5ESPw=",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -2617,8 +2617,8 @@
"dependencies": { "dependencies": {
"chalk": { "chalk": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -3016,8 +3016,8 @@
}, },
"add-dom-event-listener": { "add-dom-event-listener": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", "resolved": "https://registry.npm.taobao.org/add-dom-event-listener/download/add-dom-event-listener-1.1.0.tgz",
"integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", "integrity": "sha1-apLbOg3Qq8JU4JXA8dwUrLuq4xA=",
"requires": { "requires": {
"object-assign": "4.x" "object-assign": "4.x"
} }
@ -3113,9 +3113,9 @@
} }
}, },
"ant-design-vue": { "ant-design-vue": {
"version": "1.6.5", "version": "1.7.1",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.6.5.tgz", "resolved": "https://registry.npm.taobao.org/ant-design-vue/download/ant-design-vue-1.7.1.tgz",
"integrity": "sha512-FzLrK+JuJUq+g4vd+UjFFi13ZdSsb11EZFapisFXilJxpc1LxqyunZu5AP4nr9vFLs3J4UX2A6q+Rp/Fi6JrLg==", "integrity": "sha1-55d/kjl+/rEXM71hMcFv2rz554Q=",
"requires": { "requires": {
"@ant-design/icons": "^2.1.1", "@ant-design/icons": "^2.1.1",
"@ant-design/icons-vue": "^2.0.0", "@ant-design/icons-vue": "^2.0.0",
@ -3229,8 +3229,8 @@
}, },
"array-tree-filter": { "array-tree-filter": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", "resolved": "https://registry.npm.taobao.org/array-tree-filter/download/array-tree-filter-2.1.0.tgz",
"integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" "integrity": "sha1-hzrAD+yDdJ8lWsjdCDgUtPYykZA="
}, },
"array-union": { "array-union": {
"version": "1.0.2", "version": "1.0.2",
@ -3359,8 +3359,8 @@
}, },
"async-validator": { "async-validator": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.4.0.tgz", "resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-3.4.0.tgz",
"integrity": "sha512-VrFk4eYiJAWKskEz115iiuCf9O0ftnMMPXrOFMqyzGH2KxO7YwncKyn/FgOOP+0MDHMfXL7gLExagCutaZGigA==" "integrity": "sha1-hxs+WUEkv0xOt7zRqeeLRPOwnK4="
}, },
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
@ -3498,8 +3498,8 @@
}, },
"babel-helper-vue-jsx-merge-props": { "babel-helper-vue-jsx-merge-props": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", "resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==" "integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY="
}, },
"babel-jest": { "babel-jest": {
"version": "26.3.0", "version": "26.3.0",
@ -4906,8 +4906,8 @@
}, },
"classnames": { "classnames": {
"version": "2.2.6", "version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", "resolved": "https://registry.npm.taobao.org/classnames/download/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" "integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4="
}, },
"clean-css": { "clean-css": {
"version": "4.2.3", "version": "4.2.3",
@ -5375,7 +5375,7 @@
}, },
"component-classes": { "component-classes": {
"version": "1.2.6", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz", "resolved": "https://registry.npm.taobao.org/component-classes/download/component-classes-1.2.6.tgz",
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
"requires": { "requires": {
"component-indexof": "0.0.3" "component-indexof": "0.0.3"
@ -5389,7 +5389,7 @@
}, },
"component-indexof": { "component-indexof": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz", "resolved": "https://registry.npm.taobao.org/component-indexof/download/component-indexof-0.0.3.tgz",
"integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ=" "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
}, },
"compressible": { "compressible": {
@ -6507,12 +6507,12 @@
}, },
"dom-align": { "dom-align": {
"version": "1.12.0", "version": "1.12.0",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.0.tgz", "resolved": "https://registry.npm.taobao.org/dom-align/download/dom-align-1.12.0.tgz?cache=0&sync_timestamp=1589854754211&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdom-align%2Fdownload%2Fdom-align-1.12.0.tgz",
"integrity": "sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA==" "integrity": "sha1-VvtxVt8LkQmYMDZNLUj4iWP1opw="
}, },
"dom-closest": { "dom-closest": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz", "resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz",
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=", "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
"requires": { "requires": {
"dom-matches": ">=1.0.1" "dom-matches": ">=1.0.1"
@ -6535,13 +6535,13 @@
}, },
"dom-matches": { "dom-matches": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz",
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw=" "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
}, },
"dom-scroll-into-view": { "dom-scroll-into-view": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz", "resolved": "https://registry.npm.taobao.org/dom-scroll-into-view/download/dom-scroll-into-view-2.0.1.tgz",
"integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==" "integrity": "sha1-DezIUigB/Y0/HGujVadNOCxfmJs="
}, },
"dom-serializer": { "dom-serializer": {
"version": "0.2.2", "version": "0.2.2",
@ -9118,7 +9118,7 @@
}, },
"intersperse": { "intersperse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/intersperse/-/intersperse-1.0.0.tgz", "resolved": "https://registry.npm.taobao.org/intersperse/download/intersperse-1.0.0.tgz",
"integrity": "sha1-8lYfsc/vn1J3zDNHoiiGtDUaUYE=" "integrity": "sha1-8lYfsc/vn1J3zDNHoiiGtDUaUYE="
}, },
"invariant": { "invariant": {
@ -9323,12 +9323,12 @@
}, },
"is-mobile": { "is-mobile": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.2.tgz", "resolved": "https://registry.npm.taobao.org/is-mobile/download/is-mobile-2.2.2.tgz?cache=0&sync_timestamp=1592980494392&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-mobile%2Fdownload%2Fis-mobile-2.2.2.tgz",
"integrity": "sha512-wW/SXnYJkTjs++tVK5b6kVITZpAZPtUrt9SF80vvxGiF/Oywal+COk1jlRkiVq15RFNEQKQY31TkV24/1T5cVg==" "integrity": "sha1-9snF1Q7gElTOBec5vdg18e1OmVQ="
}, },
"is-negative-zero": { "is-negative-zero": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/is-negative-zero/download/is-negative-zero-2.0.0.tgz",
"integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
}, },
"is-number": { "is-number": {
@ -9485,8 +9485,8 @@
}, },
"ismobilejs": { "ismobilejs": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", "resolved": "https://registry.npm.taobao.org/ismobilejs/download/ismobilejs-1.1.1.tgz",
"integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==" "integrity": "sha1-xWygro5Sskyg8iul7zIVot27qg4="
}, },
"isobject": { "isobject": {
"version": "3.0.1", "version": "3.0.1",
@ -10427,7 +10427,7 @@
}, },
"json2mq": { "json2mq": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", "resolved": "https://registry.npm.taobao.org/json2mq/download/json2mq-0.2.0.tgz",
"integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=",
"requires": { "requires": {
"string-convert": "^0.2.0" "string-convert": "^0.2.0"
@ -11618,9 +11618,9 @@
} }
}, },
"moment": { "moment": {
"version": "2.27.0", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M="
}, },
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
@ -11659,8 +11659,8 @@
}, },
"mutationobserver-shim": { "mutationobserver-shim": {
"version": "0.3.7", "version": "0.3.7",
"resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.7.tgz", "resolved": "https://registry.npm.taobao.org/mutationobserver-shim/download/mutationobserver-shim-0.3.7.tgz",
"integrity": "sha512-oRIDTyZQU96nAiz2AQyngwx1e89iApl2hN5AOYwyxLUB47UYsU3Wv9lJWqH5y/QdiYkc5HQLi23ZNB3fELdHcQ==" "integrity": "sha1-i/YzsMCwKRoRByVe0ywTCIqMW/M="
}, },
"mute-stream": { "mute-stream": {
"version": "0.0.8", "version": "0.0.8",
@ -11706,9 +11706,9 @@
} }
}, },
"nanopop": { "nanopop": {
"version": "1.3.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/nanopop/-/nanopop-1.3.0.tgz", "resolved": "https://registry.npm.taobao.org/nanopop/download/nanopop-2.1.0.tgz?cache=0&sync_timestamp=1598256090184&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnanopop%2Fdownload%2Fnanopop-2.1.0.tgz",
"integrity": "sha512-DQDhHyPhKLKrXOjVkChsAoWh/WpKuVINDKl4qvFbguqokRJWQBSNSlPzMS+Xy3yBQKeQ39rICMB2asDvdUiVxw==" "integrity": "sha1-I0dlE87iQFiIr9LopLVAZrcLnmA="
}, },
"native-request": { "native-request": {
"version": "1.0.7", "version": "1.0.7",
@ -12074,8 +12074,8 @@
}, },
"omit.js": { "omit.js": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz", "resolved": "https://registry.npm.taobao.org/omit.js/download/omit.js-1.0.2.tgz",
"integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==", "integrity": "sha1-kaFPDrqEBm36AVvzDkdMR/MLyFg=",
"requires": { "requires": {
"babel-runtime": "^6.23.0" "babel-runtime": "^6.23.0"
} }
@ -13497,8 +13497,8 @@
}, },
"raf": { "raf": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "resolved": "https://registry.npm.taobao.org/raf/download/raf-3.4.1.tgz?cache=0&sync_timestamp=1586264003311&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fraf%2Fdownload%2Fraf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", "integrity": "sha1-B0LpmkplUvRF1z4+4DKK8P8e3jk=",
"requires": { "requires": {
"performance-now": "^2.1.0" "performance-now": "^2.1.0"
} }
@ -13924,8 +13924,8 @@
}, },
"resize-observer-polyfill": { "resize-observer-polyfill": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", "resolved": "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" "integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ="
}, },
"resolve": { "resolve": {
"version": "1.17.0", "version": "1.17.0",
@ -14339,13 +14339,13 @@
}, },
"shallow-equal": { "shallow-equal": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", "resolved": "https://registry.npm.taobao.org/shallow-equal/download/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" "integrity": "sha1-TBar+lYEOqINBQMk76aJQLDaedo="
}, },
"shallowequal": { "shallowequal": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "resolved": "https://registry.npm.taobao.org/shallowequal/download/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" "integrity": "sha1-GI1SHelbkIdAT9TctosT3wrk5/g="
}, },
"shebang-command": { "shebang-command": {
"version": "1.2.0", "version": "1.2.0",
@ -14610,6 +14610,11 @@
"is-plain-obj": "^1.0.0" "is-plain-obj": "^1.0.0"
} }
}, },
"sortablejs": {
"version": "1.12.0",
"resolved": "https://registry.npm.taobao.org/sortablejs/download/sortablejs-1.12.0.tgz?cache=0&sync_timestamp=1601884638047&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsortablejs%2Fdownload%2Fsortablejs-1.12.0.tgz",
"integrity": "sha1-7m1+zjWYwq8P6xVZ2YWV5eo3y9Y="
},
"source-list-map": { "source-list-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@ -14908,7 +14913,7 @@
}, },
"string-convert": { "string-convert": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", "resolved": "https://registry.npm.taobao.org/string-convert/download/string-convert-0.2.1.tgz",
"integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c="
}, },
"string-length": { "string-length": {
@ -15439,9 +15444,9 @@
"integrity": "sha512-YHsqn1zksQ4CKVvArN7MIUtGx0eV/yl15vzdiwNFwAnOQQwL1+pNSLW4ISVWUN+PrzcnX4ojLgS4kgVCmue7Pw==" "integrity": "sha512-YHsqn1zksQ4CKVvArN7MIUtGx0eV/yl15vzdiwNFwAnOQQwL1+pNSLW4ISVWUN+PrzcnX4ojLgS4kgVCmue7Pw=="
}, },
"tinycolor2": { "tinycolor2": {
"version": "1.4.1", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", "resolved": "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.2.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" "integrity": "sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM="
}, },
"tmp": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
@ -16166,8 +16171,8 @@
}, },
"vue-ref": { "vue-ref": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/vue-ref/-/vue-ref-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/vue-ref/download/vue-ref-2.0.0.tgz",
"integrity": "sha512-uKNKpFOVeWNqS2mrBZqnpLyXJo5Q+vnkex6JvpENvhXHFNBW/SJTP8vJywLuVT3DpxwXcF9N0dyIiZ4/NpTexQ==" "integrity": "sha1-SDCE1zKr7RHaeWd4qCZqOvDqGpw="
}, },
"vue-router": { "vue-router": {
"version": "3.4.3", "version": "3.4.3",
@ -16269,6 +16274,14 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true "dev": true
}, },
"vuedraggable": {
"version": "2.24.2",
"resolved": "https://registry.npm.taobao.org/vuedraggable/download/vuedraggable-2.24.2.tgz",
"integrity": "sha1-zZj67JmQUjhGnptNI1+6Wln7faI=",
"requires": {
"sortablejs": "^1.10.1"
}
},
"vuejs-logger": { "vuejs-logger": {
"version": "1.5.4", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/vuejs-logger/-/vuejs-logger-1.5.4.tgz", "resolved": "https://registry.npmjs.org/vuejs-logger/-/vuejs-logger-1.5.4.tgz",
@ -16321,8 +16334,8 @@
}, },
"warning": { "warning": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "resolved": "https://registry.npm.taobao.org/warning/download/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "integrity": "sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=",
"requires": { "requires": {
"loose-envify": "^1.0.0" "loose-envify": "^1.0.0"
} }
@ -17205,4 +17218,4 @@
} }
} }
} }
} }

View File

@ -20,7 +20,7 @@
"test:unit": "vue-cli-service test:unit" "test:unit": "vue-cli-service test:unit"
}, },
"dependencies": { "dependencies": {
"ant-design-vue": "^1.6.5", "ant-design-vue": "^1.7.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"dayjs": "^1.8.36", "dayjs": "^1.8.36",
"enquire.js": "^2.1.6", "enquire.js": "^2.1.6",
@ -40,6 +40,7 @@
"vue-filepond": "^6.0.3", "vue-filepond": "^6.0.3",
"vue-ls": "^3.2.1", "vue-ls": "^3.2.1",
"vue-router": "^3.4.3", "vue-router": "^3.4.3",
"vuedraggable": "^2.24.2",
"vuejs-logger": "^1.5.4", "vuejs-logger": "^1.5.4",
"vuex": "^3.5.1" "vuex": "^3.5.1"
}, },
@ -150,4 +151,4 @@
"last 2 versions", "last 2 versions",
"not ie <= 10" "not ie <= 10"
] ]
} }

View File

@ -18,6 +18,16 @@ menuApi.listTree = () => {
}) })
} }
menuApi.listTreeByTeam = team => {
return service({
url: `${baseUrl}/team/tree_view`,
params: {
team: team
},
method: 'get'
})
}
menuApi.create = menu => { menuApi.create = menu => {
return service({ return service({
url: baseUrl, url: baseUrl,
@ -26,6 +36,22 @@ menuApi.create = menu => {
}) })
} }
menuApi.createBatch = menus => {
return service({
url: `${baseUrl}/batch`,
data: menus,
method: 'post'
})
}
menuApi.updateBatch = menus => {
return service({
url: `${baseUrl}/batch`,
data: menus,
method: 'put'
})
}
menuApi.delete = menuId => { menuApi.delete = menuId => {
return service({ return service({
url: `${baseUrl}/${menuId}`, url: `${baseUrl}/${menuId}`,
@ -33,6 +59,14 @@ menuApi.delete = menuId => {
}) })
} }
menuApi.deleteBatch = menuIds => {
return service({
url: `${baseUrl}/batch`,
data: menuIds,
method: 'delete'
})
}
menuApi.get = menuId => { menuApi.get = menuId => {
return service({ return service({
url: `${baseUrl}/${menuId}`, url: `${baseUrl}/${menuId}`,

View File

@ -12,37 +12,37 @@ export default {
props: { props: {
type: { type: {
type: String, type: String,
default: 'primary' default: 'primary',
}, },
icon: { icon: {
type: String, type: String,
default: null default: null,
}, },
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
errored: { errored: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
text: { text: {
type: String, type: String,
default: '' default: '',
}, },
loadedText: { loadedText: {
type: String, type: String,
default: '' default: '',
}, },
erroredText: { erroredText: {
type: String, type: String,
default: '' default: '',
} },
}, },
data() { data() {
return { return {
loaded: false, loaded: false,
hasError: false hasError: false,
} }
}, },
watch: { watch: {
@ -56,9 +56,9 @@ export default {
this.loaded = false this.loaded = false
this.hasError = false this.hasError = false
this.$emit('callback') this.$emit('callback')
}, 800) }, 400)
} }
} },
}, },
computed: { computed: {
computedType() { computedType() {
@ -78,12 +78,12 @@ export default {
return this.hasError ? this.erroredText : this.loadedText return this.hasError ? this.erroredText : this.loadedText
} }
return this.text return this.text
} },
}, },
methods: { methods: {
handleClick() { handleClick() {
this.$emit('click') this.$emit('click')
} },
} },
} }
</script> </script>

View File

@ -1,7 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import { import { OPTIONS } from '@/store/mutation-types'
OPTIONS
} from '@/store/mutation-types'
import optionApi from '@/api/option' import optionApi from '@/api/option'
const keys = [ const keys = [
'blog_url', 'blog_url',
@ -13,7 +11,8 @@ const keys = [
'post_permalink_type', 'post_permalink_type',
'archives_prefix', 'archives_prefix',
'path_suffix', 'path_suffix',
'default_editor' 'default_editor',
'default_menu_team'
] ]
const option = { const option = {
state: { state: {
@ -26,9 +25,7 @@ const option = {
} }
}, },
actions: { actions: {
refreshOptionsCache({ refreshOptionsCache({ commit }) {
commit
}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
optionApi optionApi
.listAllByKeys(keys) .listAllByKeys(keys)

View File

@ -1,4 +1,3 @@
export function triggerWindowResizeEvent() { export function triggerWindowResizeEvent() {
const event = document.createEvent('HTMLEvents') const event = document.createEvent('HTMLEvents')
event.initEvent('resize', true, true) event.initEvent('resize', true, true)
@ -18,3 +17,18 @@ export function decodeHTML(html) {
elem = null elem = null
return output return output
} }
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}

View File

@ -2,390 +2,405 @@
<page-view> <page-view>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col <a-col
:xl="10" :xl="6"
:lg="10" :lg="6"
:md="10" :md="6"
:sm="24" :sm="24"
:xs="24" :xs="24"
class="mb-3" class="mb-3"
> >
<a-card <a-card
:title="title"
:bodyStyle="{ padding: '16px' }" :bodyStyle="{ padding: '16px' }"
title="分组"
> >
<a-form-model <template slot="extra">
ref="menuForm" <ReactiveButton
:model="form.model" type="default"
:rules="form.rules" @click="handleSetDefaultTeam"
layout="horizontal" @callback="handleSetDefaultTeamCallback"
:loading="teams.default.saving"
:errored="teams.default.errored"
text="设为默认"
loadedText="设置成功"
erroredText="设置失败"
></ReactiveButton>
</template>
<div class="menu-teams">
<a-spin :spinning="teams.loading">
<a-empty v-if="teams.data.length===0 && !teams.loading" />
<a-menu
class="w-full"
mode="inline"
v-model="selectedTeam"
v-if="teams.data.length>0"
@select="handleSelectedTeam"
>
<a-menu-item
v-for="(team) in teams.data"
:key="team"
>
{{ team===''?'未分组':team }}{{ defaultMenuTeam===team?'(默认)':'' }}
</a-menu-item>
</a-menu>
</a-spin>
</div>
<a-popover
v-model="teams.form.visible"
title="新增分组"
trigger="click"
placement="bottom"
@visibleChange="handleTeamFormVisibleChange"
destroyTooltipOnHide
> >
<a-form-model-item <template slot="content">
label="名称:" <a-form-model
help="* 页面上所显示的名称" ref="teamForm"
prop="name" :model="teams.form.model"
> :rules="teams.form.rules"
<a-input v-model="form.model.name" /> @keyup.enter.native="handleCreateTeam"
</a-form-model-item>
<a-form-model-item
label="地址:"
help="* 菜单的地址"
prop="url"
>
<a-input v-model="form.model.url" />
</a-form-model-item>
<a-form-model-item
label="上级菜单:"
prop="parentId"
>
<menu-select-tree
:menus="table.data"
v-model="form.model.parentId"
/>
</a-form-model-item>
<a-form-model-item
label="排序编号:"
prop="priority"
>
<a-input-number
v-model="form.model.priority"
:min="0"
style="width:100%"
/>
</a-form-model-item>
<a-form-model-item
v-show="form.moreField"
label="图标:"
help="* 请根据主题的支持选填"
prop="icon"
>
<a-input v-model="form.model.icon" />
</a-form-model-item>
<a-form-model-item
v-show="form.moreField"
label="分组:"
prop="team"
>
<a-auto-complete
:dataSource="computedTeams"
v-model="form.model.team"
allowClear
/>
</a-form-model-item>
<a-form-model-item
v-show="form.moreField"
label="打开方式:"
prop="target"
>
<a-select
defaultValue="_self"
v-model="form.model.target"
> >
<a-select-option value="_self">当前窗口</a-select-option> <a-form-model-item prop="team">
<a-select-option value="_blank">新窗口</a-select-option> <a-input
</a-select> v-model="teams.form.model.team"
</a-form-model-item> autoFocus
<a-form-model-item> />
<ReactiveButton </a-form-model-item>
v-if="!isUpdateMode" <a-form-model-item style="margin-bottom:0">
type="primary" <a-button
@click="handleCreateOrUpdateMenu" type="primary"
@callback="handleSavedCallback" @click="handleCreateTeam"
:loading="form.saving" >
:errored="form.errored" 新增
text="保存" </a-button>
loadedText="保存成功" </a-form-model-item>
erroredText="保存失败" </a-form-model>
></ReactiveButton> </template>
<a-button-group v-else> <a-button
<ReactiveButton type="primary"
type="primary" block
@click="handleCreateOrUpdateMenu" class="mt-3"
@callback="handleSavedCallback" >
:loading="form.saving" 新增分组
:errored="form.errored" </a-button>
text="更新" </a-popover>
loadedText="更新成功"
erroredText="更新失败"
></ReactiveButton>
<a-button
type="dashed"
@click="form.model = {}"
v-if="isUpdateMode"
>返回添加</a-button>
</a-button-group>
<a
class="ml-2"
@click="form.moreField = !form.moreField"
>
更多选项
<a-icon :type="form.moreField ? 'up' : 'down'" />
</a>
</a-form-model-item>
</a-form-model>
</a-card> </a-card>
</a-col> </a-col>
<a-col <a-col
:xl="14" :xl="18"
:lg="14" :lg="18"
:md="14" :md="18"
:sm="24" :sm="24"
:xs="24" :xs="24"
class="pb-3" class="pb-3"
> >
<a-card <a-card :bodyStyle="{ padding: '16px' }">
title="所有菜单" <template slot="title">
:bodyStyle="{ padding: '16px' }" <span>
> {{ menuListTitle }}
<!-- Mobile -->
<a-list
v-if="isMobile()"
itemLayout="vertical"
size="large"
:pagination="false"
:dataSource="table.data"
:loading="table.loading"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
>
<template slot="actions">
<a-dropdown
placement="topLeft"
:trigger="['click']"
>
<span>
<a-icon type="bars" />
</span>
<a-menu slot="overlay">
<a-menu-item>
<a
href="javascript:;"
@click="form.model = item"
>编辑</a>
</a-menu-item>
<a-menu-item>
<a-popconfirm
:title="'你确定要删除【' + item.name + '】菜单?'"
@confirm="handleDeleteMenu(item.id)"
okText="确定"
cancelText="取消"
>
<a href="javascript:;">删除</a>
</a-popconfirm>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
<template slot="extra">
<span>
{{ item.team }}
</span>
</template>
<a-list-item-meta>
<template slot="description">
{{ item.url }}
</template>
<span
slot="title"
style="max-width: 300px;display: block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
{{ item.name }}
</span>
</a-list-item-meta>
</a-list-item>
</a-list>
<!-- Desktop -->
<a-table
v-else
:columns="table.columns"
:dataSource="table.data"
:loading="table.loading"
:rowKey="menu => menu.id"
:scrollToFirstRowOnChange="true"
>
<span
slot="action"
slot-scope="text, record"
>
<a
href="javascript:;"
@click="form.model = record"
>编辑</a>
<a-divider type="vertical" />
<a-popconfirm
:title="'你确定要删除【' + record.name + '】菜单?'"
@confirm="handleDeleteMenu(record.id)"
okText="确定"
cancelText="取消"
>
<a href="javascript:;">删除</a>
</a-popconfirm>
</span> </span>
</a-table> <a-tooltip
slot="action"
title="分组下的菜单为空时,该分组也不会保存"
v-if="list.data.length <= 0"
>
<a-icon
type="info-circle-o"
class="cursor-pointer"
/>
</a-tooltip>
</template>
<template slot="extra">
<a-space>
<ReactiveButton
@click="handleUpdateBatch"
@callback="formBatch.errored=false"
:loading="formBatch.saving"
:errored="formBatch.errored"
text="保存"
loadedText="保存成功"
erroredText="保存失败"
:disabled="list.data.length<=0"
></ReactiveButton>
<a-button
@click="handleOpenCreateMenuForm()"
:disabled="form.visible"
type="primary"
ghost
>
新增
</a-button>
<a-dropdown :trigger="['click']">
<a-menu slot="overlay">
<a-menu-item @click="menuInternalLinkSelector.visible = true">
从系统预设链接添加
</a-menu-item>
<a-menu-item @click="handleDeleteBatch">
删除当前组
</a-menu-item>
</a-menu>
<a-button> 其他
<a-icon type="down" />
</a-button>
</a-dropdown>
</a-space>
</template>
<a-spin :spinning="list.loading">
<MenuForm
v-if="form.visible"
:menu="form.model"
@succeed="handleCreateMenuSucceed()"
@cancel="handleCloseCreateMenuForm()"
/>
<a-empty v-if="list.data.length===0 && !list.loading && !form.visible" />
<MenuTreeNode
v-model="list.data"
:excludedTeams="excludedTeams"
@reload="handleListMenus"
/>
</a-spin>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
<MenuInternalLinkSelector
v-model="menuInternalLinkSelector.visible"
:team="teams.selected"
@reload="handleListMenus"
/>
</page-view> </page-view>
</template> </template>
<script> <script>
import { mixin, mixinDevice } from '@/utils/mixin.js' // components
import MenuSelectTree from './components/MenuSelectTree'
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
import draggable from 'vuedraggable'
import MenuTreeNode from './components/MenuTreeNode'
import MenuForm from './components/MenuForm'
import MenuInternalLinkSelector from './components/MenuInternalLinkSelector'
import { deepClone } from '@/utils/util'
import { mapActions, mapGetters } from 'vuex'
// apis
import menuApi from '@/api/menu' import menuApi from '@/api/menu'
const columns = [ import optionApi from '@/api/option'
{
title: '名称',
dataIndex: 'name',
ellipsis: true,
scopedSlots: { customRender: 'name' },
},
{
title: '地址',
ellipsis: true,
dataIndex: 'url',
},
{
title: '分组',
ellipsis: true,
dataIndex: 'team',
},
{
title: '排序',
dataIndex: 'priority',
},
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' },
},
]
export default { export default {
components: { MenuSelectTree, PageView }, components: { PageView, draggable, MenuTreeNode, MenuForm, MenuInternalLinkSelector },
mixins: [mixin, mixinDevice],
data() { data() {
return { return {
table: { list: {
columns,
data: [], data: [],
loading: false, loading: false,
}, },
form: { form: {
model: { visible: false,
target: '_self', model: {},
}, },
formBatch: {
saving: false, saving: false,
errored: false, errored: false,
rules: {
name: [
{ required: true, message: '* 菜单名称不能为空', trigger: ['change'] },
{ max: 50, message: '* 菜单名称的字符长度不能超过 50', trigger: ['change'] },
],
url: [
{ required: true, message: '* 菜单地址不能为空', trigger: ['change'] },
{ max: 1023, message: '* 菜单地址的字符长度不能超过 1023', trigger: ['change'] },
],
icon: [{ max: 50, message: '* 菜单图标的字符长度不能超过 50', trigger: ['change'] }],
team: [{ max: 255, message: '* 菜单分组的字符长度不能超过 255', trigger: ['change'] }],
},
moreField: false,
}, },
teams: { teams: {
data: [], data: [],
loading: false,
selected: null,
form: {
visible: false,
model: {
team: null,
},
rules: {
team: [{ required: true, message: '分组名称不能为空', trigger: ['change'] }],
},
},
default: {
saving: false,
errored: false,
},
},
menuInternalLinkSelector: {
visible: false,
}, },
} }
}, },
computed: { computed: {
title() { ...mapGetters(['options']),
if (this.isUpdateMode) {
return '修改菜单'
}
return '添加菜单'
},
isUpdateMode() {
return !!this.form.model.id
},
computedTeams() { computedTeams() {
return this.teams.data.filter((item) => { return this.teams.data.filter((item) => {
return item !== '' return item !== ''
}) })
}, },
computedMenusMoved() {
const menus = deepClone(this.list.data)
return this.handleMenuMoved(0, menus)
},
computedMenusWithoutLevel() {
return this.handleGetMenusWithoutLevel(this.computedMenusMoved, [])
},
computedMenuIds() {
return this.computedMenusWithoutLevel.map((menu) => {
return menu.id
})
},
selectedTeam: {
get() {
return [this.teams.selected]
},
set(value) {
this.teams.selected = value[0]
},
},
menuListTitle() {
return this.teams.selected === '' ? '未分组' : this.teams.selected
},
excludedTeams() {
return this.teams.data.filter((item) => {
return item !== this.teams.selected
})
},
defaultMenuTeam() {
return this.options.default_menu_team ? this.options.default_menu_team : ''
},
}, },
created() { created() {
this.handleListMenus()
this.handleListTeams() this.handleListTeams()
}, },
methods: { methods: {
handleListMenus() { ...mapActions(['refreshOptionsCache']),
this.table.loading = true handleListTeams(autoSelectTeam = false) {
this.teams.loading = true
menuApi menuApi
.listTree() .listTeams()
.then((response) => { .then((response) => {
this.table.data = response.data.data this.teams.data = response.data.data
if (!this.teams.selected || autoSelectTeam) {
this.teams.selected = this.teams.data[0]
}
this.handleListMenus()
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.table.loading = false this.teams.loading = false
}, 200) }, 200)
}) })
}, },
handleListTeams() { handleListMenus() {
menuApi.listTeams().then((response) => { this.list.loading = true
this.teams.data = response.data.data
})
},
handleDeleteMenu(id) {
menuApi menuApi
.delete(id) .listTreeByTeam(this.teams.selected)
.then((response) => { .then((response) => {
this.$message.success('删除成功!') this.list.data = response.data.data
}) })
.finally(() => { .finally(() => {
this.handleListMenus() setTimeout(() => {
this.handleListTeams() this.list.loading = false
}, 200)
}) })
}, },
handleCreateOrUpdateMenu() { handleMenuMoved(pid, menus) {
for (let i = 0; i < menus.length; i++) {
menus[i].priority = i
menus[i].parentId = pid
menus[i].team = this.teams.selected
if (menus[i].children && menus[i].children.length > 0) {
this.handleMenuMoved(menus[i].id, menus[i].children)
}
}
return menus
},
handleGetMenusWithoutLevel(menus, result) {
for (var i = 0; i < menus.length; i++) {
result.push(menus[i])
var children = menus[i].children
if (children.length > 0) {
this.handleGetMenusWithoutLevel(children, result)
}
}
return result
},
handleSelectedTeam({ item, key, selectedKeys }) {
this.teams.selected = key
this.handleCloseCreateMenuForm()
this.handleListMenus()
},
handleUpdateBatch() {
this.formBatch.saving = true
menuApi
.updateBatch(this.computedMenusWithoutLevel)
.catch(() => {
this.formBatch.errored = true
})
.finally(() => {
setTimeout(() => {
this.formBatch.saving = false
this.handleListMenus()
}, 400)
})
},
handleDeleteBatch() {
const _this = this const _this = this
_this.$refs.menuForm.validate((valid) => { _this.$confirm({
title: '提示',
content: '确定要删除当前分组以及所有菜单?',
onOk() {
menuApi.deleteBatch(_this.computedMenuIds).finally(() => {
_this.handleListTeams(true)
})
},
})
},
handleTeamFormVisibleChange(visible) {
if (visible) {
this.teams.form.model.team = null
}
},
handleCreateTeam() {
const _this = this
_this.$refs.teamForm.validate((valid) => {
if (valid) { if (valid) {
_this.form.saving = true if (!_this.teams.data.includes(_this.teams.form.model.team)) {
if (_this.isUpdateMode) { _this.teams.data.push(_this.teams.form.model.team)
menuApi
.update(_this.form.model.id, _this.form.model)
.catch(() => {
_this.form.errored = true
})
.finally(() => {
setTimeout(() => {
_this.form.saving = false
}, 400)
})
} else {
menuApi
.create(_this.form.model)
.catch(() => {
_this.form.errored = true
})
.finally(() => {
setTimeout(() => {
_this.form.saving = false
}, 400)
})
} }
_this.teams.selected = _this.teams.form.model.team
_this.teams.form.visible = false
_this.handleListMenus()
} }
}) })
}, },
handleSavedCallback() { handleOpenCreateMenuForm() {
const _this = this this.form.visible = true
if (_this.form.errored) { this.form.model = {
_this.form.errored = false team: this.teams.selected,
target: '_self',
}
},
handleCloseCreateMenuForm() {
this.form.visible = false
this.form.model = {}
},
handleCreateMenuSucceed() {
this.handleCloseCreateMenuForm()
this.handleListMenus()
},
handleSetDefaultTeam() {
this.teams.default.saving = true
optionApi
.save({
default_menu_team: this.teams.selected,
})
.catch(() => {
this.teams.default.errored = true
})
.finally(() => {
setTimeout(() => {
this.teams.default.saving = false
}, 400)
})
},
handleSetDefaultTeamCallback() {
if (this.teams.default.errored) {
this.teams.default.errored = false
} else { } else {
_this.form.model = { target: '_self' } this.refreshOptionsCache()
_this.handleListMenus()
_this.handleListTeams()
} }
}, },
}, },

View File

@ -287,8 +287,8 @@
<a-modal <a-modal
title="提示" title="提示"
v-model="themeDeleteModal.visible" v-model="themeDeleteModal.visible"
:width=416 :width="416"
:closable=false :closable="false"
destroyOnClose destroyOnClose
@ok="handleDeleteTheme(themeDeleteModal.selected.id, themeDeleteModal.deleteSettings)" @ok="handleDeleteTheme(themeDeleteModal.selected.id, themeDeleteModal.deleteSettings)"
@cancel="themeDeleteModal.visible = false" @cancel="themeDeleteModal.visible = false"

View File

@ -0,0 +1,213 @@
<template>
<div>
<a-form-model
labelAlign="left"
ref="menuForm"
:model="menuModel"
:rules="form.rules"
@keyup.enter.native="handleCreateOrUpdateMenu"
>
<a-row :gutter="24">
<a-col
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="名称"
prop="name"
help="* 页面上所显示的名称"
>
<a-input
v-model="menuModel.name"
autoFocus
/>
</a-form-model-item>
</a-col>
<a-col
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="地址"
prop="url"
help="* 菜单的地址"
>
<a-input v-model="menuModel.url" />
</a-form-model-item>
</a-col>
<a-col
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="图标"
prop="icon"
help="* 请根据主题的支持情况选填"
>
<a-input v-model="menuModel.icon" />
</a-form-model-item>
</a-col>
<a-col
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label="打开方式"
prop="target"
>
<a-radio-group
v-model="menuModel.target"
:options="targets"
/>
</a-form-model-item>
</a-col>
<a-col
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="12"
>
<a-form-model-item
label=" "
:colon="false"
>
<a-space>
<ReactiveButton
type="primary"
@click="handleCreateOrUpdateMenu"
@callback="handleSavedCallback"
:loading="form.saving"
:errored="form.errored"
text="保存"
loadedText="保存成功"
erroredText="保存失败"
></ReactiveButton>
<a-button @click="handleCancel"></a-button>
</a-space>
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
</div>
</template>
<script>
import menuApi from '@/api/menu'
const targets = [
{
value: '_self',
label: '当前窗口',
},
{
value: '_blank',
label: '新窗口',
},
]
export default {
name: 'MenuForm',
model: {
prop: 'menu',
event: 'input',
},
props: {
menu: {
type: Object,
default: () => {
return {}
},
},
},
computed: {
menuModel: {
get() {
return this.menu
},
set(value) {
this.$emit('input', value)
},
},
isUpdateMode() {
return !!this.menuModel.id
},
},
data() {
return {
targets,
form: {
rules: {
name: [
{ required: true, message: '* 菜单名称不能为空', trigger: ['change'] },
{ max: 50, message: '* 菜单名称的字符长度不能超过 50', trigger: ['change'] },
],
url: [
{ required: true, message: '* 菜单地址不能为空', trigger: ['change'] },
{ max: 1023, message: '* 菜单地址的字符长度不能超过 1023', trigger: ['change'] },
],
icon: [{ max: 50, message: '* 菜单图标的字符长度不能超过 50', trigger: ['change'] }],
},
saving: false,
errored: false,
},
}
},
methods: {
handleCreateOrUpdateMenu() {
const _this = this
_this.$refs.menuForm.validate((valid) => {
if (valid) {
_this.form.saving = true
if (_this.isUpdateMode) {
menuApi
.update(_this.menuModel.id, _this.menuModel)
.catch(() => {
_this.form.errored = true
})
.finally(() => {
setTimeout(() => {
_this.form.saving = false
}, 400)
})
} else {
menuApi
.create(_this.menuModel)
.catch(() => {
_this.form.errored = true
})
.finally(() => {
setTimeout(() => {
_this.form.saving = false
}, 400)
})
}
}
})
},
handleSavedCallback() {
const _this = this
if (_this.form.errored) {
_this.form.errored = false
} else {
_this.menuModel = { target: '_self' }
_this.$emit('succeed')
}
},
handleCancel() {
this.$emit('cancel')
},
},
}
</script>

View File

@ -0,0 +1,377 @@
<template>
<a-modal
v-model="visible"
title="从系统预设链接添加菜单"
:width="1024"
:bodyStyle="{ padding: '0 24px 24px' }"
>
<template slot="footer">
<a-button @click="handleCancel">
取消
</a-button>
<ReactiveButton
@click="handleCreateBatch"
@callback="handleCreateBatchCallback"
:loading="saving"
:errored="saveErrored"
text="添加"
loadedText="添加成功"
erroredText="添加失败"
:disabled="menus && menus.length <= 0"
></ReactiveButton>
</template>
<a-row :gutter="24">
<a-col :span="12">
<a-spin :spinning="loading">
<div class="custom-tab-wrapper">
<a-tabs default-active-key="1">
<a-tab-pane
key="1"
tab="分类目录"
force-render
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(category,index) in categories"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ category.name }}</span>
<span slot="description">{{ category.fullPath }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(category.name,category.fullPath)"
/>
</a>
</template>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane
key="2"
tab="标签"
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(tag,index) in tags"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ tag.name }}</span>
<span slot="description">{{ tag.fullPath }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(tag.name,tag.fullPath)"
/>
</a>
</template>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane
key="3"
tab="独立页面"
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(item,index) in sheet.independents"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ item.title }}</span>
<span slot="description">{{ item.fullPath }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.title,item.fullPath)"
/>
</a>
</template>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane
key="4"
tab="自定义页面"
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(item,index) in sheet.customs.data"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ item.title }}</span>
<span slot="description">{{ item.fullPath }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.title,item.fullPath)"
/>
</a>
</template>
</a-list-item>
</a-list>
<div class="page-wrapper">
<a-pagination
class="pagination"
:current="sheet.customs.pagination.page"
:total="sheet.customs.pagination.total"
:defaultPageSize="sheet.customs.pagination.size"
:pageSizeOptions="['1', '2', '5', '10', '20', '50', '100']"
showSizeChanger
@showSizeChange="handleSheetPaginationChange"
@change="handleSheetPaginationChange"
showLessItems
/>
</div>
</a-tab-pane>
<a-tab-pane
key="5"
tab="其他"
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(item,index) in otherInternalLinks"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ item.name }}</span>
<span slot="description">{{ item.url }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
>
<a-icon
type="plus-circle"
@click="handleInsertPre(item.name,item.url)"
/>
</a>
</template>
</a-list-item>
</a-list>
</a-tab-pane>
</a-tabs>
</div>
</a-spin>
</a-col>
<a-col :span="12">
<div class="custom-tab-wrapper">
<a-tabs default-active-key="1">
<a-tab-pane
key="1"
tab="备选"
force-render
>
<a-list item-layout="horizontal">
<a-list-item
v-for="(menu,index) in menus"
:key="index"
>
<a-list-item-meta>
<span slot="title">{{ menu.name }}</span>
<span slot="description">{{ menu.url }}</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
class="text-base"
@click="handleRemovePre(index)"
>
<a-icon type="close-circle" />
</a>
</template>
</a-list-item>
</a-list>
</a-tab-pane>
</a-tabs>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import categoryApi from '@/api/category'
import tagApi from '@/api/tag'
import menuApi from '@/api/menu'
import sheetApi from '@/api/sheet'
import optionApi from '@/api/option'
export default {
name: 'MenuInternalLinkSelector',
props: {
value: {
type: Boolean,
default: false,
},
team: {
type: String,
default: '',
},
},
data() {
return {
options: {},
categories: [],
tags: [],
menus: [],
sheet: {
independents: [],
customs: {
data: [],
pagination: {
page: 1,
size: 10,
sort: null,
total: 1,
},
queryParam: {
page: 0,
size: 10,
sort: null,
},
},
},
loading: false,
saving: false,
saveErrored: false,
}
},
computed: {
visible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
},
},
otherInternalLinks() {
const options = this.options
return [
{
name: '分类目录',
url: `${options.blog_url}/${options.categories_prefix}${options.path_suffix}`,
},
{
name: '标签',
url: `${options.blog_url}/${options.tags_prefix}${options.path_suffix}`,
},
{
name: '文章归档',
url: `${options.blog_url}/${options.archives_prefix}${options.path_suffix}`,
},
{
name: 'RSS',
url: `${options.blog_url}/atom.xml`,
},
{
name: '网站地图',
url: `${options.blog_url}/sitemap.xml`,
},
{
name: '网站地图',
url: `${options.blog_url}/sitemap.html`,
},
]
},
},
watch: {
visible(value) {
if (value) {
this.handleFetchAll()
this.handleListSheets()
}
},
},
methods: {
handleFetchAll() {
this.loading = true
Promise.all([optionApi.listAll(), categoryApi.listAll(true), tagApi.listAll(true), sheetApi.listIndependent()])
.then((response) => {
this.options = response[0].data.data
this.categories = response[1].data.data
this.tags = response[2].data.data
this.sheet.independents = response[3].data.data
})
.finally(() => {
setTimeout(() => {
this.loading = false
}, 200)
})
},
handleListSheets() {
this.sheet.customs.queryParam.page = this.sheet.customs.pagination.page - 1
this.sheet.customs.queryParam.size = this.sheet.customs.pagination.size
this.sheet.customs.queryParam.sort = this.sheet.customs.pagination.sort
sheetApi.list(this.sheet.customs.queryParam).then((response) => {
this.sheet.customs.data = response.data.data.content
this.sheet.customs.pagination.total = response.data.data.total
})
},
handleSheetPaginationChange(page, pageSize) {
this.sheet.customs.pagination.page = page
this.sheet.customs.pagination.size = pageSize
this.handleListSheets()
},
handleInsertPre(name, url) {
this.menus.push({
name: name,
url: url,
team: this.team,
})
},
handleRemovePre(index) {
this.menus.splice(index, 1)
},
handleCancel() {
this.menus = []
this.visible = false
this.$emit('reload')
},
handleCreateBatch() {
this.saving = true
menuApi
.createBatch(this.menus)
.catch(() => {
this.saveErrored = false
})
.finally(() => {
setTimeout(() => {
this.saving = false
}, 400)
})
},
handleCreateBatchCallback() {
if (this.saveErrored) {
this.saveErrored = false
} else {
this.handleCancel()
}
},
},
}
</script>

View File

@ -1,57 +0,0 @@
<template>
<a-tree-select
:treeData="menuTreeData"
placeholder="请选择上级菜单,默认为顶级菜单"
treeDefaultExpandAll
:treeDataSimpleMode="true"
:allowClear="true"
:value="menuIdString"
@change="handleSelectionChange"
>
</a-tree-select>
</template>
<script>
export default {
name: 'MenuSelectTree',
model: {
prop: 'menuId',
event: 'change'
},
props: {
menuId: {
type: Number,
required: true,
default: 0
},
menus: {
type: Array,
required: false,
default: () => []
}
},
computed: {
menuTreeData() {
return this.menus.map(menu => {
return {
id: menu.id,
title: menu.name,
value: menu.id.toString(),
pId: menu.parentId
}
})
},
menuIdString() {
return this.menuId.toString()
}
},
methods: {
handleSelectionChange(value, label, extra) {
this.$log.debug('value: ', value)
this.$log.debug('label: ', label)
this.$log.debug('extra: ', extra)
this.$emit('change', value ? parseInt(value) : 0)
}
}
}
</script>

View File

@ -0,0 +1,221 @@
<template>
<a-list item-layout="horizontal">
<draggable
v-bind="dragOptions"
tag="div"
class="item-container"
:list="list"
:value="value"
@input="emitter"
@start="isDragging = true"
@end="isDragging = false"
handle=".title"
>
<transition-group>
<div
:key="item.id"
v-for="(item) in realValue"
>
<a-list-item class="cursor-pointer menu-item">
<a-list-item-meta>
<span
slot="description"
class="inline-block"
>
<a
:href="item.url"
target="_blank"
class="ant-anchor-link-title"
> {{ item.url }} </a>
</span>
<span
slot="title"
class="title cursor-move inline-block font-bold"
>{{ item.name }}
<a-tooltip title="外部链接">
<a-icon
v-if="item.target==='_blank'"
type="link"
/>
</a-tooltip>
{{ item.formVisible?'(正在编辑)':'' }}
</span>
</a-list-item-meta>
<template slot="actions">
<a
href="javascript:void(0);"
@click="handleDelete(item.id)"
>删除</a>
</template>
<template slot="actions">
<a
href="javascript:void(0);"
@click="handleOpenEditForm(item)"
>
编辑
</a>
</template>
<template
slot="actions"
v-if="excludedTeams && excludedTeams.length>0"
>
<a-dropdown :trigger="['click']">
<a
class="ant-dropdown-link"
@click="e => e.preventDefault()"
>
更多
<a-icon type="down" />
</a>
<a-menu slot="overlay">
<a-sub-menu title="移动到分组">
<a-menu-item
v-for="(team,index) in excludedTeams"
:key="index"
@click="handleMoveMenu(item,team)"
>{{ team===''?'未分组':team }}</a-menu-item>
</a-sub-menu>
<a-sub-menu title="复制到分组">
<a-menu-item
v-for="(team,index) in excludedTeams"
:key="index"
@click="handleCopyMenu(item,team)"
>{{ team===''?'未分组':team }}</a-menu-item>
</a-sub-menu>
</a-menu>
</a-dropdown>
</template>
</a-list-item>
<MenuForm
v-if="item.formVisible"
:menu="item"
@succeed="handleUpdateMenuSucceed(item)"
@cancel="handleCloseCreateMenuForm(item)"
/>
<div
class="a-list-nested"
style="margin-left: 44px;"
>
<MenuTreeNode
:list="item.children"
:excludedTeams="excludedTeams"
@reload="onReloadEmit"
/>
</div>
</div>
</transition-group>
</draggable>
</a-list>
</template>
<script>
// components
import draggable from 'vuedraggable'
import MenuForm from './MenuForm'
// apis
import menuApi from '@/api/menu'
import { deepClone } from '@/utils/util'
export default {
name: 'MenuTreeNode',
components: {
draggable,
MenuForm,
},
props: {
value: {
required: false,
type: Array,
default: null,
},
list: {
required: false,
type: Array,
default: null,
},
excludedTeams: {
required: false,
type: Array,
default: null,
},
},
data() {
return {
isDragging: false,
}
},
computed: {
dragOptions() {
return {
animation: 300,
group: 'description',
disabled: false,
ghostClass: 'ghost',
}
},
realValue() {
return this.value ? this.value : this.list
},
},
methods: {
emitter(value) {
this.$emit('input', value)
},
handleDelete(id) {
const _this = this
_this.$confirm({
title: '提示',
content: '确定要删除当前菜单?',
onOk() {
menuApi.delete(id).finally(() => {
_this.onReloadEmit()
})
},
})
},
handleOpenEditForm(item) {
this.$set(item, 'formVisible', true)
},
handleUpdateMenuSucceed(item) {
this.handleCloseCreateMenuForm(item)
// this.$emit('reload')
},
handleCloseCreateMenuForm(item) {
this.$set(item, 'formVisible', false)
},
handleCopyMenu(item, team) {
const menu = deepClone(item)
menu.team = team
menu.parentId = 0
menu.priority = 0
menu.id = null
menuApi.create(menu).then((response) => {
this.$emit('reload')
})
},
handleMoveMenu(item, team) {
const menu = deepClone(item)
menu.team = team
menu.parentId = 0
menu.priority = 0
menuApi.update(menu.id, menu).then((response) => {
this.$emit('reload')
})
},
onReloadEmit() {
this.$emit('reload')
},
},
}
</script>
<style scoped>
.ghost {
opacity: 0.8;
background: #c8ebfb;
}
::v-deep .ant-list-item-action {
display: none;
}
::v-deep .menu-item:hover .ant-list-item-action {
display: block;
}
</style>

View File

@ -145,16 +145,6 @@
@click="form.model = item" @click="form.model = item"
>编辑</a> >编辑</a>
</a-menu-item> </a-menu-item>
<a-menu-item>
<a-popconfirm
:title="'你确定要添加【' + item.name + '】到菜单?'"
@confirm="handleCreateMenuByCategory(item)"
okText="确定"
cancelText="取消"
>
<a href="javascript:void(0);">添加到菜单</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item> <a-menu-item>
<a-popconfirm <a-popconfirm
:title="'你确定要删除【' + item.name + '】分类?'" :title="'你确定要删除【' + item.name + '】分类?'"
@ -216,34 +206,14 @@
@click="form.model = record" @click="form.model = record"
>编辑</a> >编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-dropdown :trigger="['click']"> <a-popconfirm
<a :title="'你确定要删除【' + record.name + '】分类?'"
href="javascript:void(0);" @confirm="handleDeleteCategory(record.id)"
class="ant-dropdown-link" okText="确定"
>更多</a> cancelText="取消"
<a-menu slot="overlay"> >
<a-menu-item key="1"> <a href="javascript:void(0);">删除</a>
<a-popconfirm </a-popconfirm>
:title="'你确定要添加【' + record.name + '】到菜单?'"
@confirm="handleCreateMenuByCategory(record)"
okText="确定"
cancelText="取消"
>
<a href="javascript:void(0);">添加到菜单</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item key="2">
<a-popconfirm
:title="'你确定要删除【' + record.name + '】分类?'"
@confirm="handleDeleteCategory(record.id)"
okText="确定"
cancelText="取消"
>
<a href="javascript:void(0);">删除</a>
</a-popconfirm>
</a-menu-item>
</a-menu>
</a-dropdown>
</span> </span>
</a-table> </a-table>
</a-card> </a-card>
@ -262,34 +232,33 @@
import { mixin, mixinDevice } from '@/utils/mixin.js' import { mixin, mixinDevice } from '@/utils/mixin.js'
import CategorySelectTree from './components/CategorySelectTree' import CategorySelectTree from './components/CategorySelectTree'
import categoryApi from '@/api/category' import categoryApi from '@/api/category'
import menuApi from '@/api/menu'
const columns = [ const columns = [
{ {
title: '名称', title: '名称',
ellipsis: true, ellipsis: true,
dataIndex: 'name' dataIndex: 'name',
}, },
{ {
title: '别名', title: '别名',
ellipsis: true, ellipsis: true,
dataIndex: 'slug' dataIndex: 'slug',
}, },
{ {
title: '描述', title: '描述',
ellipsis: true, ellipsis: true,
dataIndex: 'description' dataIndex: 'description',
}, },
{ {
title: '文章数', title: '文章数',
dataIndex: 'postCount', dataIndex: 'postCount',
scopedSlots: { customRender: 'postCount' } scopedSlots: { customRender: 'postCount' },
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
scopedSlots: { customRender: 'action' } scopedSlots: { customRender: 'action' },
} },
] ]
export default { export default {
@ -300,7 +269,7 @@ export default {
table: { table: {
columns, columns,
data: [], data: [],
loading: false loading: false,
}, },
form: { form: {
model: {}, model: {},
@ -309,16 +278,16 @@ export default {
rules: { rules: {
name: [ name: [
{ required: true, message: '* 分类名称不能为空', trigger: ['change'] }, { required: true, message: '* 分类名称不能为空', trigger: ['change'] },
{ max: 255, message: '* 分类名称的字符长度不能超过 255', trigger: ['change'] } { max: 255, message: '* 分类名称的字符长度不能超过 255', trigger: ['change'] },
], ],
slug: [{ max: 255, message: '* 分类别名的字符长度不能超过 255', trigger: ['change'] }], slug: [{ max: 255, message: '* 分类别名的字符长度不能超过 255', trigger: ['change'] }],
thumbnail: [{ max: 1023, message: '* 封面图链接的字符长度不能超过 1023', trigger: ['change'] }], thumbnail: [{ max: 1023, message: '* 封面图链接的字符长度不能超过 1023', trigger: ['change'] }],
description: [{ max: 100, message: '* 分类描述的字符长度不能超过 100', trigger: ['change'] }] description: [{ max: 100, message: '* 分类描述的字符长度不能超过 100', trigger: ['change'] }],
} },
}, },
thumbnailDrawer: { thumbnailDrawer: {
visible: false visible: false,
} },
} }
}, },
computed: { computed: {
@ -330,7 +299,7 @@ export default {
}, },
isUpdateMode() { isUpdateMode() {
return !!this.form.model.id return !!this.form.model.id
} },
}, },
created() { created() {
this.handleListCategories() this.handleListCategories()
@ -340,7 +309,7 @@ export default {
this.table.loading = true this.table.loading = true
categoryApi categoryApi
.listAll(true) .listAll(true)
.then(response => { .then((response) => {
this.table.data = response.data.data this.table.data = response.data.data
}) })
.finally(() => { .finally(() => {
@ -352,7 +321,7 @@ export default {
handleDeleteCategory(id) { handleDeleteCategory(id) {
categoryApi categoryApi
.delete(id) .delete(id)
.then(response => { .then((response) => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
this.form.model = {} this.form.model = {}
}) })
@ -366,7 +335,7 @@ export default {
*/ */
handleCreateOrUpdateCategory() { handleCreateOrUpdateCategory() {
const _this = this const _this = this
_this.$refs.categoryForm.validate(valid => { _this.$refs.categoryForm.validate((valid) => {
if (valid) { if (valid) {
_this.form.saving = true _this.form.saving = true
if (_this.isUpdateMode) { if (_this.isUpdateMode) {
@ -404,22 +373,13 @@ export default {
_this.handleListCategories() _this.handleListCategories()
} }
}, },
handleCreateMenuByCategory(category) {
const menu = {
name: category.name,
url: `${category.fullPath}`
}
menuApi.create(menu).then(response => {
this.$message.success('添加到菜单成功!')
})
},
handleSelectThumbnail(data) { handleSelectThumbnail(data) {
this.$set(this.form.model, 'thumbnail', encodeURI(data.path)) this.$set(this.form.model, 'thumbnail', encodeURI(data.path))
this.thumbnailDrawer.visible = false this.thumbnailDrawer.visible = false
}, },
handleQueryCategoryPosts(category) { handleQueryCategoryPosts(category) {
this.$router.push({ name: 'PostList', query: { categoryId: category.id } }) this.$router.push({ name: 'PostList', query: { categoryId: category.id } })
} },
} },
} }
</script> </script>

View File

@ -67,16 +67,6 @@
<a href="javascript:;">删除</a> <a href="javascript:;">删除</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item>
<a-popconfirm
:title="'你确定要添加【' + item.title + '】到菜单?'"
@confirm="handleSheetToMenu(item)"
okText="确定"
cancelText="取消"
>
<a href="javascript:void(0);">添加到菜单</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item> <a-menu-item>
<a <a
rel="noopener noreferrer" rel="noopener noreferrer"
@ -278,30 +268,10 @@
<a href="javascript:;">删除</a> <a href="javascript:;">删除</a>
</a-popconfirm> </a-popconfirm>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-dropdown :trigger="['click']"> <a
<a href="javascript:void(0);"
href="javascript:void(0);" @click="handleShowSheetSettings(sheet)"
class="ant-dropdown-link" >设置</a>
>更多</a>
<a-menu slot="overlay">
<a-menu-item key="1">
<a
href="javascript:void(0);"
@click="handleShowSheetSettings(sheet)"
>设置</a>
</a-menu-item>
<a-menu-item key="2">
<a-popconfirm
:title="'你确定要添加【' + sheet.title + '】到菜单?'"
@confirm="handleSheetToMenu(sheet)"
okText="确定"
cancelText="取消"
>
<a href="javascript:void(0);">添加到菜单</a>
</a-popconfirm>
</a-menu-item>
</a-menu>
</a-dropdown>
</span> </span>
</a-table> </a-table>
<div class="page-wrapper"> <div class="page-wrapper">
@ -343,48 +313,47 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
import SheetSettingDrawer from './SheetSettingDrawer' import SheetSettingDrawer from './SheetSettingDrawer'
import TargetCommentDrawer from '../../comment/components/TargetCommentDrawer' import TargetCommentDrawer from '../../comment/components/TargetCommentDrawer'
import sheetApi from '@/api/sheet' import sheetApi from '@/api/sheet'
import menuApi from '@/api/menu'
const customColumns = [ const customColumns = [
{ {
title: '标题', title: '标题',
dataIndex: 'title', dataIndex: 'title',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'sheetTitle' } scopedSlots: { customRender: 'sheetTitle' },
}, },
{ {
title: '状态', title: '状态',
className: 'status', className: 'status',
dataIndex: 'statusProperty', dataIndex: 'statusProperty',
scopedSlots: { customRender: 'status' } scopedSlots: { customRender: 'status' },
}, },
{ {
title: '评论量', title: '评论量',
dataIndex: 'commentCount', dataIndex: 'commentCount',
scopedSlots: { customRender: 'commentCount' } scopedSlots: { customRender: 'commentCount' },
}, },
{ {
title: '访问量', title: '访问量',
dataIndex: 'visits', dataIndex: 'visits',
scopedSlots: { customRender: 'visits' } scopedSlots: { customRender: 'visits' },
}, },
{ {
title: '发布时间', title: '发布时间',
dataIndex: 'createTime', dataIndex: 'createTime',
scopedSlots: { customRender: 'createTime' } scopedSlots: { customRender: 'createTime' },
}, },
{ {
title: '操作', title: '操作',
width: '180px', width: '180px',
scopedSlots: { customRender: 'action' } scopedSlots: { customRender: 'action' },
} },
] ]
export default { export default {
name: 'CustomSheetList', name: 'CustomSheetList',
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
SheetSettingDrawer, SheetSettingDrawer,
TargetCommentDrawer TargetCommentDrawer,
}, },
data() { data() {
return { return {
@ -392,7 +361,7 @@ export default {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1 total: 1,
}, },
queryParam: { queryParam: {
page: 0, page: 0,
@ -400,7 +369,7 @@ export default {
sort: null, sort: null,
keyword: null, keyword: null,
categoryId: null, categoryId: null,
status: null status: null,
}, },
loading: false, loading: false,
sheetStatus: sheetApi.sheetStatus, sheetStatus: sheetApi.sheetStatus,
@ -410,21 +379,21 @@ export default {
sheetSettingVisible: false, sheetSettingVisible: false,
sheetCommentVisible: false, sheetCommentVisible: false,
sheets: [], sheets: [],
menu: {} menu: {},
} }
}, },
computed: { computed: {
formattedSheets() { formattedSheets() {
return this.sheets.map(sheet => { return this.sheets.map((sheet) => {
sheet.statusProperty = this.sheetStatus[sheet.status] sheet.statusProperty = this.sheetStatus[sheet.status]
return sheet return sheet
}) })
} },
}, },
created() { created() {
this.handleListSheets() this.handleListSheets()
}, },
destroyed: function() { destroyed() {
if (this.sheetSettingVisible) { if (this.sheetSettingVisible) {
this.sheetSettingVisible = false this.sheetSettingVisible = false
} }
@ -445,7 +414,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
sheetApi sheetApi
.list(this.queryParam) .list(this.queryParam)
.then(response => { .then((response) => {
this.sheets = response.data.data.content this.sheets = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -461,7 +430,7 @@ export default {
handleEditStatusClick(sheetId, status) { handleEditStatusClick(sheetId, status) {
sheetApi sheetApi
.updateStatus(sheetId, status) .updateStatus(sheetId, status)
.then(response => { .then((response) => {
this.$message.success('操作成功!') this.$message.success('操作成功!')
}) })
.finally(() => { .finally(() => {
@ -471,36 +440,28 @@ export default {
handleDeleteClick(sheetId) { handleDeleteClick(sheetId) {
sheetApi sheetApi
.delete(sheetId) .delete(sheetId)
.then(response => { .then((response) => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
this.handleListSheets() this.handleListSheets()
}) })
}, },
handleSheetToMenu(sheet) {
this.menu['name'] = sheet.title
this.menu['url'] = `${sheet.fullPath}`
menuApi.create(this.menu).then(response => {
this.$message.success('添加到菜单成功!')
this.menu = {}
})
},
handleShowSheetSettings(sheet) { handleShowSheetSettings(sheet) {
sheetApi.get(sheet.id).then(response => { sheetApi.get(sheet.id).then((response) => {
this.selectedSheet = response.data.data this.selectedSheet = response.data.data
this.selectedMetas = this.selectedSheet.metas this.selectedMetas = this.selectedSheet.metas
this.sheetSettingVisible = true this.sheetSettingVisible = true
}) })
}, },
handleShowSheetComments(sheet) { handleShowSheetComments(sheet) {
sheetApi.get(sheet.id).then(response => { sheetApi.get(sheet.id).then((response) => {
this.selectedSheet = response.data.data this.selectedSheet = response.data.data
this.sheetCommentVisible = true this.sheetCommentVisible = true
}) })
}, },
handlePreview(sheetId) { handlePreview(sheetId) {
sheetApi.preview(sheetId).then(response => { sheetApi.preview(sheetId).then((response) => {
window.open(response.data, '_blank') window.open(response.data, '_blank')
}) })
}, },
@ -529,7 +490,7 @@ export default {
}, },
onRefreshSheetMetasFromSetting(metas) { onRefreshSheetMetasFromSetting(metas) {
this.selectedMetas = metas this.selectedMetas = metas
} },
} },
} }
</script> </script>