26个文件已修改
2个文件已添加
18个文件已删除
| | |
| | | env: { |
| | | node: true, |
| | | }, |
| | | extends: ['plugin:vue/recommended', '@vue/prettier'], |
| | | extends: ["plugin:vue/recommended", "@vue/prettier"], |
| | | rules: { |
| | | // 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', |
| | | // 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', |
| | | // 'vue/no-v-html': 'off', |
| | | }, |
| | | parserOptions: { |
| | | parser: 'babel-eslint', |
| | | parser: "babel-eslint", |
| | | }, |
| | | overrides: [ |
| | | { |
| | | files: [ |
| | | '**/__tests__/*.{j,t}s?(x)', |
| | | '**/tests/unit/**/*.spec.{j,t}s?(x)', |
| | | "**/__tests__/*.{j,t}s?(x)", |
| | | "**/tests/unit/**/*.spec.{j,t}s?(x)", |
| | | ], |
| | | env: { |
| | | jest: true, |
| | | }, |
| | | }, |
| | | ], |
| | | } |
| | | }; |
| | |
| | | }); |
| | | } |
| | | |
| | | export function rebuild(data) { |
| | | return request({ |
| | | url: "/package/" + data.id + "/rebuild", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function download(query) { |
| | | return request({ |
| | | url: "/package/download", |
| | |
| | | import request from "@/utils/request"; |
| | | import { loginRSA, tokenName } from "@/config"; |
| | | |
| | | export async function login(data) { |
| | | return request({ |
| | | url: "/user/login", |
| | | url: "/account/login", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function logout() { |
| | | return request({ |
| | | url: "/account/logout", |
| | | method: "post", |
| | | }); |
| | | } |
| | | |
| | |
| | | return request({ |
| | | url: "/user/info", |
| | | method: "post", |
| | | data: { |
| | | [tokenName]: accessToken, |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | export function logout() { |
| | | export function register(data) { |
| | | return request({ |
| | | url: "/logout", |
| | | url: "/user/register", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function register() { |
| | | export function doEdit(data) { |
| | | return request({ |
| | | url: "/register", |
| | | url: "/user/" + data.id, |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function getUsers(query) { |
| | | return request({ |
| | | url: "/user/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function deleteUser(id) { |
| | | return request({ |
| | | url: "/user/" + id, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | export function updatePassword(id, data) { |
| | | return request({ |
| | | url: "/user/" + id + "/password", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'SelectTreeTemplate', |
| | | props: { |
| | | /* 树形结构数据 */ |
| | | treeOptions: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | }, |
| | | }, |
| | | /* 单选/多选 */ |
| | | selectType: { |
| | | type: String, |
| | | default: () => { |
| | | return 'single' |
| | | }, |
| | | }, |
| | | /* 初始选中值key */ |
| | | selectedKey: { |
| | | type: String, |
| | | default: () => { |
| | | return '' |
| | | }, |
| | | }, |
| | | /* 初始选中值name */ |
| | | selectedValue: { |
| | | type: String, |
| | | default: () => { |
| | | return '' |
| | | }, |
| | | }, |
| | | /* 可做选择的层级 */ |
| | | selectLevel: { |
| | | type: [String, Number], |
| | | default: () => { |
| | | return '' |
| | | }, |
| | | }, |
| | | /* 可清空选项 */ |
| | | clearable: { |
| | | type: Boolean, |
| | | default: () => { |
| | | return true |
| | | }, |
| | | export default { |
| | | name: "SelectTreeTemplate", |
| | | props: { |
| | | /* 树形结构数据 */ |
| | | treeOptions: { |
| | | type: Array, |
| | | default: () => { |
| | | return []; |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | defaultProps: { |
| | | children: 'children', |
| | | label: 'name', |
| | | }, |
| | | defaultSelectedKeys: [], //初始选中值数组 |
| | | currentNodeKey: this.selectedKey, |
| | | selectValue: |
| | | this.selectType == 'multiple' |
| | | ? this.selectedValue.split(',') |
| | | : this.selectedValue, //下拉框选中值label |
| | | selectKey: |
| | | this.selectType == 'multiple' |
| | | ? this.selectedKey.split(',') |
| | | : this.selectedKey, //下拉框选中值value |
| | | /* 单选/多选 */ |
| | | selectType: { |
| | | type: String, |
| | | default: () => { |
| | | return "single"; |
| | | }, |
| | | }, |
| | | /* 初始选中值key */ |
| | | selectedKey: { |
| | | type: String, |
| | | default: () => { |
| | | return ""; |
| | | }, |
| | | }, |
| | | /* 初始选中值name */ |
| | | selectedValue: { |
| | | type: String, |
| | | default: () => { |
| | | return ""; |
| | | }, |
| | | }, |
| | | /* 可做选择的层级 */ |
| | | selectLevel: { |
| | | type: [String, Number], |
| | | default: () => { |
| | | return ""; |
| | | }, |
| | | }, |
| | | /* 可清空选项 */ |
| | | clearable: { |
| | | type: Boolean, |
| | | default: () => { |
| | | return true; |
| | | }, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | defaultProps: { |
| | | children: "children", |
| | | label: "name", |
| | | }, |
| | | defaultSelectedKeys: [], //初始选中值数组 |
| | | currentNodeKey: this.selectedKey, |
| | | selectValue: |
| | | this.selectType == "multiple" |
| | | ? this.selectedValue.split(",") |
| | | : this.selectedValue, //下拉框选中值label |
| | | selectKey: |
| | | this.selectType == "multiple" |
| | | ? this.selectedKey.split(",") |
| | | : this.selectedKey, //下拉框选中值value |
| | | }; |
| | | }, |
| | | mounted() { |
| | | const that = this; |
| | | this.initTree(); |
| | | }, |
| | | methods: { |
| | | // 初始化树的值 |
| | | initTree() { |
| | | const that = this; |
| | | if (that.selectedKey) { |
| | | that.defaultSelectedKeys = that.selectedKey.split(","); // 设置默认展开 |
| | | if (that.selectType == "single") { |
| | | that.$refs.treeOption.setCurrentKey(that.selectedKey); // 设置默认选中 |
| | | } else { |
| | | that.$refs.treeOption.setCheckedKeys(that.defaultSelectedKeys); |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | const that = this |
| | | this.initTree() |
| | | // 清除选中 |
| | | clearHandle() { |
| | | const that = this; |
| | | this.selectValue = ""; |
| | | this.selectKey = ""; |
| | | this.defaultSelectedKeys = []; |
| | | this.currentNodeKey = ""; |
| | | this.clearSelected(); |
| | | if (that.selectType == "single") { |
| | | that.$refs.treeOption.setCurrentKey(""); // 设置默认选中 |
| | | } else { |
| | | that.$refs.treeOption.setCheckedKeys([]); |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化树的值 |
| | | initTree() { |
| | | const that = this |
| | | if (that.selectedKey) { |
| | | that.defaultSelectedKeys = that.selectedKey.split(',') // 设置默认展开 |
| | | if (that.selectType == 'single') { |
| | | that.$refs.treeOption.setCurrentKey(that.selectedKey) // 设置默认选中 |
| | | } else { |
| | | that.$refs.treeOption.setCheckedKeys(that.defaultSelectedKeys) |
| | | } |
| | | } |
| | | }, |
| | | // 清除选中 |
| | | clearHandle() { |
| | | const that = this |
| | | this.selectValue = '' |
| | | this.selectKey = '' |
| | | this.defaultSelectedKeys = [] |
| | | this.currentNodeKey = '' |
| | | this.clearSelected() |
| | | if (that.selectType == 'single') { |
| | | that.$refs.treeOption.setCurrentKey('') // 设置默认选中 |
| | | } else { |
| | | that.$refs.treeOption.setCheckedKeys([]) |
| | | } |
| | | }, |
| | | /* 清空选中样式 */ |
| | | clearSelected() { |
| | | const allNode = document.querySelectorAll('#treeOption .el-tree-node') |
| | | allNode.forEach((element) => element.classList.remove('is-current')) |
| | | }, |
| | | // select多选时移除某项操作 |
| | | removeTag(val) { |
| | | this.$refs.treeOption.setCheckedKeys([]) |
| | | }, |
| | | // 点击叶子节点 |
| | | nodeClick(data, node, el) { |
| | | if (data.rank >= this.selectLevel) { |
| | | this.selectValue = data.name |
| | | this.selectKey = data.id |
| | | } |
| | | }, |
| | | // 节点选中操作 |
| | | checkNode(data, node, el) { |
| | | const checkedNodes = this.$refs.treeOption.getCheckedNodes() |
| | | const keyArr = [] |
| | | const valueArr = [] |
| | | checkedNodes.forEach((item) => { |
| | | if (item.rank >= this.selectLevel) { |
| | | keyArr.push(item.id) |
| | | valueArr.push(item.name) |
| | | } |
| | | }) |
| | | this.selectValue = valueArr |
| | | this.selectKey = keyArr |
| | | }, |
| | | /* 清空选中样式 */ |
| | | clearSelected() { |
| | | const allNode = document.querySelectorAll("#treeOption .el-tree-node"); |
| | | allNode.forEach((element) => element.classList.remove("is-current")); |
| | | }, |
| | | } |
| | | // select多选时移除某项操作 |
| | | removeTag(val) { |
| | | this.$refs.treeOption.setCheckedKeys([]); |
| | | }, |
| | | // 点击叶子节点 |
| | | nodeClick(data, node, el) { |
| | | if (data.rank >= this.selectLevel) { |
| | | this.selectValue = data.name; |
| | | this.selectKey = data.id; |
| | | } |
| | | }, |
| | | // 节点选中操作 |
| | | checkNode(data, node, el) { |
| | | const checkedNodes = this.$refs.treeOption.getCheckedNodes(); |
| | | const keyArr = []; |
| | | const valueArr = []; |
| | | checkedNodes.forEach((item) => { |
| | | if (item.rank >= this.selectLevel) { |
| | | keyArr.push(item.id); |
| | | valueArr.push(item.name); |
| | | } |
| | | }); |
| | | this.selectValue = valueArr; |
| | | this.selectKey = keyArr; |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .el-scrollbar .el-scrollbar__view .el-select-dropdown__item { |
| | | height: auto; |
| | | max-height: 274px; |
| | | padding: 0; |
| | | overflow-y: auto; |
| | | } |
| | | .el-scrollbar .el-scrollbar__view .el-select-dropdown__item { |
| | | height: auto; |
| | | max-height: 274px; |
| | | padding: 0; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .el-select-dropdown__item.selected { |
| | | font-weight: normal; |
| | | } |
| | | .el-select-dropdown__item.selected { |
| | | font-weight: normal; |
| | | } |
| | | |
| | | ul li > .el-tree .el-tree-node__content { |
| | | height: auto; |
| | | padding: 0 20px; |
| | | } |
| | | ul li > .el-tree .el-tree-node__content { |
| | | height: auto; |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .el-tree-node__label { |
| | | font-weight: normal; |
| | | } |
| | | .el-tree-node__label { |
| | | font-weight: normal; |
| | | } |
| | | |
| | | .el-tree > .is-current .el-tree-node__label { |
| | | font-weight: 700; |
| | | color: #409eff; |
| | | } |
| | | .el-tree > .is-current .el-tree-node__label { |
| | | font-weight: 700; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .el-tree > .is-current .el-tree-node__children .el-tree-node__label { |
| | | font-weight: normal; |
| | | color: #606266; |
| | | } |
| | | .el-tree > .is-current .el-tree-node__children .el-tree-node__label { |
| | | font-weight: normal; |
| | | color: #606266; |
| | | } |
| | | </style> |
| | | <style lang="scss"> |
| | | /* .vab-tree-select{ |
| | | /* .vab-tree-select{ |
| | | .el-tag__close.el-icon-close{ |
| | | width:0; |
| | | overflow:hidden; |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'VabCharge', |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | }, |
| | | }, |
| | | startVal: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | endVal: { |
| | | type: Number, |
| | | default: 100, |
| | | export default { |
| | | name: "VabCharge", |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | decimals: 2, |
| | | prefix: '', |
| | | suffix: '%', |
| | | separator: ',', |
| | | duration: 3000, |
| | | } |
| | | startVal: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | } |
| | | endVal: { |
| | | type: Number, |
| | | default: 100, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | decimals: 2, |
| | | prefix: "", |
| | | suffix: "%", |
| | | separator: ",", |
| | | duration: 3000, |
| | | }; |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .content { |
| | | .content { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; /* 垂直居中 */ |
| | | justify-content: center; /* 水平居中 */ |
| | | width: 100%; |
| | | background: #000; |
| | | |
| | | .g-number { |
| | | position: absolute; |
| | | top: 27%; |
| | | z-index: 99; |
| | | width: 300px; |
| | | font-size: 32px; |
| | | color: #fff; |
| | | text-align: center; |
| | | } |
| | | |
| | | .g-container { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; /* 垂直居中 */ |
| | | justify-content: center; /* 水平居中 */ |
| | | width: 100%; |
| | | background: #000; |
| | | width: 300px; |
| | | height: 400px; |
| | | margin: auto; |
| | | } |
| | | |
| | | .g-number { |
| | | .g-contrast { |
| | | width: 300px; |
| | | height: 400px; |
| | | overflow: hidden; |
| | | background-color: #000; |
| | | filter: contrast(15) hue-rotate(0); |
| | | animation: hueRotate 10s infinite linear; |
| | | } |
| | | |
| | | .g-circle { |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | width: 300px; |
| | | height: 300px; |
| | | filter: blur(8px); |
| | | |
| | | &::after { |
| | | position: absolute; |
| | | top: 27%; |
| | | z-index: 99; |
| | | width: 300px; |
| | | font-size: 32px; |
| | | color: #fff; |
| | | text-align: center; |
| | | } |
| | | |
| | | .g-container { |
| | | position: relative; |
| | | width: 300px; |
| | | height: 400px; |
| | | margin: auto; |
| | | } |
| | | |
| | | .g-contrast { |
| | | width: 300px; |
| | | height: 400px; |
| | | overflow: hidden; |
| | | background-color: #000; |
| | | filter: contrast(15) hue-rotate(0); |
| | | animation: hueRotate 10s infinite linear; |
| | | } |
| | | |
| | | .g-circle { |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | width: 300px; |
| | | height: 300px; |
| | | filter: blur(8px); |
| | | |
| | | &::after { |
| | | position: absolute; |
| | | top: 40%; |
| | | left: 50%; |
| | | width: 200px; |
| | | height: 200px; |
| | | content: ''; |
| | | background-color: #00ff6f; |
| | | border-radius: 42% 38% 62% 49% / 45%; |
| | | transform: translate(-50%, -50%) rotate(0); |
| | | animation: rotate 10s infinite linear; |
| | | } |
| | | |
| | | &::before { |
| | | position: absolute; |
| | | top: 40%; |
| | | left: 50%; |
| | | z-index: 99; |
| | | width: 176px; |
| | | height: 176px; |
| | | content: ''; |
| | | background-color: #000; |
| | | border-radius: 50%; |
| | | transform: translate(-50%, -50%); |
| | | } |
| | | } |
| | | |
| | | .g-bubbles { |
| | | position: absolute; |
| | | bottom: 0; |
| | | top: 40%; |
| | | left: 50%; |
| | | width: 100px; |
| | | height: 40px; |
| | | width: 200px; |
| | | height: 200px; |
| | | content: ""; |
| | | background-color: #00ff6f; |
| | | filter: blur(5px); |
| | | border-radius: 100px 100px 0 0; |
| | | transform: translate(-50%, 0); |
| | | border-radius: 42% 38% 62% 49% / 45%; |
| | | transform: translate(-50%, -50%) rotate(0); |
| | | animation: rotate 10s infinite linear; |
| | | } |
| | | |
| | | li { |
| | | &::before { |
| | | position: absolute; |
| | | background: #00ff6f; |
| | | top: 40%; |
| | | left: 50%; |
| | | z-index: 99; |
| | | width: 176px; |
| | | height: 176px; |
| | | content: ""; |
| | | background-color: #000; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | @for $i from 0 through 15 { |
| | | li:nth-child(#{$i}) { |
| | | $width: 15 + random(15) + px; |
| | | |
| | | top: 50%; |
| | | left: 15 + random(70) + px; |
| | | width: $width; |
| | | height: $width; |
| | | transform: translate(-50%, -50%); |
| | | animation: moveToTop |
| | | #{random(6) + |
| | | 3}s |
| | | ease-in-out -#{random(5000) / |
| | | 1000}s |
| | | infinite; |
| | | } |
| | | } |
| | | |
| | | @keyframes rotate { |
| | | 50% { |
| | | border-radius: 45% / 42% 38% 58% 49%; |
| | | } |
| | | |
| | | 100% { |
| | | transform: translate(-50%, -50%) rotate(720deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes moveToTop { |
| | | 90% { |
| | | opacity: 1; |
| | | } |
| | | |
| | | 100% { |
| | | opacity: 0.1; |
| | | transform: translate(-50%, -180px); |
| | | } |
| | | } |
| | | |
| | | @keyframes hueRotate { |
| | | 100% { |
| | | filter: contrast(15) hue-rotate(360deg); |
| | | } |
| | | transform: translate(-50%, -50%); |
| | | } |
| | | } |
| | | |
| | | .g-bubbles { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 50%; |
| | | width: 100px; |
| | | height: 40px; |
| | | background-color: #00ff6f; |
| | | filter: blur(5px); |
| | | border-radius: 100px 100px 0 0; |
| | | transform: translate(-50%, 0); |
| | | } |
| | | |
| | | li { |
| | | position: absolute; |
| | | background: #00ff6f; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | @for $i from 0 through 15 { |
| | | li:nth-child(#{$i}) { |
| | | $width: 15 + random(15) + px; |
| | | |
| | | top: 50%; |
| | | left: 15 + random(70) + px; |
| | | width: $width; |
| | | height: $width; |
| | | transform: translate(-50%, -50%); |
| | | animation: moveToTop |
| | | #{random(6) + |
| | | 3}s |
| | | ease-in-out -#{random(5000) / |
| | | 1000}s |
| | | infinite; |
| | | } |
| | | } |
| | | |
| | | @keyframes rotate { |
| | | 50% { |
| | | border-radius: 45% / 42% 38% 58% 49%; |
| | | } |
| | | |
| | | 100% { |
| | | transform: translate(-50%, -50%) rotate(720deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes moveToTop { |
| | | 90% { |
| | | opacity: 1; |
| | | } |
| | | |
| | | 100% { |
| | | opacity: 0.1; |
| | | transform: translate(-50%, -180px); |
| | | } |
| | | } |
| | | |
| | | @keyframes hueRotate { |
| | | 100% { |
| | | filter: contrast(15) hue-rotate(360deg); |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'VabProfile', |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | }, |
| | | }, |
| | | username: { |
| | | type: String, |
| | | default: '', |
| | | }, |
| | | avatar: { |
| | | type: String, |
| | | default: '', |
| | | }, |
| | | iconArray: { |
| | | type: Array, |
| | | default: () => { |
| | | return [ |
| | | { icon: 'bell', url: '' }, |
| | | { icon: 'bookmark', url: '' }, |
| | | { icon: 'cloud-sun', url: '' }, |
| | | ] |
| | | }, |
| | | export default { |
| | | name: "VabProfile", |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | }, |
| | | }, |
| | | data() { |
| | | return {} |
| | | username: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | } |
| | | avatar: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | iconArray: { |
| | | type: Array, |
| | | default: () => { |
| | | return [ |
| | | { icon: "bell", url: "" }, |
| | | { icon: "bookmark", url: "" }, |
| | | { icon: "cloud-sun", url: "" }, |
| | | ]; |
| | | }, |
| | | }, |
| | | }, |
| | | data() { |
| | | return {}; |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .card { |
| | | --card-bg-color: hsl(240, 31%, 25%); |
| | | --card-bg-color-transparent: hsla(240, 31%, 25%, 0.7); |
| | | .card { |
| | | --card-bg-color: hsl(240, 31%, 25%); |
| | | --card-bg-color-transparent: hsla(240, 31%, 25%, 0.7); |
| | | |
| | | position: relative; |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .card-borders { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | .card-borders { |
| | | .border-top { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 2px; |
| | | background: var(--card-bg-color); |
| | | transform: translateX(-100%); |
| | | animation: slide-in-horizontal 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) |
| | | forwards; |
| | | } |
| | | |
| | | .border-right { |
| | | position: absolute; |
| | | right: 0; |
| | | width: 2px; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | background: var(--card-bg-color); |
| | | transform: translateY(100%); |
| | | animation: slide-in-vertical 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) |
| | | forwards; |
| | | } |
| | | |
| | | .border-top { |
| | | .border-bottom { |
| | | position: absolute; |
| | | bottom: 0; |
| | | width: 100%; |
| | | height: 2px; |
| | | background: var(--card-bg-color); |
| | | transform: translateX(100%); |
| | | animation: slide-in-horizontal-reverse 0.8s |
| | | cubic-bezier(0.645, 0.045, 0.355, 1) forwards; |
| | | } |
| | | |
| | | .border-left { |
| | | position: absolute; |
| | | top: 0; |
| | | width: 2px; |
| | | height: 100%; |
| | | background: var(--card-bg-color); |
| | | transform: translateY(-100%); |
| | | animation: slide-in-vertical-reverse 0.8s |
| | | cubic-bezier(0.645, 0.045, 0.355, 1) forwards; |
| | | } |
| | | } |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | height: 100%; |
| | | padding: 40px 0 40px 0; |
| | | background: var(--card-bg-color-transparent); |
| | | opacity: 0; |
| | | transform: scale(0.6); |
| | | animation: bump-in 0.5s 0.8s forwards; |
| | | |
| | | .avatar { |
| | | width: 80px; |
| | | height: 80px; |
| | | border: 1px solid $base-color-white; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transform: scale(0.6); |
| | | animation: bump-in 0.5s 1s forwards; |
| | | } |
| | | |
| | | .username { |
| | | position: relative; |
| | | margin-top: 20px; |
| | | margin-bottom: 20px; |
| | | font-size: 26px; |
| | | color: transparent; |
| | | letter-spacing: 2px; |
| | | animation: fill-text-white 1.2s 2s forwards; |
| | | |
| | | &::before { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 2px; |
| | | background: var(--card-bg-color); |
| | | transform: translateX(-100%); |
| | | animation: slide-in-horizontal 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) |
| | | forwards; |
| | | } |
| | | |
| | | .border-right { |
| | | position: absolute; |
| | | right: 0; |
| | | width: 2px; |
| | | height: 100%; |
| | | background: var(--card-bg-color); |
| | | transform: translateY(100%); |
| | | animation: slide-in-vertical 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) |
| | | forwards; |
| | | } |
| | | |
| | | .border-bottom { |
| | | position: absolute; |
| | | bottom: 0; |
| | | width: 100%; |
| | | height: 2px; |
| | | background: var(--card-bg-color); |
| | | transform: translateX(100%); |
| | | animation: slide-in-horizontal-reverse 0.8s |
| | | cubic-bezier(0.645, 0.045, 0.355, 1) forwards; |
| | | } |
| | | |
| | | .border-left { |
| | | position: absolute; |
| | | top: 0; |
| | | width: 2px; |
| | | height: 100%; |
| | | background: var(--card-bg-color); |
| | | transform: translateY(-100%); |
| | | animation: slide-in-vertical-reverse 0.8s |
| | | cubic-bezier(0.645, 0.045, 0.355, 1) forwards; |
| | | color: black; |
| | | content: ""; |
| | | background: #35b9f1; |
| | | transform: scaleX(0); |
| | | transform-origin: left; |
| | | animation: slide-in-out 1.2s 1.2s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | } |
| | | |
| | | .card-content { |
| | | .social-icons { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | height: 100%; |
| | | padding: 40px 0 40px 0; |
| | | background: var(--card-bg-color-transparent); |
| | | opacity: 0; |
| | | transform: scale(0.6); |
| | | animation: bump-in 0.5s 0.8s forwards; |
| | | |
| | | .avatar { |
| | | width: 80px; |
| | | height: 80px; |
| | | border: 1px solid $base-color-white; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transform: scale(0.6); |
| | | animation: bump-in 0.5s 1s forwards; |
| | | } |
| | | |
| | | .username { |
| | | .social-icon { |
| | | position: relative; |
| | | margin-top: 20px; |
| | | margin-bottom: 20px; |
| | | font-size: 26px; |
| | | color: transparent; |
| | | letter-spacing: 2px; |
| | | animation: fill-text-white 1.2s 2s forwards; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 2.5em; |
| | | height: 2.5em; |
| | | margin: 0 15px; |
| | | color: white; |
| | | text-decoration: none; |
| | | border-radius: 50%; |
| | | |
| | | &::before { |
| | | @for $i from 1 through 3 { |
| | | &:nth-child(#{$i}) { |
| | | &::before { |
| | | animation-delay: 2s + 0.1s * $i; |
| | | } |
| | | |
| | | &::after { |
| | | animation-delay: 2.1s + 0.1s * $i; |
| | | } |
| | | |
| | | svg { |
| | | animation-delay: 2.2s + 0.1s * $i; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &::before, |
| | | &::after { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | color: black; |
| | | content: ''; |
| | | background: #35b9f1; |
| | | transform: scaleX(0); |
| | | transform-origin: left; |
| | | animation: slide-in-out 1.2s 1.2s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | content: ""; |
| | | border-radius: inherit; |
| | | transform: scale(0); |
| | | } |
| | | } |
| | | |
| | | .social-icons { |
| | | display: flex; |
| | | &::before { |
| | | background: #f7f1e3; |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | |
| | | .social-icon { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 2.5em; |
| | | height: 2.5em; |
| | | margin: 0 15px; |
| | | color: white; |
| | | text-decoration: none; |
| | | border-radius: 50%; |
| | | &::after { |
| | | background: #2c3e50; |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | |
| | | @for $i from 1 through 3 { |
| | | &:nth-child(#{$i}) { |
| | | &::before { |
| | | animation-delay: 2s + 0.1s * $i; |
| | | } |
| | | |
| | | &::after { |
| | | animation-delay: 2.1s + 0.1s * $i; |
| | | } |
| | | |
| | | svg { |
| | | animation-delay: 2.2s + 0.1s * $i; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &::before, |
| | | &::after { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | content: ''; |
| | | border-radius: inherit; |
| | | transform: scale(0); |
| | | } |
| | | |
| | | &::before { |
| | | background: #f7f1e3; |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | |
| | | &::after { |
| | | background: #2c3e50; |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | |
| | | svg { |
| | | z-index: 99; |
| | | transform: scale(0); |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | svg { |
| | | z-index: 99; |
| | | transform: scale(0); |
| | | animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @keyframes bump-in { |
| | | 50% { |
| | | transform: scale(1.05); |
| | | } |
| | | |
| | | to { |
| | | opacity: 1; |
| | | transform: scale(1); |
| | | } |
| | | @keyframes bump-in { |
| | | 50% { |
| | | transform: scale(1.05); |
| | | } |
| | | |
| | | @keyframes slide-in-horizontal { |
| | | 50% { |
| | | transform: translateX(0); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: scale(1); |
| | | } |
| | | } |
| | | |
| | | to { |
| | | transform: translateX(100%); |
| | | } |
| | | @keyframes slide-in-horizontal { |
| | | 50% { |
| | | transform: translateX(0); |
| | | } |
| | | |
| | | @keyframes slide-in-horizontal-reverse { |
| | | 50% { |
| | | transform: translateX(0); |
| | | } |
| | | to { |
| | | transform: translateX(100%); |
| | | } |
| | | } |
| | | |
| | | to { |
| | | transform: translateX(-100%); |
| | | } |
| | | @keyframes slide-in-horizontal-reverse { |
| | | 50% { |
| | | transform: translateX(0); |
| | | } |
| | | |
| | | @keyframes slide-in-vertical { |
| | | 50% { |
| | | transform: translateY(0); |
| | | } |
| | | to { |
| | | transform: translateX(-100%); |
| | | } |
| | | } |
| | | |
| | | to { |
| | | transform: translateY(-100%); |
| | | } |
| | | @keyframes slide-in-vertical { |
| | | 50% { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | @keyframes slide-in-vertical-reverse { |
| | | 50% { |
| | | transform: translateY(0); |
| | | } |
| | | to { |
| | | transform: translateY(-100%); |
| | | } |
| | | } |
| | | |
| | | to { |
| | | transform: translateY(100%); |
| | | } |
| | | @keyframes slide-in-vertical-reverse { |
| | | 50% { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | @keyframes slide-in-out { |
| | | 50% { |
| | | transform: scaleX(1); |
| | | transform-origin: left; |
| | | } |
| | | to { |
| | | transform: translateY(100%); |
| | | } |
| | | } |
| | | |
| | | 50.1% { |
| | | transform-origin: right; |
| | | } |
| | | |
| | | 100% { |
| | | transform: scaleX(0); |
| | | transform-origin: right; |
| | | } |
| | | @keyframes slide-in-out { |
| | | 50% { |
| | | transform: scaleX(1); |
| | | transform-origin: left; |
| | | } |
| | | |
| | | @keyframes fill-text-white { |
| | | to { |
| | | color: white; |
| | | } |
| | | 50.1% { |
| | | transform-origin: right; |
| | | } |
| | | |
| | | @keyframes scale-in { |
| | | to { |
| | | transform: scale(1); |
| | | } |
| | | 100% { |
| | | transform: scaleX(0); |
| | | transform-origin: right; |
| | | } |
| | | } |
| | | |
| | | @keyframes fill-text-white { |
| | | to { |
| | | color: white; |
| | | } |
| | | } |
| | | |
| | | @keyframes scale-in { |
| | | to { |
| | | transform: scale(1); |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'VabSnow', |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | }, |
| | | export default { |
| | | name: "VabSnow", |
| | | props: { |
| | | styleObj: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | }, |
| | | }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | } |
| | | }, |
| | | data() { |
| | | return {}; |
| | | }, |
| | | created() {}, |
| | | mounted() {}, |
| | | methods: {}, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .content { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%); |
| | | filter: drop-shadow(0 0 10px white); |
| | | } |
| | | .content { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%); |
| | | filter: drop-shadow(0 0 10px white); |
| | | } |
| | | |
| | | @function random_range($min, $max) { |
| | | $rand: random(); |
| | | $random_range: $min + floor($rand * (($max - $min) + 1)); |
| | | @function random_range($min, $max) { |
| | | $rand: random(); |
| | | $random_range: $min + floor($rand * (($max - $min) + 1)); |
| | | |
| | | @return $random_range; |
| | | } |
| | | @return $random_range; |
| | | } |
| | | |
| | | .snow { |
| | | $total: 200; |
| | | .snow { |
| | | $total: 200; |
| | | |
| | | position: absolute; |
| | | width: 10px; |
| | | height: 10px; |
| | | background: white; |
| | | border-radius: 50%; |
| | | position: absolute; |
| | | width: 10px; |
| | | height: 10px; |
| | | background: white; |
| | | border-radius: 50%; |
| | | |
| | | @for $i from 1 through $total { |
| | | $random-x: random(1000000) * 0.0001vw; |
| | | $random-offset: random_range(-100000, 100000) * 0.0001vw; |
| | | $random-x-end: $random-x + $random-offset; |
| | | $random-x-end-yoyo: $random-x + ($random-offset / 2); |
| | | $random-yoyo-time: random_range(30000, 80000) / 100000; |
| | | $random-yoyo-y: $random-yoyo-time * 100vh; |
| | | $random-scale: random(10000) * 0.0001; |
| | | $fall-duration: random_range(10, 30) * 1s; |
| | | $fall-delay: random(30) * -1s; |
| | | @for $i from 1 through $total { |
| | | $random-x: random(1000000) * 0.0001vw; |
| | | $random-offset: random_range(-100000, 100000) * 0.0001vw; |
| | | $random-x-end: $random-x + $random-offset; |
| | | $random-x-end-yoyo: $random-x + ($random-offset / 2); |
| | | $random-yoyo-time: random_range(30000, 80000) / 100000; |
| | | $random-yoyo-y: $random-yoyo-time * 100vh; |
| | | $random-scale: random(10000) * 0.0001; |
| | | $fall-duration: random_range(10, 30) * 1s; |
| | | $fall-delay: random(30) * -1s; |
| | | |
| | | &:nth-child(#{$i}) { |
| | | opacity: random(10000) * 0.0001; |
| | | transform: translate($random-x, -10px) scale($random-scale); |
| | | animation: fall-#{$i} $fall-duration $fall-delay linear infinite; |
| | | &:nth-child(#{$i}) { |
| | | opacity: random(10000) * 0.0001; |
| | | transform: translate($random-x, -10px) scale($random-scale); |
| | | animation: fall-#{$i} $fall-duration $fall-delay linear infinite; |
| | | } |
| | | |
| | | @keyframes fall-#{$i} { |
| | | #{percentage($random-yoyo-time)} { |
| | | transform: translate($random-x-end, $random-yoyo-y) scale($random-scale); |
| | | } |
| | | |
| | | @keyframes fall-#{$i} { |
| | | #{percentage($random-yoyo-time)} { |
| | | transform: translate($random-x-end, $random-yoyo-y) |
| | | scale($random-scale); |
| | | } |
| | | |
| | | to { |
| | | transform: translate($random-x-end-yoyo, 100vh) scale($random-scale); |
| | | } |
| | | to { |
| | | transform: translate($random-x-end-yoyo, 100vh) scale($random-scale); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { baseURL, tokenName } from '@/config' |
| | | import { baseURL, tokenName } from "@/config"; |
| | | |
| | | export default { |
| | | name: 'VabUpload', |
| | | props: { |
| | | url: { |
| | | type: String, |
| | | default: '/upload', |
| | | required: true, |
| | | }, |
| | | name: { |
| | | type: String, |
| | | default: 'file', |
| | | required: true, |
| | | }, |
| | | limit: { |
| | | type: Number, |
| | | default: 50, |
| | | required: true, |
| | | }, |
| | | size: { |
| | | type: Number, |
| | | default: 1, |
| | | required: true, |
| | | }, |
| | | export default { |
| | | name: "VabUpload", |
| | | props: { |
| | | url: { |
| | | type: String, |
| | | default: "/upload", |
| | | required: true, |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | loading: false, |
| | | dialogVisible: false, |
| | | dialogImageUrl: '', |
| | | action: 'https://vab-unicloud-3a9da9.service.tcloudbase.com/upload', |
| | | headers: {}, |
| | | fileList: [], |
| | | picture: 'picture', |
| | | imgNum: 0, |
| | | imgSuccessNum: 0, |
| | | imgErrorNum: 0, |
| | | typeList: null, |
| | | title: '上传', |
| | | dialogFormVisible: false, |
| | | data: {}, |
| | | name: { |
| | | type: String, |
| | | default: "file", |
| | | required: true, |
| | | }, |
| | | limit: { |
| | | type: Number, |
| | | default: 50, |
| | | required: true, |
| | | }, |
| | | size: { |
| | | type: Number, |
| | | default: 1, |
| | | required: true, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | loading: false, |
| | | dialogVisible: false, |
| | | dialogImageUrl: "", |
| | | action: "https://vab-unicloud-3a9da9.service.tcloudbase.com/upload", |
| | | headers: {}, |
| | | fileList: [], |
| | | picture: "picture", |
| | | imgNum: 0, |
| | | imgSuccessNum: 0, |
| | | imgErrorNum: 0, |
| | | typeList: null, |
| | | title: "上传", |
| | | dialogFormVisible: false, |
| | | data: {}, |
| | | }; |
| | | }, |
| | | computed: { |
| | | percentage() { |
| | | if (this.allImgNum == 0) return 0; |
| | | return this.$baseLodash.round(this.imgNum / this.allImgNum, 2) * 100; |
| | | }, |
| | | }, |
| | | methods: { |
| | | submitUpload() { |
| | | this.$refs.upload.submit(); |
| | | }, |
| | | handleProgress(event, file, fileList) { |
| | | this.loading = true; |
| | | this.show = true; |
| | | }, |
| | | handleChange(file, fileList) { |
| | | if (file.size > 1048576 * this.size) { |
| | | fileList.map((item, index) => { |
| | | if (item === file) { |
| | | fileList.splice(index, 1); |
| | | } |
| | | }); |
| | | this.fileList = fileList; |
| | | } else { |
| | | this.allImgNum = fileList.length; |
| | | } |
| | | }, |
| | | computed: { |
| | | percentage() { |
| | | if (this.allImgNum == 0) return 0 |
| | | return this.$baseLodash.round(this.imgNum / this.allImgNum, 2) * 100 |
| | | }, |
| | | }, |
| | | methods: { |
| | | submitUpload() { |
| | | this.$refs.upload.submit() |
| | | }, |
| | | handleProgress(event, file, fileList) { |
| | | this.loading = true |
| | | this.show = true |
| | | }, |
| | | handleChange(file, fileList) { |
| | | if (file.size > 1048576 * this.size) { |
| | | fileList.map((item, index) => { |
| | | if (item === file) { |
| | | fileList.splice(index, 1) |
| | | } |
| | | }) |
| | | this.fileList = fileList |
| | | } else { |
| | | this.allImgNum = fileList.length |
| | | } |
| | | }, |
| | | handleSuccess(response, file, fileList) { |
| | | this.imgNum = this.imgNum + 1 |
| | | this.imgSuccessNum = this.imgSuccessNum + 1 |
| | | if (fileList.length === this.imgNum) { |
| | | setTimeout(() => { |
| | | this.$baseMessage( |
| | | `上传完成! 共上传${fileList.length}张图片`, |
| | | 'success' |
| | | ) |
| | | }, 1000) |
| | | } |
| | | handleSuccess(response, file, fileList) { |
| | | this.imgNum = this.imgNum + 1; |
| | | this.imgSuccessNum = this.imgSuccessNum + 1; |
| | | if (fileList.length === this.imgNum) { |
| | | setTimeout(() => { |
| | | this.$baseMessage( |
| | | `上传完成! 共上传${fileList.length}张图片`, |
| | | "success" |
| | | ); |
| | | }, 1000); |
| | | } |
| | | |
| | | setTimeout(() => { |
| | | this.loading = false |
| | | this.show = false |
| | | }, 1000) |
| | | }, |
| | | handleError(err, file, fileList) { |
| | | this.imgNum = this.imgNum + 1 |
| | | this.imgErrorNum = this.imgErrorNum + 1 |
| | | this.$baseMessage( |
| | | `文件[${file.raw.name}]上传失败,文件大小为${this.$baseLodash.round( |
| | | file.raw.size / 1024, |
| | | 0 |
| | | )}KB`, |
| | | 'error' |
| | | ) |
| | | setTimeout(() => { |
| | | this.loading = false |
| | | this.show = false |
| | | }, 1000) |
| | | }, |
| | | handleRemove(file, fileList) { |
| | | this.imgNum = this.imgNum - 1 |
| | | this.allNum = this.allNum - 1 |
| | | }, |
| | | handlePreview(file) { |
| | | this.dialogImageUrl = file.url |
| | | this.dialogVisible = true |
| | | }, |
| | | handleExceed(files, fileList) { |
| | | this.$baseMessage( |
| | | `当前限制选择 ${this.limit} 个文件,本次选择了 |
| | | setTimeout(() => { |
| | | this.loading = false; |
| | | this.show = false; |
| | | }, 1000); |
| | | }, |
| | | handleError(err, file, fileList) { |
| | | this.imgNum = this.imgNum + 1; |
| | | this.imgErrorNum = this.imgErrorNum + 1; |
| | | this.$baseMessage( |
| | | `文件[${file.raw.name}]上传失败,文件大小为${this.$baseLodash.round( |
| | | file.raw.size / 1024, |
| | | 0 |
| | | )}KB`, |
| | | "error" |
| | | ); |
| | | setTimeout(() => { |
| | | this.loading = false; |
| | | this.show = false; |
| | | }, 1000); |
| | | }, |
| | | handleRemove(file, fileList) { |
| | | this.imgNum = this.imgNum - 1; |
| | | this.allNum = this.allNum - 1; |
| | | }, |
| | | handlePreview(file) { |
| | | this.dialogImageUrl = file.url; |
| | | this.dialogVisible = true; |
| | | }, |
| | | handleExceed(files, fileList) { |
| | | this.$baseMessage( |
| | | `当前限制选择 ${this.limit} 个文件,本次选择了 |
| | | ${files.length} |
| | | 个文件`, |
| | | 'error' |
| | | ) |
| | | }, |
| | | handleShow(data) { |
| | | this.title = '上传' |
| | | this.data = data |
| | | this.dialogFormVisible = true |
| | | }, |
| | | handleClose() { |
| | | this.fileList = [] |
| | | this.picture = 'picture' |
| | | this.allImgNum = 0 |
| | | this.imgNum = 0 |
| | | this.imgSuccessNum = 0 |
| | | this.imgErrorNum = 0 |
| | | /* if ("development" === process.env.NODE_ENV) { |
| | | "error" |
| | | ); |
| | | }, |
| | | handleShow(data) { |
| | | this.title = "上传"; |
| | | this.data = data; |
| | | this.dialogFormVisible = true; |
| | | }, |
| | | handleClose() { |
| | | this.fileList = []; |
| | | this.picture = "picture"; |
| | | this.allImgNum = 0; |
| | | this.imgNum = 0; |
| | | this.imgSuccessNum = 0; |
| | | this.imgErrorNum = 0; |
| | | /* if ("development" === process.env.NODE_ENV) { |
| | | this.api = process.env.VUE_APP_BASE_API; |
| | | } else { |
| | | this.api = `${window.location.protocol}//${window.location.host}`; |
| | | } |
| | | |
| | | this.action = this.api + this.url; */ |
| | | this.dialogFormVisible = false |
| | | }, |
| | | this.dialogFormVisible = false; |
| | | }, |
| | | } |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .upload { |
| | | height: 500px; |
| | | .upload { |
| | | height: 500px; |
| | | |
| | | .upload-content { |
| | | .el-upload__tip { |
| | | display: block; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | .upload-content { |
| | | .el-upload__tip { |
| | | display: block; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | } |
| | | |
| | | ::v-deep { |
| | | .el-upload--picture-card { |
| | | width: 128px; |
| | | height: 128px; |
| | | margin: 3px 8px 8px 8px; |
| | | border: 2px dashed #c0ccda; |
| | | } |
| | | |
| | | ::v-deep { |
| | | .el-upload--picture-card { |
| | | .el-upload-list--picture { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .el-upload-list--picture-card { |
| | | .el-upload-list__item { |
| | | width: 128px; |
| | | height: 128px; |
| | | margin: 3px 8px 8px 8px; |
| | | border: 2px dashed #c0ccda; |
| | | } |
| | | |
| | | .el-upload-list--picture { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .el-upload-list--picture-card { |
| | | .el-upload-list__item { |
| | | width: 128px; |
| | | height: 128px; |
| | | margin: 3px 8px 8px 8px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | const network = { |
| | | // 默认的接口地址 如果是开发环境和生产环境走vab-mock-server,当然你也可以选择自己配置成需要的接口地址 |
| | | baseURL: |
| | | process.env.NODE_ENV === 'development' |
| | | ? 'http://192.168.20.10:9696/api/v1/' |
| | | : 'http://192.168.20.10:9696/api/v1/', |
| | | process.env.NODE_ENV === "development" |
| | | ? "http://192.168.20.189:9696/api/v1/" |
| | | : "http://192.168.20.189:9696/api/v1/", |
| | | //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form-urlencoded;charset=UTF-8 |
| | | contentType: 'application/json;charset=UTF-8', |
| | | contentType: "application/json;charset=UTF-8", |
| | | //消息框消失时间 |
| | | messageDuration: 3000, |
| | | //最长请求时间 |
| | | requestTimeout: 5000, |
| | | //操作正常code,支持String、Array、int多种类型 |
| | | successCode: [200, 0], |
| | | successCode: [200, 204, 0], |
| | | //登录失效code |
| | | invalidCode: 402, |
| | | //无权限code |
| | | noPermissionCode: 401, |
| | | } |
| | | module.exports = network |
| | | }; |
| | | module.exports = network; |
| | |
| | | * @author chuzhixin 1204505056@qq.com (不想保留author可删除) |
| | | * @description 路由守卫,目前两种模式:all模式与intelligence模式 |
| | | */ |
| | | import router from '@/router' |
| | | import store from '@/store' |
| | | import VabProgress from 'nprogress' |
| | | import 'nprogress/nprogress.css' |
| | | import getPageTitle from '@/utils/pageTitle' |
| | | import router from "@/router"; |
| | | import store from "@/store"; |
| | | import VabProgress from "nprogress"; |
| | | import "nprogress/nprogress.css"; |
| | | import getPageTitle from "@/utils/pageTitle"; |
| | | import { |
| | | authentication, |
| | | loginInterception, |
| | | progressBar, |
| | | recordRoute, |
| | | routesWhiteList, |
| | | } from '@/config' |
| | | } from "@/config"; |
| | | |
| | | VabProgress.configure({ |
| | | easing: 'ease', |
| | | easing: "ease", |
| | | speed: 500, |
| | | trickleSpeed: 200, |
| | | showSpinner: false, |
| | | }) |
| | | }); |
| | | router.beforeResolve(async (to, from, next) => { |
| | | if (progressBar) VabProgress.start() |
| | | let hasToken = store.getters['user/accessToken'] |
| | | if (progressBar) VabProgress.start(); |
| | | let hasToken = store.getters["user/accessToken"]; |
| | | |
| | | if (!loginInterception) hasToken = true |
| | | if (!loginInterception) hasToken = true; |
| | | |
| | | if (hasToken) { |
| | | if (to.path === '/login') { |
| | | next({ path: '/' }) |
| | | if (progressBar) VabProgress.done() |
| | | if (to.path === "/login") { |
| | | next({ path: "/" }); |
| | | if (progressBar) VabProgress.done(); |
| | | } else { |
| | | const hasPermissions = |
| | | store.getters['user/permissions'] && |
| | | store.getters['user/permissions'].length > 0 |
| | | store.getters["user/permissions"] && |
| | | store.getters["user/permissions"].length > 0; |
| | | if (hasPermissions) { |
| | | next() |
| | | next(); |
| | | } else { |
| | | try { |
| | | let permissions |
| | | let permissions; |
| | | if (!loginInterception) { |
| | | //settings.js loginInterception为false时,创建虚拟权限 |
| | | await store.dispatch('user/setPermissions', ['admin']) |
| | | permissions = ['admin'] |
| | | await store.dispatch("user/setPermissions", ["admin"]); |
| | | permissions = ["admin"]; |
| | | } else { |
| | | permissions = await store.dispatch('user/getUserInfo') |
| | | permissions = await store.dispatch("user/getUserInfo"); |
| | | } |
| | | |
| | | let accessRoutes = [] |
| | | if (authentication === 'intelligence') { |
| | | accessRoutes = await store.dispatch('routes/setRoutes', permissions) |
| | | } else if (authentication === 'all') { |
| | | accessRoutes = await store.dispatch('routes/setAllRoutes') |
| | | let accessRoutes = []; |
| | | if (authentication === "intelligence") { |
| | | accessRoutes = await store.dispatch( |
| | | "routes/setRoutes", |
| | | permissions |
| | | ); |
| | | } else if (authentication === "all") { |
| | | accessRoutes = await store.dispatch("routes/setAllRoutes"); |
| | | } |
| | | accessRoutes.forEach((item) => { |
| | | router.addRoute(item) |
| | | }) |
| | | next({ ...to, replace: true }) |
| | | router.addRoute(item); |
| | | }); |
| | | next({ ...to, replace: true }); |
| | | } catch { |
| | | await store.dispatch('user/resetAccessToken') |
| | | if (progressBar) VabProgress.done() |
| | | await store.dispatch("user/resetAccessToken"); |
| | | if (progressBar) VabProgress.done(); |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | if (routesWhiteList.indexOf(to.path) !== -1) { |
| | | next() |
| | | next(); |
| | | } else { |
| | | if (recordRoute) { |
| | | next(`/login?redirect=${to.path}`) |
| | | next(`/login?redirect=${to.path}`); |
| | | } else { |
| | | next('/login') |
| | | next("/login"); |
| | | } |
| | | |
| | | if (progressBar) VabProgress.done() |
| | | if (progressBar) VabProgress.done(); |
| | | } |
| | | } |
| | | document.title = getPageTitle(to.meta.title) |
| | | }) |
| | | document.title = getPageTitle(to.meta.title); |
| | | }); |
| | | router.afterEach(() => { |
| | | if (progressBar) VabProgress.done() |
| | | }) |
| | | if (progressBar) VabProgress.done(); |
| | | }); |
| | |
| | | */ |
| | | const setting = { |
| | | // 开发以及部署时的URL |
| | | publicPath: '', |
| | | publicPath: "", |
| | | // 生产环境构建文件的目录名 |
| | | outputDir: 'dist', |
| | | outputDir: "dist", |
| | | // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。 |
| | | assetsDir: 'static', |
| | | assetsDir: "static", |
| | | // 开发环境每次保存时是否输出为eslint编译警告 |
| | | lintOnSave: false, |
| | | // 进行编译的依赖 |
| | | transpileDependencies: [], |
| | | //标题 (包括初次加载雪花屏的标题 页面的标题 浏览器的标题) |
| | | title: 'Basic自动构建系统', |
| | | title: "Basic自动构建系统", |
| | | //简写 |
| | | abbreviation: 'vab', |
| | | abbreviation: "vab", |
| | | //开发环境端口号 |
| | | devPort: '81', |
| | | devPort: "81", |
| | | //版本号 |
| | | version: process.env.VUE_APP_VERSION, |
| | | //这一项非常重要!请务必保留MIT协议下package.json及copyright作者信息 即可免费商用,不遵守此项约定你将无法使用该框架,如需自定义版权信息请联系QQ1204505056 |
| | | copyright: 'vab', |
| | | copyright: "vab", |
| | | //是否显示页面底部自定义版权信息 |
| | | footerCopyright: true, |
| | | //是否显示顶部进度条 |
| | |
| | | //缓存路由的最大数量 |
| | | keepAliveMaxNum: 99, |
| | | // 路由模式,可选值为 history 或 hash |
| | | routerMode: 'hash', |
| | | routerMode: "hash", |
| | | //不经过token校验的路由 |
| | | routesWhiteList: ['/login', '/register', '/404', '/401'], |
| | | routesWhiteList: ["/login", "/register", "/404", "/401"], |
| | | //加载时显示文字 |
| | | loadingText: '正在加载中...', |
| | | loadingText: "正在加载中...", |
| | | //token名称 |
| | | tokenName: 'accessToken', |
| | | tokenName: "access_token", |
| | | //token在localStorage、sessionStorage存储的key的名称 |
| | | tokenTableName: 'vue-admin-beautiful-2021', |
| | | tokenTableName: "va-integrate", |
| | | //token存储位置localStorage sessionStorage |
| | | storage: 'localStorage', |
| | | storage: "localStorage", |
| | | //token失效回退到登录页时是否记录本次的路由 |
| | | recordRoute: true, |
| | | //是否显示logo,不显示时设置false,显示时请填写remixIcon图标名称,暂时只支持设置remixIcon |
| | | logo: 'vuejs-fill', |
| | | logo: "vuejs-fill", |
| | | //是否显示在页面高亮错误 |
| | | errorLog: ['development', 'production'], |
| | | errorLog: ["development", "production"], |
| | | //是否开启登录拦截 |
| | | loginInterception: true, |
| | | //是否开启登录RSA加密 |
| | | loginRSA: true, |
| | | //intelligence和all两种方式,前者后端权限只控制permissions不控制view文件的import(前后端配合,减轻后端工作量),all方式完全交给后端前端只负责加载 |
| | | authentication: 'intelligence', |
| | | authentication: "intelligence", |
| | | //vertical布局时是否只保持一个子菜单的展开 |
| | | uniqueOpened: true, |
| | | //vertical布局时默认展开的菜单path,使用逗号隔开建议只展开一个 |
| | | defaultOopeneds: ['/vab'], |
| | | defaultOopeneds: ["/vab"], |
| | | //需要加loading层的请求,防止重复提交 |
| | | debounce: ['doEdit'], |
| | | debounce: ["doEdit"], |
| | | //需要自动注入并加载的模块 |
| | | providePlugin: { maptalks: 'maptalks', 'window.maptalks': 'maptalks' }, |
| | | providePlugin: { maptalks: "maptalks", "window.maptalks": "maptalks" }, |
| | | //npm run build时是否自动生成7z压缩包 |
| | | build7z: false, |
| | | //代码生成机生成在view下的文件夹名称 |
| | | templateFolder: 'project', |
| | | templateFolder: "project", |
| | | //是否显示终端donation打印 |
| | | donation: false, |
| | | } |
| | | module.exports = setting |
| | | }; |
| | | module.exports = setting; |
| | |
| | | </span> |
| | | |
| | | <el-dropdown-menu slot="dropdown"> |
| | | <el-dropdown-item command="password" divided>修改密码</el-dropdown-item> |
| | | <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | |
| | | <el-dialog |
| | | title="修改密码" |
| | | :visible.sync="dialogVisible" |
| | | width="25%" |
| | | :modal="false" |
| | | :close-on-click-modal="false" |
| | | destroy-on-close |
| | | > |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
| | | <el-form-item label="原密码" prop="oldPassword"> |
| | | <el-input |
| | | v-model.trim="form.oldPassword" |
| | | autocomplete="off" |
| | | show-password |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="新密码" prop="newPassword"> |
| | | <el-input |
| | | v-model.trim="form.newPassword" |
| | | autocomplete="off" |
| | | show-password |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="确认密码" prop="confirmPassword"> |
| | | <el-input |
| | | v-model.trim="form.confirmPassword" |
| | | autocomplete="off" |
| | | show-password |
| | | ></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="resetPassword">确 定</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | </el-dropdown> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapGetters } from 'vuex' |
| | | import { recordRoute } from '@/config' |
| | | import { mapGetters } from "vuex"; |
| | | import { recordRoute } from "@/config"; |
| | | import { updatePassword } from "@/api/user"; |
| | | |
| | | export default { |
| | | name: 'VabAvatar', |
| | | name: "VabAvatar", |
| | | data() { |
| | | var validatePass = (rule, value, callback) => { |
| | | if (value === "") { |
| | | callback(new Error("请再次输入密码")); |
| | | } else if (value.length < 6) { |
| | | callback(new Error("请输入6位以上密码")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }; |
| | | |
| | | var validatePass2 = (rule, value, callback) => { |
| | | if (value === "") { |
| | | callback(new Error("请再次输入密码")); |
| | | } else if (value !== this.form.newPassword) { |
| | | callback(new Error("两次输入密码不一致!")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }; |
| | | return { |
| | | form: {}, |
| | | rules: { |
| | | oldPassword: [ |
| | | { |
| | | required: true, |
| | | trigger: "blur", |
| | | message: "原密码不能为空", |
| | | }, |
| | | ], |
| | | newPassword: [ |
| | | { |
| | | validator: validatePass, |
| | | required: true, |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | confirmPassword: [ |
| | | { |
| | | validator: validatePass2, |
| | | required: true, |
| | | trigger: "blur", |
| | | }, |
| | | ], |
| | | }, |
| | | dialogVisible: false, |
| | | }; |
| | | }, |
| | | computed: { |
| | | ...mapGetters({ |
| | | avatar: 'user/avatar', |
| | | username: 'user/username', |
| | | avatar: "user/avatar", |
| | | username: "user/username", |
| | | userId: "user/userId", |
| | | }), |
| | | }, |
| | | methods: { |
| | | handleCommand(command) { |
| | | switch (command) { |
| | | case 'logout': |
| | | this.logout() |
| | | break |
| | | case "logout": |
| | | this.logout(); |
| | | break; |
| | | case "password": |
| | | this.dialogVisible = true; |
| | | this.form = {}; |
| | | break; |
| | | } |
| | | }, |
| | | personalCenter() { |
| | | this.$router.push('/personalCenter/personalCenter') |
| | | }, |
| | | logout() { |
| | | this.$baseConfirm( |
| | | '您确定要退出' + this.$baseTitle + '吗?', |
| | | "您确定要退出" + this.$baseTitle + "吗?", |
| | | null, |
| | | async () => { |
| | | await this.$store.dispatch('user/logout') |
| | | await this.$store.dispatch("user/logout"); |
| | | if (recordRoute) { |
| | | const fullPath = this.$route.fullPath |
| | | this.$router.push(`/login?redirect=${fullPath}`) |
| | | const fullPath = this.$route.fullPath; |
| | | this.$router.push(`/login?redirect=${fullPath}`); |
| | | } else { |
| | | this.$router.push('/login') |
| | | this.$router.push("/login"); |
| | | } |
| | | } |
| | | ) |
| | | ); |
| | | }, |
| | | resetPassword() { |
| | | this.$refs["form"].validate(async (valid) => { |
| | | if (valid) { |
| | | let rsp = await updatePassword(this.userId, { |
| | | oldPassword: this.form.oldPassword, |
| | | newPassword: this.form.newPassword, |
| | | }); |
| | | if (rsp && rsp.success) { |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | this.$refs["form"].resetFields(); |
| | | this.dialogVisible = false; |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | handleClose() { |
| | | console.log("close"); |
| | | this.$refs["form"].resetFields(); |
| | | }, |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .avatar-dropdown { |
| | |
| | | <div class="right-panel"> |
| | | <vab-error-log /> |
| | | <vab-full-screen-bar @refresh="refreshRoute" /> |
| | | <vab-icon title="重载所有路由" :pulse="pulse" :icon="['fas', 'redo']" @click="refreshRoute" /> |
| | | <vab-icon |
| | | title="重载所有路由" |
| | | :pulse="pulse" |
| | | :icon="['fas', 'redo']" |
| | | @click="refreshRoute" |
| | | /> |
| | | <vab-avatar /> |
| | | <!-- <vab-icon |
| | | title="退出系统" |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapActions, mapGetters } from 'vuex' |
| | | import { mapActions, mapGetters } from "vuex"; |
| | | |
| | | export default { |
| | | name: 'VabNavBar', |
| | | name: "VabNavBar", |
| | | data() { |
| | | return { |
| | | pulse: false, |
| | | } |
| | | }; |
| | | }, |
| | | computed: { |
| | | ...mapGetters({ |
| | | collapse: 'settings/collapse', |
| | | visitedRoutes: 'tabsBar/visitedRoutes', |
| | | device: 'settings/device', |
| | | routes: 'routes/routes', |
| | | collapse: "settings/collapse", |
| | | visitedRoutes: "tabsBar/visitedRoutes", |
| | | device: "settings/device", |
| | | routes: "routes/routes", |
| | | }), |
| | | }, |
| | | methods: { |
| | | ...mapActions({ |
| | | changeCollapse: 'settings/changeCollapse', |
| | | changeCollapse: "settings/changeCollapse", |
| | | }), |
| | | handleCollapse() { |
| | | this.changeCollapse() |
| | | this.changeCollapse(); |
| | | }, |
| | | async refreshRoute() { |
| | | this.$baseEventBus.$emit('reload-router-view') |
| | | this.pulse = true |
| | | this.$baseEventBus.$emit("reload-router-view"); |
| | | this.pulse = true; |
| | | setTimeout(() => { |
| | | this.pulse = false |
| | | }, 1000) |
| | | this.pulse = false; |
| | | }, 1000); |
| | | }, |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | import 'echarts' |
| | | import VabChart from 'vue-echarts' |
| | | import "echarts"; |
| | | import VabChart from "vue-echarts"; |
| | | |
| | | export default VabChart |
| | | export default VabChart; |
| | |
| | | import Vue from 'vue' |
| | | import ElementUI from 'element-ui' |
| | | import 'element-ui/lib/theme-chalk/display.css' |
| | | import Vue from "vue"; |
| | | import ElementUI from "element-ui"; |
| | | import "element-ui/lib/theme-chalk/display.css"; |
| | | |
| | | import '@/styles/element-variables.scss' |
| | | import "@/styles/element-variables.scss"; |
| | | |
| | | Vue.use(ElementUI, { |
| | | size: 'small', |
| | | }) |
| | | size: "small", |
| | | }); |
| | |
| | | /* 公共引入,勿随意修改,修改时需经过确认 */ |
| | | import Vue from 'vue' |
| | | import './element' |
| | | import './support' |
| | | import '@/styles/vab.scss' |
| | | import '@/remixIcon' |
| | | import '@/colorfulIcon' |
| | | import '@/config/permission' |
| | | import '@/utils/errorLog' |
| | | import './vabIcon' |
| | | import VabPermissions from 'zx-layouts/Permissions' |
| | | import Vab from '@/utils/vab' |
| | | import VabCount from 'zx-count' |
| | | import Vue from "vue"; |
| | | import "./element"; |
| | | import "./support"; |
| | | import "@/styles/vab.scss"; |
| | | import "@/remixIcon"; |
| | | import "@/colorfulIcon"; |
| | | import "@/config/permission"; |
| | | import "@/utils/errorLog"; |
| | | import "./vabIcon"; |
| | | import VabPermissions from "zx-layouts/Permissions"; |
| | | import Vab from "@/utils/vab"; |
| | | import VabCount from "zx-count"; |
| | | |
| | | Vue.use(Vab) |
| | | Vue.use(VabPermissions) |
| | | Vue.use(VabCount) |
| | | Vue.use(Vab); |
| | | Vue.use(VabPermissions); |
| | | Vue.use(VabCount); |
| | |
| | | import Vue from 'vue' |
| | | import VueRouter from 'vue-router' |
| | | import Layout from '@/layouts' |
| | | import { publicPath, routerMode } from '@/config' |
| | | import Vue from "vue"; |
| | | import VueRouter from "vue-router"; |
| | | import Layout from "@/layouts"; |
| | | import { publicPath, routerMode } from "@/config"; |
| | | |
| | | Vue.use(VueRouter) |
| | | Vue.use(VueRouter); |
| | | export const constantRoutes = [ |
| | | { |
| | | path: '/login', |
| | | component: () => import('@/views/login/index'), |
| | | path: "/login", |
| | | component: () => import("@/views/login/index"), |
| | | hidden: true, |
| | | }, |
| | | { |
| | | path: '/401', |
| | | name: '401', |
| | | component: () => import('@/views/401'), |
| | | path: "/401", |
| | | name: "401", |
| | | component: () => import("@/views/401"), |
| | | hidden: true, |
| | | }, |
| | | { |
| | | path: '/404', |
| | | name: '404', |
| | | component: () => import('@/views/404'), |
| | | path: "/404", |
| | | name: "404", |
| | | component: () => import("@/views/404"), |
| | | hidden: true, |
| | | }, |
| | | ] |
| | | ]; |
| | | |
| | | export const asyncRoutes = [ |
| | | { |
| | | path: '/', |
| | | path: "/", |
| | | component: Layout, |
| | | redirect: '/index', |
| | | redirect: "/index", |
| | | children: [ |
| | | { |
| | | path: 'index', |
| | | name: 'Index', |
| | | component: () => import('@/views/index/index'), |
| | | path: "index", |
| | | name: "Index", |
| | | component: () => import("@/views/index/index"), |
| | | meta: { |
| | | title: '首页', |
| | | icon: 'home', |
| | | title: "首页", |
| | | icon: "home", |
| | | affix: true, |
| | | }, |
| | | }, |
| | |
| | | }, |
| | | |
| | | { |
| | | path: '/project', |
| | | path: "/project", |
| | | component: Layout, |
| | | redirect: 'project', |
| | | redirect: "project", |
| | | // name: 'Vab', |
| | | // alwaysShow: true, |
| | | children: [ |
| | | { |
| | | path: 'index', |
| | | component: () => import('@/views/project/index'), |
| | | name: 'Project', |
| | | path: "index", |
| | | component: () => import("@/views/project/index"), |
| | | name: "Project", |
| | | meta: { |
| | | title: '项目', |
| | | icon: 'box-open', |
| | | permissions: ['admin'], |
| | | title: "项目", |
| | | icon: "box-open", |
| | | affix: true, |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | |
| | | { |
| | | path: '*', |
| | | redirect: '/404', |
| | | path: "/user", |
| | | component: Layout, |
| | | redirect: "user", |
| | | // name: 'Vab', |
| | | // alwaysShow: true, |
| | | children: [ |
| | | { |
| | | path: "index", |
| | | component: () => import("@/views/user/index"), |
| | | name: "User", |
| | | meta: { |
| | | title: "用户", |
| | | icon: "user", |
| | | permissions: ["admin"], |
| | | }, |
| | | }, |
| | | ], |
| | | meta: { permissions: ["admin"] }, |
| | | }, |
| | | |
| | | { |
| | | path: "*", |
| | | redirect: "/404", |
| | | hidden: true, |
| | | }, |
| | | ] |
| | | ]; |
| | | |
| | | const router = new VueRouter({ |
| | | base: publicPath, |
| | |
| | | y: 0, |
| | | }), |
| | | routes: constantRoutes, |
| | | }) |
| | | }); |
| | | |
| | | export function resetRouter() { |
| | | location.reload() |
| | | location.reload(); |
| | | } |
| | | |
| | | export default router |
| | | export default router; |
| | |
| | | * @author chuzhixin 1204505056@qq.com (不想保留author可删除) |
| | | * @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partialRoutes是菜单暂未使用 |
| | | */ |
| | | import { asyncRoutes, constantRoutes } from '@/router' |
| | | import { convertRouter, filterAsyncRoutes } from '@/utils/handleRoutes' |
| | | import { asyncRoutes, constantRoutes } from "@/router"; |
| | | import { convertRouter, filterAsyncRoutes } from "@/utils/handleRoutes"; |
| | | |
| | | const state = () => ({ |
| | | routes: [], |
| | | partialRoutes: [], |
| | | }) |
| | | }); |
| | | const getters = { |
| | | routes: (state) => state.routes, |
| | | partialRoutes: (state) => state.partialRoutes, |
| | | } |
| | | }; |
| | | const mutations = { |
| | | setRoutes(state, routes) { |
| | | state.routes = constantRoutes.concat(routes) |
| | | state.routes = constantRoutes.concat(routes); |
| | | }, |
| | | setAllRoutes(state, routes) { |
| | | // state.routes = constantRoutes.concat(routes) |
| | | }, |
| | | setPartialRoutes(state, routes) { |
| | | state.partialRoutes = constantRoutes.concat(routes) |
| | | state.partialRoutes = constantRoutes.concat(routes); |
| | | }, |
| | | } |
| | | }; |
| | | const actions = { |
| | | async setRoutes({ commit }, permissions) { |
| | | //开源版只过滤动态路由permissions,admin不再默认拥有全部权限 |
| | | const finallyAsyncRoutes = await filterAsyncRoutes( |
| | | [...asyncRoutes], |
| | | permissions |
| | | ) |
| | | commit('setRoutes', finallyAsyncRoutes) |
| | | return finallyAsyncRoutes |
| | | ); |
| | | commit("setRoutes", finallyAsyncRoutes); |
| | | return finallyAsyncRoutes; |
| | | }, |
| | | setPartialRoutes({ commit }, accessRoutes) { |
| | | commit('setPartialRoutes', accessRoutes) |
| | | return accessRoutes |
| | | commit("setPartialRoutes", accessRoutes); |
| | | return accessRoutes; |
| | | }, |
| | | } |
| | | export default { state, getters, mutations, actions } |
| | | }; |
| | | export default { state, getters, mutations, actions }; |
| | |
| | | * @description 登录、获取用户信息、退出登录、清除accessToken逻辑,不建议修改 |
| | | */ |
| | | |
| | | import Vue from 'vue' |
| | | import { getUserInfo, login, logout } from '@/api/user' |
| | | import Vue from "vue"; |
| | | import { getUserInfo, login, logout } from "@/api/user"; |
| | | import { |
| | | getAccessToken, |
| | | removeAccessToken, |
| | | setAccessToken, |
| | | } from '@/utils/accessToken' |
| | | import { resetRouter } from '@/router' |
| | | import { title, tokenName } from '@/config' |
| | | } from "@/utils/accessToken"; |
| | | import { resetRouter } from "@/router"; |
| | | import { title, tokenName } from "@/config"; |
| | | |
| | | const state = () => ({ |
| | | accessToken: getAccessToken(), |
| | | username: '', |
| | | avatar: '', |
| | | username: "", |
| | | userId: "", |
| | | avatar: "", |
| | | permissions: [], |
| | | }) |
| | | }); |
| | | const getters = { |
| | | accessToken: (state) => state.accessToken, |
| | | username: (state) => state.username, |
| | | userId: (state) => state.userId, |
| | | avatar: (state) => state.avatar, |
| | | permissions: (state) => state.permissions, |
| | | } |
| | | }; |
| | | const mutations = { |
| | | setAccessToken(state, accessToken) { |
| | | state.accessToken = accessToken |
| | | setAccessToken(accessToken) |
| | | state.accessToken = accessToken; |
| | | setAccessToken(accessToken); |
| | | }, |
| | | setUsername(state, username) { |
| | | state.username = username |
| | | state.username = username; |
| | | }, |
| | | setUserId(state, id) { |
| | | state.userId = id; |
| | | }, |
| | | setAvatar(state, avatar) { |
| | | state.avatar = avatar |
| | | state.avatar = avatar; |
| | | }, |
| | | setPermissions(state, permissions) { |
| | | state.permissions = permissions |
| | | state.permissions = permissions; |
| | | }, |
| | | } |
| | | }; |
| | | const actions = { |
| | | setPermissions({ commit }, permissions) { |
| | | commit('setPermissions', permissions) |
| | | commit("setPermissions", permissions); |
| | | }, |
| | | async login({ commit }, userInfo) { |
| | | const { data } = await login(userInfo) |
| | | const accessToken = data[tokenName] |
| | | const { data } = await login(userInfo); |
| | | const accessToken = data[tokenName]; |
| | | if (accessToken) { |
| | | commit('setAccessToken', accessToken) |
| | | const hour = new Date().getHours() |
| | | commit("setAccessToken", accessToken); |
| | | const hour = new Date().getHours(); |
| | | const thisTime = |
| | | hour < 8 |
| | | ? '早上好' |
| | | ? "早上好" |
| | | : hour <= 11 |
| | | ? '上午好' |
| | | ? "上午好" |
| | | : hour <= 13 |
| | | ? '中午好' |
| | | ? "中午好" |
| | | : hour < 18 |
| | | ? '下午好' |
| | | : '晚上好' |
| | | Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`) |
| | | ? "下午好" |
| | | : "晚上好"; |
| | | Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`); |
| | | } else { |
| | | Vue.prototype.$baseMessage( |
| | | `登录接口异常,未正确返回${tokenName}...`, |
| | | 'error' |
| | | ) |
| | | "error" |
| | | ); |
| | | } |
| | | }, |
| | | async getUserInfo({ commit, state }) { |
| | | const { data } = await getUserInfo(state.accessToken) |
| | | const { data } = await getUserInfo(state.accessToken); |
| | | if (!data) { |
| | | Vue.prototype.$baseMessage('验证失败,请重新登录...', 'error') |
| | | return false |
| | | Vue.prototype.$baseMessage("验证失败,请重新登录...", "error"); |
| | | return false; |
| | | } |
| | | let { permissions, username, avatar } = data |
| | | let { permissions, username, userId, avatar } = data; |
| | | if (permissions && username && Array.isArray(permissions)) { |
| | | commit('setPermissions', permissions) |
| | | commit('setUsername', username) |
| | | commit('setAvatar', avatar) |
| | | return permissions |
| | | commit("setPermissions", permissions); |
| | | commit("setUsername", username); |
| | | commit("setUserId", userId); |
| | | commit("setAvatar", avatar); |
| | | return permissions; |
| | | } else { |
| | | Vue.prototype.$baseMessage('用户信息接口异常', 'error') |
| | | return false |
| | | Vue.prototype.$baseMessage("用户信息接口异常", "error"); |
| | | return false; |
| | | } |
| | | }, |
| | | async logout({ dispatch }) { |
| | | await logout(state.accessToken) |
| | | await dispatch('resetAccessToken') |
| | | await resetRouter() |
| | | await logout(state.accessToken); |
| | | await dispatch("resetAccessToken"); |
| | | await resetRouter(); |
| | | }, |
| | | resetAccessToken({ commit }) { |
| | | commit('setPermissions', []) |
| | | commit('setAccessToken', '') |
| | | removeAccessToken() |
| | | commit("setPermissions", []); |
| | | commit("setAccessToken", ""); |
| | | removeAccessToken(); |
| | | }, |
| | | } |
| | | export default { state, getters, mutations, actions } |
| | | }; |
| | | export default { state, getters, mutations, actions }; |
| | |
| | | import Vue from 'vue' |
| | | import store from '@/store' |
| | | import { isArray, isString } from '@/utils/validate' |
| | | import { errorLog } from '@/config' |
| | | import Vue from "vue"; |
| | | import store from "@/store"; |
| | | import { isArray, isString } from "@/utils/validate"; |
| | | import { errorLog } from "@/config"; |
| | | |
| | | const needErrorLog = errorLog |
| | | const needErrorLog = errorLog; |
| | | const checkNeed = () => { |
| | | const env = process.env.NODE_ENV |
| | | const env = process.env.NODE_ENV; |
| | | if (isString(needErrorLog)) { |
| | | return env === needErrorLog |
| | | return env === needErrorLog; |
| | | } |
| | | if (isArray(needErrorLog)) { |
| | | return needErrorLog.includes(env) |
| | | return needErrorLog.includes(env); |
| | | } |
| | | return false |
| | | } |
| | | return false; |
| | | }; |
| | | if (checkNeed()) { |
| | | Vue.config.errorHandler = (err, vm, info) => { |
| | | console.error('vue-admin-beautiful错误拦截:', err, vm, info) |
| | | const url = window.location.href |
| | | console.error("vue-admin-beautiful错误拦截:", err, vm, info); |
| | | const url = window.location.href; |
| | | Vue.nextTick(() => { |
| | | store.dispatch('errorLog/addErrorLog', { err, vm, info, url }) |
| | | }) |
| | | } |
| | | store.dispatch("errorLog/addErrorLog", { err, vm, info, url }); |
| | | }); |
| | | }; |
| | | } |
| | |
| | | export function convertRouter(asyncRoutes) { |
| | | return asyncRoutes.map((route) => { |
| | | if (route.component) { |
| | | if (route.component === 'Layout') { |
| | | route.component = (resolve) => require(['@/layouts'], resolve) |
| | | } else if (route.component === 'EmptyLayout') { |
| | | if (route.component === "Layout") { |
| | | route.component = (resolve) => require(["@/layouts"], resolve); |
| | | } else if (route.component === "EmptyLayout") { |
| | | route.component = (resolve) => |
| | | require(['@/layouts/EmptyLayout'], resolve) |
| | | require(["@/layouts/EmptyLayout"], resolve); |
| | | } else { |
| | | const index = route.component.indexOf('views') |
| | | const index = route.component.indexOf("views"); |
| | | const path = |
| | | index > 0 ? route.component.slice(index) : `views/${route.component}` |
| | | route.component = (resolve) => require([`@/${path}`], resolve) |
| | | index > 0 ? route.component.slice(index) : `views/${route.component}`; |
| | | route.component = (resolve) => require([`@/${path}`], resolve); |
| | | } |
| | | } |
| | | if (route.children && route.children.length) |
| | | route.children = convertRouter(route.children) |
| | | if (route.children && route.children.length === 0) delete route.children |
| | | return route |
| | | }) |
| | | route.children = convertRouter(route.children); |
| | | if (route.children && route.children.length === 0) delete route.children; |
| | | return route; |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | function hasPermission(permissions, route) { |
| | | if (route.meta && route.meta.permissions) { |
| | | return permissions.some((role) => route.meta.permissions.includes(role)) |
| | | return permissions.some((role) => route.meta.permissions.includes(role)); |
| | | } else { |
| | | return true |
| | | return true; |
| | | } |
| | | } |
| | | |
| | |
| | | * @returns {[]} |
| | | */ |
| | | export function filterAsyncRoutes(routes, permissions) { |
| | | const finallyRoutes = [] |
| | | const finallyRoutes = []; |
| | | routes.forEach((route) => { |
| | | const item = { ...route } |
| | | const item = { ...route }; |
| | | if (hasPermission(permissions, item)) { |
| | | if (item.children) { |
| | | item.children = filterAsyncRoutes(item.children, permissions) |
| | | item.children = filterAsyncRoutes(item.children, permissions); |
| | | } |
| | | finallyRoutes.push(item) |
| | | finallyRoutes.push(item); |
| | | } |
| | | }) |
| | | return finallyRoutes |
| | | }); |
| | | return finallyRoutes; |
| | | } |
| | |
| | | import Vue from 'vue' |
| | | import axios from 'axios' |
| | | import Vue from "vue"; |
| | | import axios from "axios"; |
| | | import { |
| | | baseURL, |
| | | contentType, |
| | |
| | | successCode, |
| | | tokenName, |
| | | loginInterception, |
| | | } from '@/config' |
| | | import store from '@/store' |
| | | import qs from 'qs' |
| | | import router from '@/router' |
| | | import { isArray } from '@/utils/validate' |
| | | } from "@/config"; |
| | | import store from "@/store"; |
| | | import qs from "qs"; |
| | | import router from "@/router"; |
| | | import { isArray } from "@/utils/validate"; |
| | | |
| | | let loadingInstance |
| | | let loadingInstance; |
| | | |
| | | /** |
| | | * @author chuzhixin 1204505056@qq.com (不想保留author可删除) |
| | | * @description 处理code异常 |
| | | * @param {*} code |
| | | * @param {*} msg |
| | | */ |
| | | const handleCode = (code, msg) => { |
| | | console.log(code, msg); |
| | | switch (code) { |
| | | case invalidCode: |
| | | Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') |
| | | store.dispatch('user/resetAccessToken').catch(() => {}) |
| | | Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, "error"); |
| | | store.dispatch("user/resetAccessToken").catch(() => {}); |
| | | if (loginInterception) { |
| | | location.reload() |
| | | location.reload(); |
| | | } |
| | | break |
| | | break; |
| | | case noPermissionCode: |
| | | router.push({ path: '/401' }).catch(() => {}) |
| | | break |
| | | router.push({ path: "/401" }).catch(() => {}); |
| | | break; |
| | | default: |
| | | Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') |
| | | break |
| | | Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, "error"); |
| | | break; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const instance = axios.create({ |
| | | baseURL, |
| | | timeout: requestTimeout, |
| | | headers: { |
| | | 'Content-Type': contentType, |
| | | "Content-Type": contentType, |
| | | }, |
| | | }) |
| | | }); |
| | | |
| | | instance.interceptors.request.use( |
| | | (config) => { |
| | | if (store.getters['user/accessToken']) { |
| | | config.headers[tokenName] = store.getters['user/accessToken'] |
| | | if (store.getters["user/accessToken"]) { |
| | | config.headers["Authorization"] = |
| | | "Bearer " + store.getters["user/accessToken"]; |
| | | } |
| | | //这里会过滤所有为空、0、false的key,如果不需要请自行注释 |
| | | if (config.data) |
| | | config.data = Vue.prototype.$baseLodash.pickBy( |
| | | config.data, |
| | | Vue.prototype.$baseLodash.identity |
| | | ) |
| | | ); |
| | | if ( |
| | | config.data && |
| | | config.headers['Content-Type'] === |
| | | 'application/x-www-form-urlencoded;charset=UTF-8' |
| | | config.headers["Content-Type"] === |
| | | "application/x-www-form-urlencoded;charset=UTF-8" |
| | | ) |
| | | config.data = qs.stringify(config.data) |
| | | config.data = qs.stringify(config.data); |
| | | if (debounce.some((item) => config.url.includes(item))) |
| | | loadingInstance = Vue.prototype.$baseLoading() |
| | | return config |
| | | loadingInstance = Vue.prototype.$baseLoading(); |
| | | return config; |
| | | }, |
| | | (error) => { |
| | | return Promise.reject(error) |
| | | return Promise.reject(error); |
| | | } |
| | | ) |
| | | ); |
| | | |
| | | instance.interceptors.response.use( |
| | | (response) => { |
| | | if (loadingInstance) loadingInstance.close() |
| | | if (loadingInstance) loadingInstance.close(); |
| | | |
| | | const { data, config } = response |
| | | const { code, msg } = data |
| | | const { data, config } = response; |
| | | const { code, msg } = data; |
| | | |
| | | // 操作正常Code数组 |
| | | const codeVerificationArray = isArray(successCode) |
| | | ? [...successCode] |
| | | : [...[successCode]] |
| | | : [...[successCode]]; |
| | | // 是否操作正常 |
| | | if (codeVerificationArray.includes(code)) { |
| | | return data |
| | | return data; |
| | | } else { |
| | | handleCode(code, msg) |
| | | handleCode(code, msg); |
| | | return Promise.reject( |
| | | 'vue-admin-beautiful请求异常拦截:' + |
| | | JSON.stringify({ url: config.url, code, msg }) || 'Error' |
| | | ) |
| | | "请求异常拦截:" + JSON.stringify({ url: config.url, code, msg }) || |
| | | "Error" |
| | | ); |
| | | } |
| | | }, |
| | | (error) => { |
| | | if (loadingInstance) loadingInstance.close() |
| | | const { response, message } = error |
| | | if (loadingInstance) loadingInstance.close(); |
| | | const { response, message } = error; |
| | | if (error.response && error.response.data) { |
| | | const { status, data } = response |
| | | handleCode(status, data.msg || message) |
| | | return Promise.reject(error) |
| | | const { status, data } = response; |
| | | handleCode(status, data.msg || message); |
| | | return Promise.reject(error); |
| | | } else { |
| | | let { message } = error |
| | | if (message === 'Network Error') { |
| | | message = '后端接口连接异常' |
| | | let { message } = error; |
| | | if (message === "Network Error") { |
| | | message = "后端接口连接异常"; |
| | | } |
| | | if (message.includes('timeout')) { |
| | | message = '后端接口请求超时' |
| | | if (message.includes("timeout")) { |
| | | message = "后端接口请求超时"; |
| | | } |
| | | if (message.includes('Request failed with status code')) { |
| | | const code = message.substr(message.length - 3) |
| | | message = '后端接口' + code + '异常' |
| | | if (message.includes("Request failed with status code")) { |
| | | const code = message.substr(message.length - 3); |
| | | message = "后端接口" + code + "异常"; |
| | | } |
| | | Vue.prototype.$baseMessage(message || `后端接口未知异常`, 'error') |
| | | return Promise.reject(error) |
| | | Vue.prototype.$baseMessage(message || `后端接口未知异常`, "error"); |
| | | return Promise.reject(error); |
| | | } |
| | | } |
| | | ) |
| | | ); |
| | | |
| | | export default instance |
| | | export default instance; |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isExternal(path) { |
| | | return /^(https?:|mailto:|tel:)/.test(path) |
| | | return /^(https?:|mailto:|tel:)/.test(path); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isPassword(str) { |
| | | return str.length >= 6 |
| | | return str.length >= 5; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isNumber(value) { |
| | | const reg = /^[0-9]*$/ |
| | | return reg.test(value) |
| | | const reg = /^[0-9]*$/; |
| | | return reg.test(value); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isName(value) { |
| | | const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/ |
| | | return reg.test(value) |
| | | const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/; |
| | | return reg.test(value); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | export function isIP(ip) { |
| | | const reg = |
| | | /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ |
| | | return reg.test(ip) |
| | | /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; |
| | | return reg.test(ip); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | export function isUrl(url) { |
| | | const reg = |
| | | /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ |
| | | return reg.test(url) |
| | | /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; |
| | | return reg.test(url); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isLowerCase(str) { |
| | | const reg = /^[a-z]+$/ |
| | | return reg.test(str) |
| | | const reg = /^[a-z]+$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isUpperCase(str) { |
| | | const reg = /^[A-Z]+$/ |
| | | return reg.test(str) |
| | | const reg = /^[A-Z]+$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isAlphabets(str) { |
| | | const reg = /^[A-Za-z]+$/ |
| | | return reg.test(str) |
| | | const reg = /^[A-Za-z]+$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isString(str) { |
| | | return typeof str === 'string' || str instanceof String |
| | | return typeof str === "string" || str instanceof String; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {arg is any[]|boolean} |
| | | */ |
| | | export function isArray(arg) { |
| | | if (typeof Array.isArray === 'undefined') { |
| | | return Object.prototype.toString.call(arg) === '[object Array]' |
| | | if (typeof Array.isArray === "undefined") { |
| | | return Object.prototype.toString.call(arg) === "[object Array]"; |
| | | } |
| | | return Array.isArray(arg) |
| | | return Array.isArray(arg); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | export function isPort(str) { |
| | | const reg = |
| | | /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/ |
| | | return reg.test(str) |
| | | /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isPhone(str) { |
| | | const reg = /^1\d{10}$/ |
| | | return reg.test(str) |
| | | const reg = /^1\d{10}$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | export function isIdCard(str) { |
| | | const reg = |
| | | /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/ |
| | | return reg.test(str) |
| | | /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isEmail(str) { |
| | | const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ |
| | | return reg.test(str) |
| | | const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isChina(str) { |
| | | const reg = /^[\u4E00-\u9FA5]{2,4}$/ |
| | | return reg.test(str) |
| | | const reg = /^[\u4E00-\u9FA5]{2,4}$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | return ( |
| | | str == null || |
| | | false || |
| | | str === '' || |
| | | str.trim() === '' || |
| | | str.toLocaleLowerCase().trim() === 'null' |
| | | ) |
| | | str === "" || |
| | | str.trim() === "" || |
| | | str.toLocaleLowerCase().trim() === "null" |
| | | ); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | export function isTel(str) { |
| | | const reg = |
| | | /^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})(-| )?)?([0-9]{7,8})((-| |转)*([0-9]{1,4}))?$/ |
| | | return reg.test(str) |
| | | /^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})(-| )?)?([0-9]{7,8})((-| |转)*([0-9]{1,4}))?$/; |
| | | return reg.test(str); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @returns {boolean} |
| | | */ |
| | | export function isNum(str) { |
| | | const reg = /^\d+(\.\d{1,2})?$/ |
| | | return reg.test(str) |
| | | const reg = /^\d+(\.\d{1,2})?$/; |
| | | return reg.test(str); |
| | | } |
| | |
| | | <template> |
| | | <div class="login-container"> |
| | | <el-alert |
| | | title="beautiful boys and girls欢迎加入vue-admin-beautifulQQ群:972435319" |
| | | type="success" |
| | | :closable="false" |
| | | style="position: fixed" |
| | | ></el-alert> |
| | | <el-row> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="16" :xl="16"> |
| | | <div style="color: transparent">占位符</div> |
| | |
| | | class="login-form" |
| | | label-position="left" |
| | | > |
| | | <div class="title">hello !</div> |
| | | <div class="title-tips">欢迎来到{{ title }}!</div> |
| | | <div class="title-tips">欢迎使用{{ title }}!</div> |
| | | <el-form-item style="margin-top: 40px" prop="username"> |
| | | <span class="svg-container svg-container-admin"> |
| | | <vab-icon :icon="['fas', 'user']" /> |
| | |
| | | > |
| | | 登录 |
| | | </el-button> |
| | | <router-link to="/register"> |
| | | <div style="margin-top: 20px">注册</div> |
| | | </router-link> |
| | | </el-form> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | }; |
| | | const validatePassword = (rule, value, callback) => { |
| | | if (!isPassword(value)) { |
| | | callback(new Error("密码不能少于6位")); |
| | | callback(new Error("密码不能少于5位")); |
| | | } else { |
| | | callback(); |
| | | } |
| | |
| | | beforeDestroy() { |
| | | document.body.style.overflow = "auto"; |
| | | }, |
| | | mounted() { |
| | | this.form.username = "admin"; |
| | | this.form.password = "123456"; |
| | | setTimeout(() => { |
| | | this.handleLogin(); |
| | | }, 3000); |
| | | }, |
| | | methods: { |
| | | handlePassword() { |
| | | this.passwordType === "password" |
| | |
| | | ? "/" |
| | | : this.redirect; |
| | | this.$router.push(routerPath).catch(() => {}); |
| | | this.loading = false; |
| | | this.loading = true; |
| | | }) |
| | | .catch(() => { |
| | | this.loading = false; |
| | |
| | | <el-input |
| | | v-model.trim="form.srcPassword" |
| | | autocomplete="off" |
| | | ></el-input> |
| | | show-password |
| | | > |
| | | ></el-input |
| | | > |
| | | </el-form-item> |
| | | <el-form-item label="项目描述" prop="desc"> |
| | | <el-input v-model.trim="form.desc" autocomplete="off"></el-input> |
| | | <el-input v-model="form.desc" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="编译分支" prop="branch"> |
| | | <el-input v-model.trim="form.branch" autocomplete="off"></el-input> |
| | |
| | | save() { |
| | | this.$refs["form"].validate(async (valid) => { |
| | | if (valid) { |
| | | const { msg } = await doEdit(this.form); |
| | | this.$baseMessage(msg, "success"); |
| | | const rsp = await doEdit(this.form); |
| | | if (rsp && rsp.success) { |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | } |
| | | this.$refs["form"].resetFields(); |
| | | this.dialogFormVisible = false; |
| | | this.$emit("fetch-data"); |
| | |
| | | |
| | | <el-table-column prop="state" width="150"> |
| | | <template #default="{ row }"> |
| | | <el-tag>{{ packageState[row.state] }}</el-tag> |
| | | <el-tag :type="row.state == 0 ? 'success' : 'danger'">{{ |
| | | packageState[row.state] |
| | | }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column width="200"> |
| | | <el-table-column width="300"> |
| | | <template #default="scope"> |
| | | <el-button size="small" @click="handlePublish(scope.row)" |
| | | >发布</el-button |
| | | > |
| | | <el-button size="small" @click="handleDownload(scope.row)" |
| | | >下载</el-button |
| | | > |
| | | <el-button |
| | | size="small" |
| | | :disabled="scope.row.state === 0" |
| | | @click="handleRebuild(scope.row)" |
| | | >重置</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <!-- <el-table-column show-overflow-tooltip label="当前版本" prop="latestVersion"></el-table-column> --> |
| | | <el-table-column label="状态"> |
| | | <template #default="{ row }"> |
| | | <el-tag>{{ row.state | stateFilter }}</el-tag> |
| | | <el-tag :type="row.state == 2 ? 'danger' : 'success'">{{ |
| | | row.state | stateFilter |
| | | }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | |
| | | |
| | | <script> |
| | | import { getList, deletePrj, getPkgList, buildPkg } from "@/api/project"; |
| | | import { publish, download } from "@/api/package"; |
| | | import { publish, download, rebuild } from "@/api/package"; |
| | | |
| | | import TableEdit from "./components/ProjectEdit.vue"; |
| | | export default { |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | imgShow: true, |
| | | list: [], |
| | | listLoading: true, |
| | | layout: "total, sizes, prev, pager, next, jumper", |
| | | packageState: [ |
| | | "完成", |
| | | "已提交", |
| | | "排队中", |
| | | "打包中", |
| | | "打包完成", |
| | | "已 提 交", |
| | | "排 队 中", |
| | | "打 包 中", |
| | | "编译失败", |
| | | "打包失败", |
| | | ], |
| | |
| | | const { data, total } = await getPkgList(row.id); |
| | | this.list.forEach((item, idx) => { |
| | | if (item.id === row.id) { |
| | | this.list[idx].pkgList = data; |
| | | this.list[idx].pkgList = data.reverse(); |
| | | } |
| | | }); |
| | | }, |
| | |
| | | } |
| | | }); |
| | | }, |
| | | handleRebuild(row) { |
| | | rebuild(row).then((rsp) => { |
| | | if (rsp && rsp.success) { |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | } else { |
| | | this.$baseMessage("保存失败", "error"); |
| | | } |
| | | }); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <el-dialog |
| | | :title="title" |
| | | :visible.sync="dialogFormVisible" |
| | | width="500px" |
| | | :close-on-click-modal="false" |
| | | @close="close" |
| | | > |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
| | | <el-form-item label="用户名" prop="username"> |
| | | <el-tag v-if="!createAction">{{ form.username }}</el-tag> |
| | | <el-input |
| | | v-else |
| | | v-model.trim="form.username" |
| | | autocomplete="off" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="姓名" prop="name"> |
| | | <el-input v-model.trim="form.name" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="手机号" prop="tel"> |
| | | <el-input v-model.trim="form.tel" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="权限" prop="permissions"> |
| | | <el-radio-group v-model="form.permissions"> |
| | | <el-radio label="admin">管理员</el-radio> |
| | | <el-radio label="user">用户</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item v-if="createAction" label="密码" prop="tel"> |
| | | <el-input v-model.trim="form.password" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="close">取 消</el-button> |
| | | <el-button type="primary" @click="save">确 定</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import { doEdit, register } from "@/api/user"; |
| | | |
| | | export default { |
| | | name: "UserEdit", |
| | | data() { |
| | | return { |
| | | activeName: "base", |
| | | form: { |
| | | name: "", |
| | | username: "", |
| | | }, |
| | | rules: { |
| | | username: [ |
| | | { required: true, trigger: "blur", message: "请输入用户名" }, |
| | | ], |
| | | password: [{ required: true, trigger: "blur", message: "请输入密码" }], |
| | | }, |
| | | title: "", |
| | | dialogFormVisible: false, |
| | | createAction: false, |
| | | }; |
| | | }, |
| | | created() {}, |
| | | methods: { |
| | | showEdit(row) { |
| | | if (!row) { |
| | | this.title = "添加"; |
| | | this.createAction = true; |
| | | this.form.permissions = "user"; |
| | | } else { |
| | | this.title = "编辑"; |
| | | this.form = Object.assign({}, row); |
| | | this.createAction = false; |
| | | } |
| | | this.dialogFormVisible = true; |
| | | }, |
| | | close() { |
| | | this.$refs["form"].resetFields(); |
| | | this.form = this.$options.data().form; |
| | | this.dialogFormVisible = false; |
| | | this.$emit("fetch-data"); |
| | | }, |
| | | async save() { |
| | | if (this.createAction) { |
| | | this.$refs["form"].validate(async (valid) => { |
| | | if (valid) { |
| | | let rsp = await register(this.form); |
| | | if (rsp && rsp.success) { |
| | | console.log(rsp); |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | this.closeDialog(); |
| | | } |
| | | } |
| | | }); |
| | | } else { |
| | | let rsp = await doEdit(this.form); |
| | | if (rsp && rsp.success) { |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | this.closeDialog(); |
| | | } |
| | | } |
| | | }, |
| | | closeDialog() { |
| | | this.$refs["form"].resetFields(); |
| | | this.dialogFormVisible = false; |
| | | this.$emit("fetch-data"); |
| | | this.form = this.$options.data().form; |
| | | }, |
| | | handleClick(tab, event) { |
| | | // console.log(tab, event) |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <div class="table-container"> |
| | | <vab-query-form> |
| | | <vab-query-form-left-panel> |
| | | <el-form |
| | | ref="form" |
| | | :model="queryForm" |
| | | :inline="true" |
| | | @submit.native.prevent |
| | | > |
| | | <el-form-item> |
| | | <el-input |
| | | v-model="queryForm.name" |
| | | placeholder="姓名/用户名/手机号" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button |
| | | icon="el-icon-search" |
| | | type="primary" |
| | | native-type="submit" |
| | | @click="handleQuery" |
| | | >查询</el-button |
| | | > |
| | | </el-form-item> |
| | | </el-form> |
| | | </vab-query-form-left-panel> |
| | | <vab-query-form-right-panel> |
| | | <el-button icon="el-icon-plus" type="primary" @click="handleAdd"> |
| | | 添加</el-button |
| | | > |
| | | <el-button icon="el-icon-delete" type="danger" @click="handleDelete" |
| | | >删除</el-button |
| | | > |
| | | </vab-query-form-right-panel> |
| | | </vab-query-form> |
| | | |
| | | <el-table |
| | | ref="tableSort" |
| | | v-loading="listLoading" |
| | | stripe |
| | | :data="list" |
| | | :element-loading-text="elementLoadingText" |
| | | :height="height" |
| | | @selection-change="setSelectRows" |
| | | > |
| | | <!-- <el-table-column show-overflow-tooltip type="selection" width="55"></el-table-column> --> |
| | | <el-table-column show-overflow-tooltip label="序号" width="95"> |
| | | <template #default="scope">{{ scope.$index + 1 }}</template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | show-overflow-tooltip |
| | | prop="username" |
| | | label="用户名" |
| | | ></el-table-column> |
| | | <el-table-column |
| | | show-overflow-tooltip |
| | | prop="name" |
| | | label="姓名" |
| | | ></el-table-column> |
| | | <el-table-column prop="tel" label="手机号"> </el-table-column> |
| | | <el-table-column label="权限"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.permissions == 'admin' ? 'danger' : 'success'">{{ |
| | | row.permissions |
| | | }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="创建时间" |
| | | prop="createdAt" |
| | | width="200" |
| | | ></el-table-column> |
| | | <el-table-column label="操作" width="180px"> |
| | | <template #default="{ row }"> |
| | | <el-button type="text" @click="handleEdit(row)">编辑</el-button> |
| | | <el-button |
| | | type="text" |
| | | style="color: red" |
| | | @click="handleDeleteRow(row)" |
| | | >删除</el-button |
| | | > |
| | | <el-button type="text" @click="resetPassword(row)" |
| | | >重置密码</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | :background="background" |
| | | :current-page="queryForm.pageNo" |
| | | :layout="layout" |
| | | :page-size="queryForm.pageSize" |
| | | :total="total" |
| | | @current-change="handleCurrentChange" |
| | | @size-change="handleSizeChange" |
| | | ></el-pagination> |
| | | <table-edit ref="edit" @fetch-data="fetchData"></table-edit> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getUsers, deleteUser, updatePassword } from "@/api/user"; |
| | | |
| | | import TableEdit from "./components/UserEdit.vue"; |
| | | export default { |
| | | name: "ComprehensiveTable", |
| | | components: { |
| | | TableEdit, |
| | | }, |
| | | data() { |
| | | return { |
| | | list: [], |
| | | listLoading: true, |
| | | layout: "total, sizes, prev, pager, next, jumper", |
| | | |
| | | total: 0, |
| | | background: true, |
| | | selectRows: "", |
| | | elementLoadingText: "正在加载...", |
| | | queryForm: { |
| | | pageNo: 1, |
| | | pageSize: 20, |
| | | name: "", |
| | | }, |
| | | }; |
| | | }, |
| | | computed: { |
| | | height() { |
| | | return this.$baseTableHeight(); |
| | | }, |
| | | }, |
| | | mounted() { |
| | | this.fetchData(); |
| | | }, |
| | | methods: { |
| | | setSelectRows(val) { |
| | | this.selectRows = val; |
| | | }, |
| | | handleAdd() { |
| | | this.$refs["edit"].showEdit(); |
| | | }, |
| | | handleEdit(row) { |
| | | this.$refs["edit"].showEdit(row); |
| | | }, |
| | | handleDelete(row) { |
| | | if (this.selectRows.length > 0) { |
| | | const ids = this.selectRows.map((item) => item.id).join(); |
| | | this.$baseConfirm("你确定要删除选中项吗", null, async () => { |
| | | const { msg } = await doDelete({ ids: ids }); |
| | | this.$baseMessage(msg, "success"); |
| | | this.fetchData(); |
| | | }); |
| | | } else { |
| | | this.$baseMessage("未选中任何行", "error"); |
| | | return false; |
| | | } |
| | | }, |
| | | handleDeleteRow(row) { |
| | | if (row.id) { |
| | | this.$baseConfirm("你确定要删除当前项吗", null, async () => { |
| | | const { msg } = await deleteUser(row.id); |
| | | this.$baseMessage(msg, "success"); |
| | | this.fetchData(); |
| | | }); |
| | | } |
| | | }, |
| | | handleSizeChange(val) { |
| | | this.queryForm.pageSize = val; |
| | | this.fetchData(); |
| | | }, |
| | | handleCurrentChange(val) { |
| | | this.queryForm.pageNo = val; |
| | | this.fetchData(); |
| | | }, |
| | | handleQuery() { |
| | | this.queryForm.pageNo = 1; |
| | | this.fetchData(); |
| | | }, |
| | | async fetchData() { |
| | | this.listLoading = true; |
| | | this.list = []; |
| | | const { data, total } = await getUsers(this.queryForm); |
| | | |
| | | if (data) { |
| | | this.list = data; |
| | | } |
| | | |
| | | this.total = total; |
| | | setTimeout(() => { |
| | | this.listLoading = false; |
| | | }, 500); |
| | | }, |
| | | |
| | | resetPassword(row) { |
| | | const h = this.$createElement; |
| | | let desc = ""; // 这里保存输入的值 |
| | | this.$msgbox({ |
| | | // msgbox里面如果是dom元素,只能是原生的dom,如果想用elmentui,就应该手动给他添加类 |
| | | title: "重置密码", |
| | | message: h( |
| | | "div", |
| | | { |
| | | class: { |
| | | "el-input": true, |
| | | "el-input--small": true, |
| | | "el-input--suffix": true, |
| | | }, |
| | | }, |
| | | [ |
| | | h("input", { |
| | | domProps: { |
| | | type: "text", |
| | | value: desc, |
| | | placeholder: "请输入6位以上密码", |
| | | }, |
| | | class: { |
| | | "el-input__inner": true, |
| | | }, |
| | | on: { |
| | | change: function (event) { |
| | | desc = event.target.value; // 监听change事件,手动的将值给保存到desc中 |
| | | }, |
| | | }, |
| | | }), |
| | | ] |
| | | ), |
| | | showCancelButton: true, |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | inputPattern: /^[0-9A-Za-z]{6,}$/, |
| | | inputErrorMessage: "密码长度要大于6位", |
| | | beforeClose: (action, instance, done) => { |
| | | if (action === "confirm") { |
| | | instance.confirmButtonLoading = true; |
| | | instance.confirmButtonText = "提交..."; |
| | | updatePassword(row.id, { newPassword: desc }).then((rsp) => { |
| | | if (rsp && rsp.success) { |
| | | this.$baseMessage(rsp.msg, "success"); |
| | | done(); |
| | | } |
| | | |
| | | instance.confirmButtonLoading = false; |
| | | }); |
| | | } else { |
| | | done(); |
| | | } |
| | | }, |
| | | }); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |