作者 北风

调整空格同步格式

1 <template> 1 <template>
2 - <div :class="{'has-logo':showLogo}">  
3 - <logo v-if="showLogo" :collapse="isCollapse" />  
4 - <el-scrollbar wrap-class="scrollbar-wrapper">  
5 - <el-menu  
6 - :default-active="activeMenu"  
7 - :collapse="isCollapse"  
8 - :background-color="variables.menuBg"  
9 - :text-color="variables.menuText"  
10 - :unique-opened="true"  
11 - :active-text-color="settings.theme"  
12 - :collapse-transition="false"  
13 - mode="vertical"  
14 - >  
15 - <sidebar-item  
16 - v-for="route in permission_routes"  
17 - :key="route.path"  
18 - :item="route"  
19 - :base-path="route.path"  
20 - />  
21 - </el-menu>  
22 - </el-scrollbar>  
23 - </div> 2 + <div :class="{'has-logo':showLogo}">
  3 + <logo v-if="showLogo" :collapse="isCollapse" />
  4 + <el-scrollbar wrap-class="scrollbar-wrapper">
  5 + <el-menu
  6 + :default-active="activeMenu"
  7 + :collapse="isCollapse"
  8 + :background-color="variables.menuBg"
  9 + :text-color="variables.menuText"
  10 + :unique-opened="true"
  11 + :active-text-color="settings.theme"
  12 + :collapse-transition="false"
  13 + mode="vertical"
  14 + >
  15 + <sidebar-item
  16 + v-for="route in permission_routes"
  17 + :key="route.path"
  18 + :item="route"
  19 + :base-path="route.path"
  20 + />
  21 + </el-menu>
  22 + </el-scrollbar>
  23 + </div>
24 </template> 24 </template>
25 25
26 <script> 26 <script>
@@ -30,28 +30,28 @@ import SidebarItem from "./SidebarItem"; @@ -30,28 +30,28 @@ import SidebarItem from "./SidebarItem";
30 import variables from "@/assets/styles/variables.scss"; 30 import variables from "@/assets/styles/variables.scss";
31 31
32 export default { 32 export default {
33 - components: { SidebarItem, Logo },  
34 - computed: {  
35 - ...mapState(["settings"]),  
36 - ...mapGetters(["permission_routes", "sidebar"]),  
37 - activeMenu() {  
38 - const route = this.$route;  
39 - const { meta, path } = route;  
40 - // if set path, the sidebar will highlight the path you set  
41 - if (meta.activeMenu) {  
42 - return meta.activeMenu;  
43 - }  
44 - return path;  
45 - },  
46 - showLogo() {  
47 - return this.$store.state.settings.sidebarLogo;  
48 - },  
49 - variables() {  
50 - return variables;  
51 - },  
52 - isCollapse() {  
53 - return !this.sidebar.opened; 33 + components: { SidebarItem, Logo },
  34 + computed: {
  35 + ...mapState(["settings"]),
  36 + ...mapGetters(["permission_routes", "sidebar"]),
  37 + activeMenu() {
  38 + const route = this.$route;
  39 + const { meta, path } = route;
  40 + // if set path, the sidebar will highlight the path you set
  41 + if (meta.activeMenu) {
  42 + return meta.activeMenu;
  43 + }
  44 + return path;
  45 + },
  46 + showLogo() {
  47 + return this.$store.state.settings.sidebarLogo;
  48 + },
  49 + variables() {
  50 + return variables;
  51 + },
  52 + isCollapse() {
  53 + return !this.sidebar.opened;
  54 + }
54 } 55 }
55 - }  
56 }; 56 };
57 </script> 57 </script>
1 <template> 1 <template>
2 - <div id="tags-view-container" class="tags-view-container">  
3 - <scroll-pane ref="scrollPane" class="tags-view-wrapper">  
4 - <router-link  
5 - v-for="tag in visitedViews"  
6 - ref="tag"  
7 - :key="tag.path"  
8 - :class="isActive(tag)?'active':''"  
9 - :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"  
10 - tag="span"  
11 - class="tags-view-item"  
12 - :style="activeStyle(tag)"  
13 - @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"  
14 - @contextmenu.prevent.native="openMenu(tag,$event)"  
15 - >  
16 - {{ tag.title }}  
17 - <span  
18 - v-if="!isAffix(tag)"  
19 - class="el-icon-close"  
20 - @click.prevent.stop="closeSelectedTag(tag)"  
21 - />  
22 - </router-link>  
23 - </scroll-pane>  
24 - <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">  
25 - <li @click="refreshSelectedTag(selectedTag)">刷新页面</li>  
26 - <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li>  
27 - <li @click="closeOthersTags">关闭其他</li>  
28 - <li @click="closeAllTags(selectedTag)">关闭所有</li>  
29 - </ul>  
30 - </div> 2 + <div id="tags-view-container" class="tags-view-container">
  3 + <scroll-pane ref="scrollPane" class="tags-view-wrapper">
  4 + <router-link
  5 + v-for="tag in visitedViews"
  6 + ref="tag"
  7 + :key="tag.path"
  8 + :class="isActive(tag)?'active':''"
  9 + :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
  10 + tag="span"
  11 + class="tags-view-item"
  12 + :style="activeStyle(tag)"
  13 + @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
  14 + @contextmenu.prevent.native="openMenu(tag,$event)"
  15 + >
  16 + {{ tag.title }}
  17 + <span
  18 + v-if="!isAffix(tag)"
  19 + class="el-icon-close"
  20 + @click.prevent.stop="closeSelectedTag(tag)"
  21 + />
  22 + </router-link>
  23 + </scroll-pane>
  24 + <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
  25 + <li @click="refreshSelectedTag(selectedTag)">刷新页面</li>
  26 + <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li>
  27 + <li @click="closeOthersTags">关闭其他</li>
  28 + <li @click="closeAllTags(selectedTag)">关闭所有</li>
  29 + </ul>
  30 + </div>
31 </template> 31 </template>
32 32
33 <script> 33 <script>
@@ -35,274 +35,284 @@ import ScrollPane from "./ScrollPane"; @@ -35,274 +35,284 @@ import ScrollPane from "./ScrollPane";
35 import path from "path"; 35 import path from "path";
36 36
37 export default { 37 export default {
38 - components: { ScrollPane },  
39 - data() {  
40 - return {  
41 - visible: false,  
42 - top: 0,  
43 - left: 0,  
44 - selectedTag: {},  
45 - affixTags: []  
46 - };  
47 - },  
48 - computed: {  
49 - visitedViews() {  
50 - return this.$store.state.tagsView.visitedViews; 38 + components: { ScrollPane },
  39 + data() {
  40 + return {
  41 + visible: false,
  42 + top: 0,
  43 + left: 0,
  44 + selectedTag: {},
  45 + affixTags: []
  46 + };
51 }, 47 },
52 - routes() {  
53 - return this.$store.state.permission.routes;  
54 - },  
55 - theme() {  
56 - return this.$store.state.settings.theme;  
57 - }  
58 - },  
59 - watch: {  
60 - $route() {  
61 - this.addTags();  
62 - this.moveToCurrentTag();  
63 - },  
64 - visible(value) {  
65 - if (value) {  
66 - document.body.addEventListener("click", this.closeMenu);  
67 - } else {  
68 - document.body.removeEventListener("click", this.closeMenu);  
69 - }  
70 - }  
71 - },  
72 - mounted() {  
73 - this.initTags();  
74 - this.addTags();  
75 - },  
76 - methods: {  
77 - isActive(route) {  
78 - return route.path === this.$route.path;  
79 - },  
80 - activeStyle(tag) {  
81 - if (!this.isActive(tag)) return {};  
82 - return {  
83 - "background-color": this.theme,  
84 - "border-color": this.theme  
85 - };  
86 - },  
87 - isAffix(tag) {  
88 - return tag.meta && tag.meta.affix;  
89 - },  
90 - filterAffixTags(routes, basePath = "/") {  
91 - let tags = [];  
92 - routes.forEach(route => {  
93 - if (route.meta && route.meta.affix) {  
94 - const tagPath = path.resolve(basePath, route.path);  
95 - tags.push({  
96 - fullPath: tagPath,  
97 - path: tagPath,  
98 - name: route.name,  
99 - meta: { ...route.meta }  
100 - });  
101 - }  
102 - if (route.children) {  
103 - const tempTags = this.filterAffixTags(route.children, route.path);  
104 - if (tempTags.length >= 1) {  
105 - tags = [...tags, ...tempTags];  
106 - }  
107 - }  
108 - });  
109 - return tags;  
110 - },  
111 - initTags() {  
112 - const affixTags = (this.affixTags = this.filterAffixTags(this.routes));  
113 - for (const tag of affixTags) {  
114 - // Must have tag name  
115 - if (tag.name) {  
116 - this.$store.dispatch("tagsView/addVisitedView", tag); 48 + computed: {
  49 + visitedViews() {
  50 + return this.$store.state.tagsView.visitedViews;
  51 + },
  52 + routes() {
  53 + return this.$store.state.permission.routes;
  54 + },
  55 + theme() {
  56 + return this.$store.state.settings.theme;
117 } 57 }
118 - }  
119 - },  
120 - addTags() {  
121 - const { name } = this.$route;  
122 - if (name) {  
123 - this.$store.dispatch("tagsView/addView", this.$route);  
124 - }  
125 - return false;  
126 }, 58 },
127 - moveToCurrentTag() {  
128 - const tags = this.$refs.tag;  
129 - this.$nextTick(() => {  
130 - for (const tag of tags) {  
131 - if (tag.to.path === this.$route.path) {  
132 - this.$refs.scrollPane.moveToTarget(tag);  
133 - // when query is different then update  
134 - if (tag.to.fullPath !== this.$route.fullPath) {  
135 - this.$store.dispatch("tagsView/updateVisitedView", this.$route); 59 + watch: {
  60 + $route() {
  61 + this.addTags();
  62 + this.moveToCurrentTag();
  63 + },
  64 + visible(value) {
  65 + if (value) {
  66 + document.body.addEventListener("click", this.closeMenu);
  67 + } else {
  68 + document.body.removeEventListener("click", this.closeMenu);
136 } 69 }
137 - break;  
138 - }  
139 } 70 }
140 - });  
141 - },  
142 - refreshSelectedTag(view) {  
143 - this.$store.dispatch("tagsView/delCachedView", view).then(() => {  
144 - const { fullPath } = view;  
145 - this.$nextTick(() => {  
146 - this.$router.replace({  
147 - path: "/redirect" + fullPath  
148 - });  
149 - });  
150 - });  
151 }, 71 },
152 - closeSelectedTag(view) {  
153 - this.$store  
154 - .dispatch("tagsView/delView", view)  
155 - .then(({ visitedViews }) => {  
156 - if (this.isActive(view)) {  
157 - this.toLastView(visitedViews, view);  
158 - }  
159 - }); 72 + mounted() {
  73 + this.initTags();
  74 + this.addTags();
160 }, 75 },
161 - closeOthersTags() {  
162 - this.$router.push(this.selectedTag);  
163 - this.$store  
164 - .dispatch("tagsView/delOthersViews", this.selectedTag)  
165 - .then(() => {  
166 - this.moveToCurrentTag();  
167 - });  
168 - },  
169 - closeAllTags(view) {  
170 - this.$store.dispatch("tagsView/delAllViews").then(({ visitedViews }) => {  
171 - if (this.affixTags.some(tag => tag.path === view.path)) {  
172 - return;  
173 - }  
174 - this.toLastView(visitedViews, view);  
175 - });  
176 - },  
177 - toLastView(visitedViews, view) {  
178 - const latestView = visitedViews.slice(-1)[0];  
179 - if (latestView) {  
180 - this.$router.push(latestView.fullPath);  
181 - } else {  
182 - // now the default is to redirect to the home page if there is no tags-view,  
183 - // you can adjust it according to your needs.  
184 - if (view.name === "Dashboard") {  
185 - // to reload home page  
186 - this.$router.replace({ path: "/redirect" + view.fullPath });  
187 - } else {  
188 - this.$router.push("/");  
189 - }  
190 - }  
191 - },  
192 - openMenu(tag, e) {  
193 - const menuMinWidth = 105;  
194 - const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left  
195 - const offsetWidth = this.$el.offsetWidth; // container width  
196 - const maxLeft = offsetWidth - menuMinWidth; // left boundary  
197 - const left = e.clientX - offsetLeft + 15; // 15: margin right 76 + methods: {
  77 + isActive(route) {
  78 + return route.path === this.$route.path;
  79 + },
  80 + activeStyle(tag) {
  81 + if (!this.isActive(tag)) return {};
  82 + return {
  83 + "background-color": this.theme,
  84 + "border-color": this.theme
  85 + };
  86 + },
  87 + isAffix(tag) {
  88 + return tag.meta && tag.meta.affix;
  89 + },
  90 + filterAffixTags(routes, basePath = "/") {
  91 + let tags = [];
  92 + routes.forEach(route => {
  93 + if (route.meta && route.meta.affix) {
  94 + const tagPath = path.resolve(basePath, route.path);
  95 + tags.push({
  96 + fullPath: tagPath,
  97 + path: tagPath,
  98 + name: route.name,
  99 + meta: { ...route.meta }
  100 + });
  101 + }
  102 + if (route.children) {
  103 + const tempTags = this.filterAffixTags(
  104 + route.children,
  105 + route.path
  106 + );
  107 + if (tempTags.length >= 1) {
  108 + tags = [...tags, ...tempTags];
  109 + }
  110 + }
  111 + });
  112 + return tags;
  113 + },
  114 + initTags() {
  115 + const affixTags = (this.affixTags = this.filterAffixTags(
  116 + this.routes
  117 + ));
  118 + for (const tag of affixTags) {
  119 + // Must have tag name
  120 + if (tag.name) {
  121 + this.$store.dispatch("tagsView/addVisitedView", tag);
  122 + }
  123 + }
  124 + },
  125 + addTags() {
  126 + const { name } = this.$route;
  127 + if (name) {
  128 + this.$store.dispatch("tagsView/addView", this.$route);
  129 + }
  130 + return false;
  131 + },
  132 + moveToCurrentTag() {
  133 + const tags = this.$refs.tag;
  134 + this.$nextTick(() => {
  135 + for (const tag of tags) {
  136 + if (tag.to.path === this.$route.path) {
  137 + this.$refs.scrollPane.moveToTarget(tag);
  138 + // when query is different then update
  139 + if (tag.to.fullPath !== this.$route.fullPath) {
  140 + this.$store.dispatch(
  141 + "tagsView/updateVisitedView",
  142 + this.$route
  143 + );
  144 + }
  145 + break;
  146 + }
  147 + }
  148 + });
  149 + },
  150 + refreshSelectedTag(view) {
  151 + this.$store.dispatch("tagsView/delCachedView", view).then(() => {
  152 + const { fullPath } = view;
  153 + this.$nextTick(() => {
  154 + this.$router.replace({
  155 + path: "/redirect" + fullPath
  156 + });
  157 + });
  158 + });
  159 + },
  160 + closeSelectedTag(view) {
  161 + this.$store
  162 + .dispatch("tagsView/delView", view)
  163 + .then(({ visitedViews }) => {
  164 + if (this.isActive(view)) {
  165 + this.toLastView(visitedViews, view);
  166 + }
  167 + });
  168 + },
  169 + closeOthersTags() {
  170 + this.$router.push(this.selectedTag);
  171 + this.$store
  172 + .dispatch("tagsView/delOthersViews", this.selectedTag)
  173 + .then(() => {
  174 + this.moveToCurrentTag();
  175 + });
  176 + },
  177 + closeAllTags(view) {
  178 + this.$store
  179 + .dispatch("tagsView/delAllViews")
  180 + .then(({ visitedViews }) => {
  181 + if (this.affixTags.some(tag => tag.path === view.path)) {
  182 + return;
  183 + }
  184 + this.toLastView(visitedViews, view);
  185 + });
  186 + },
  187 + toLastView(visitedViews, view) {
  188 + const latestView = visitedViews.slice(-1)[0];
  189 + if (latestView) {
  190 + this.$router.push(latestView.fullPath);
  191 + } else {
  192 + // now the default is to redirect to the home page if there is no tags-view,
  193 + // you can adjust it according to your needs.
  194 + if (view.name === "Dashboard") {
  195 + // to reload home page
  196 + this.$router.replace({ path: "/redirect" + view.fullPath });
  197 + } else {
  198 + this.$router.push("/");
  199 + }
  200 + }
  201 + },
  202 + openMenu(tag, e) {
  203 + const menuMinWidth = 105;
  204 + const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
  205 + const offsetWidth = this.$el.offsetWidth; // container width
  206 + const maxLeft = offsetWidth - menuMinWidth; // left boundary
  207 + const left = e.clientX - offsetLeft + 15; // 15: margin right
198 208
199 - if (left > maxLeft) {  
200 - this.left = maxLeft;  
201 - } else {  
202 - this.left = left;  
203 - } 209 + if (left > maxLeft) {
  210 + this.left = maxLeft;
  211 + } else {
  212 + this.left = left;
  213 + }
204 214
205 - this.top = e.clientY;  
206 - this.visible = true;  
207 - this.selectedTag = tag;  
208 - },  
209 - closeMenu() {  
210 - this.visible = false; 215 + this.top = e.clientY;
  216 + this.visible = true;
  217 + this.selectedTag = tag;
  218 + },
  219 + closeMenu() {
  220 + this.visible = false;
  221 + }
211 } 222 }
212 - }  
213 }; 223 };
214 </script> 224 </script>
215 225
216 <style lang="scss" scoped> 226 <style lang="scss" scoped>
217 .tags-view-container { 227 .tags-view-container {
218 - height: 34px;  
219 - width: 100%;  
220 - background: #fff;  
221 - border-bottom: 1px solid #d8dce5;  
222 - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);  
223 - .tags-view-wrapper {  
224 - .tags-view-item {  
225 - display: inline-block;  
226 - position: relative;  
227 - cursor: pointer;  
228 - height: 26px;  
229 - line-height: 26px;  
230 - border: 1px solid #d8dce5;  
231 - color: #495060;  
232 - background: #fff;  
233 - padding: 0 8px;  
234 - font-size: 12px;  
235 - margin-left: 5px;  
236 - margin-top: 4px;  
237 - &:first-of-type {  
238 - margin-left: 15px;  
239 - }  
240 - &:last-of-type {  
241 - margin-right: 15px;  
242 - }  
243 - &.active {  
244 - background-color: #42b983;  
245 - color: #fff;  
246 - border-color: #42b983;  
247 - &::before {  
248 - content: "";  
249 - background: #fff;  
250 - display: inline-block;  
251 - width: 8px;  
252 - height: 8px;  
253 - border-radius: 50%;  
254 - position: relative;  
255 - margin-right: 2px; 228 + height: 34px;
  229 + width: 100%;
  230 + background: #fff;
  231 + border-bottom: 1px solid #d8dce5;
  232 + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
  233 + .tags-view-wrapper {
  234 + .tags-view-item {
  235 + display: inline-block;
  236 + position: relative;
  237 + cursor: pointer;
  238 + height: 26px;
  239 + line-height: 26px;
  240 + border: 1px solid #d8dce5;
  241 + color: #495060;
  242 + background: #fff;
  243 + padding: 0 8px;
  244 + font-size: 12px;
  245 + margin-left: 5px;
  246 + margin-top: 4px;
  247 + &:first-of-type {
  248 + margin-left: 15px;
  249 + }
  250 + &:last-of-type {
  251 + margin-right: 15px;
  252 + }
  253 + &.active {
  254 + background-color: #42b983;
  255 + color: #fff;
  256 + border-color: #42b983;
  257 + &::before {
  258 + content: "";
  259 + background: #fff;
  260 + display: inline-block;
  261 + width: 8px;
  262 + height: 8px;
  263 + border-radius: 50%;
  264 + position: relative;
  265 + margin-right: 2px;
  266 + }
  267 + }
256 } 268 }
257 - }  
258 } 269 }
259 - }  
260 - .contextmenu {  
261 - margin: 0;  
262 - background: #fff;  
263 - z-index: 3000;  
264 - position: absolute;  
265 - list-style-type: none;  
266 - padding: 5px 0;  
267 - border-radius: 4px;  
268 - font-size: 12px;  
269 - font-weight: 400;  
270 - color: #333;  
271 - box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);  
272 - li {  
273 - margin: 0;  
274 - padding: 7px 16px;  
275 - cursor: pointer;  
276 - &:hover {  
277 - background: #eee;  
278 - } 270 + .contextmenu {
  271 + margin: 0;
  272 + background: #fff;
  273 + z-index: 3000;
  274 + position: absolute;
  275 + list-style-type: none;
  276 + padding: 5px 0;
  277 + border-radius: 4px;
  278 + font-size: 12px;
  279 + font-weight: 400;
  280 + color: #333;
  281 + box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
  282 + li {
  283 + margin: 0;
  284 + padding: 7px 16px;
  285 + cursor: pointer;
  286 + &:hover {
  287 + background: #eee;
  288 + }
  289 + }
279 } 290 }
280 - }  
281 } 291 }
282 </style> 292 </style>
283 293
284 <style lang="scss"> 294 <style lang="scss">
285 //reset element css of el-icon-close 295 //reset element css of el-icon-close
286 .tags-view-wrapper { 296 .tags-view-wrapper {
287 - .tags-view-item {  
288 - .el-icon-close {  
289 - width: 16px;  
290 - height: 16px;  
291 - vertical-align: 2px;  
292 - border-radius: 50%;  
293 - text-align: center;  
294 - transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);  
295 - transform-origin: 100% 50%;  
296 - &:before {  
297 - transform: scale(0.6);  
298 - display: inline-block;  
299 - vertical-align: -3px;  
300 - }  
301 - &:hover {  
302 - background-color: #b4bccc;  
303 - color: #fff;  
304 - } 297 + .tags-view-item {
  298 + .el-icon-close {
  299 + width: 16px;
  300 + height: 16px;
  301 + vertical-align: 2px;
  302 + border-radius: 50%;
  303 + text-align: center;
  304 + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  305 + transform-origin: 100% 50%;
  306 + &:before {
  307 + transform: scale(0.6);
  308 + display: inline-block;
  309 + vertical-align: -3px;
  310 + }
  311 + &:hover {
  312 + background-color: #b4bccc;
  313 + color: #fff;
  314 + }
  315 + }
305 } 316 }
306 - }  
307 } 317 }
308 </style> 318 </style>