Commit 75dd27ad authored by 周远喜's avatar 周远喜

ok

parent d48c4163
import request from '@/plugins/request';
export function AccountLogin (data) {
return request({
url: '/api/login',
method: 'post',
data
});
}
export function AccountRegister (data) {
return request({
url: '/api/register',
method: 'post',
data
});
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1457px" height="651px" viewBox="0 0 1457 651" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54 (76480) - https://sketchapp.com -->
<title>bg</title>
<desc>Created with Sketch.</desc>
<g id="页面1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="body">
<rect id="矩形" fill="#E0E3F5" transform="translate(1111.405592, 620.405592) rotate(45.000000) translate(-1111.405592, -620.405592) " x="1089.90559" y="598.905592" width="43" height="43"></rect>
<rect id="矩形" fill="#DEEFF6" transform="translate(127.254834, 230.254834) rotate(40.000000) translate(-127.254834, -230.254834) " x="95.254834" y="198.254834" width="64" height="64"></rect>
<circle id="Oval-7" fill="#C9CFED" fill-rule="nonzero" opacity="0.45" cx="1286.5" cy="27.5" r="23.5"></circle>
<circle id="Oval-7" fill="#E7E6E6" fill-rule="nonzero" opacity="0.45" cx="191" cy="51" r="18"></circle>
<circle id="Oval-7" fill="#EFEFEF" fill-rule="nonzero" opacity="0.45" cx="888" cy="499" r="18"></circle>
<ellipse id="Oval-7" stroke="#E4E4F2" stroke-width="10" fill-opacity="0" fill="#FFFFFF" fill-rule="nonzero" opacity="0.45" cx="1373.5" cy="331.5" rx="78.5" ry="79.5"></ellipse>
<ellipse id="Oval-7" stroke="#CEECF8" stroke-width="10" fill-opacity="0" fill="#FFFFFF" fill-rule="nonzero" opacity="0.45" cx="235" cy="481.5" rx="44" ry="44.5"></ellipse>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Group 5 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="-1" y="0" width="49" height="10"></rect>
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="44"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Group 5 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-11" fill="#2d8cf0" mask="url(#mask-3)" x="-1" y="0" width="49" height="10"></rect>
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="44"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Group 5 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="44"></rect>
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="49" height="10"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1559281361864" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2715" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#FBD971" p-id="2716"></path><path d="M512 877.714286c-161.328762 0-292.571429-131.242667-292.571429-292.571429a24.380952 24.380952 0 0 1 48.761905 0c0 134.436571 109.372952 243.809524 243.809524 243.809524s243.809524-109.372952 243.809524-243.809524a24.380952 24.380952 0 0 1 48.761905 0c0 161.328762-131.242667 292.571429-292.571429 292.571429z" fill="#F0C419" p-id="2717"></path><path d="M390.095238 414.47619a24.380952 24.380952 0 0 1-24.380952-24.380952c0-40.326095-32.816762-73.142857-73.142857-73.142857s-73.142857 32.816762-73.142858 73.142857a24.380952 24.380952 0 0 1-48.761904 0c0-67.218286 54.686476-121.904762 121.904762-121.904762s121.904762 54.686476 121.904761 121.904762a24.380952 24.380952 0 0 1-24.380952 24.380952zM828.952381 414.47619a24.380952 24.380952 0 0 1-24.380952-24.380952c0-40.326095-32.816762-73.142857-73.142858-73.142857s-73.142857 32.816762-73.142857 73.142857a24.380952 24.380952 0 0 1-48.761904 0c0-67.218286 54.686476-121.904762 121.904761-121.904762s121.904762 54.686476 121.904762 121.904762a24.380952 24.380952 0 0 1-24.380952 24.380952z" fill="#F29C1F" p-id="2718"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1559281374840" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3076" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#FBD971" p-id="3077"></path><path d="M780.190476 877.714286a24.380952 24.380952 0 0 1-24.380952-24.380953c0-134.436571-109.372952-243.809524-243.809524-243.809523s-243.809524 109.372952-243.809524 243.809523a24.380952 24.380952 0 1 1-48.761905 0c0-161.328762 131.242667-292.571429 292.571429-292.571428s292.571429 131.242667 292.571429 292.571428a24.380952 24.380952 0 0 1-24.380953 24.380953z" fill="#F0C419" p-id="3078"></path><path d="M390.095238 414.47619a24.380952 24.380952 0 0 1-24.380952-24.380952c0-40.326095-32.816762-73.142857-73.142857-73.142857s-73.142857 32.816762-73.142858 73.142857a24.380952 24.380952 0 1 1-48.761904 0c0-67.218286 54.686476-121.904762 121.904762-121.904762s121.904762 54.686476 121.904761 121.904762a24.380952 24.380952 0 0 1-24.380952 24.380952zM828.952381 414.47619a24.380952 24.380952 0 0 1-24.380952-24.380952c0-40.326095-32.816762-73.142857-73.142858-73.142857s-73.142857 32.816762-73.142857 73.142857a24.380952 24.380952 0 1 1-48.761904 0c0-67.218286 54.686476-121.904762 121.904761-121.904762s121.904762 54.686476 121.904762 121.904762a24.380952 24.380952 0 0 1-24.380952 24.380952z" fill="#F29C1F" p-id="3079"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543867554" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16276" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M253.44 577.536c2.56 18.432 5.12 36.352 7.68 55.296 15.36-11.776 30.208-23.04 45.056-34.304-1.536-2.56-43.008-18.944-52.736-20.992zM610.304 577.536c2.56 18.432 5.12 36.352 7.68 55.296 15.36-11.776 30.208-23.04 45.056-34.304-2.048-2.56-43.008-18.944-52.736-20.992z" fill="#DD6DA6" p-id="16277"></path><path d="M512 0C229.376 0 0 229.376 0 512s229.376 512 512 512 512-229.376 512-512S794.624 0 512 0z m-29.184 444.928h16.896c3.072 0 4.096 1.024 4.096 4.608v53.76c-6.656-0.512-13.824-0.512-20.992-1.024V444.928z m-115.712-6.656c2.048 0 3.072 1.024 3.584 3.584 1.536 17.92 3.584 35.328 5.12 53.76-6.656 1.024-13.312 1.024-20.48 1.536-2.56-19.456-5.12-38.912-7.168-58.368 6.144-0.512 12.8-0.512 18.944-0.512z m-27.648 3.072c2.56-0.512 2.048 2.048 2.56 3.584 0.512 6.656 1.536 12.8 2.048 19.456 1.024 10.24 2.56 19.968 3.584 30.208v4.096l-15.872 2.56c-3.584-18.944-6.656-37.376-10.24-56.832 5.632-1.024 11.776-2.56 17.92-3.072z m-3.072 184.32c-2.56 8.192-8.192 14.848-15.36 20.48-16.896 13.312-34.304 25.088-54.784 31.744-14.336 5.12-29.184 8.192-44.032 10.752-19.456 3.072-39.424 4.096-59.392 6.144-4.096 0.512-8.192 0-12.288 0-2.048 0-2.56-1.024-2.56-3.072 0-8.704-1.024-17.92-1.536-27.136-1.024-11.776-2.048-23.04-3.584-34.816-2.048-16.384-4.096-32.256-5.632-48.64-2.048-16.896-4.096-34.304-6.144-51.2-2.048-16.896-3.584-33.28-5.632-49.664-2.56-18.944-5.12-37.376-8.192-56.832-3.072-22.016-7.168-44.544-12.8-66.56-0.512-1.024 0.512-3.072 1.024-3.584 19.456-7.168 38.4-14.848 57.856-23.04 6.144-2.56 6.656-3.584 6.656 4.608 0 34.304 0 68.608 0.512 103.424 0.512 17.92 1.024 35.84 2.56 53.76 1.536 21.504 4.096 43.008 5.632 65.024 0 1.024 0.512 1.536 0.512 2.56 7.168-0.512 14.848-1.024 22.016-1.024 30.72-1.024 60.416 3.584 89.088 14.848 13.824 5.632 27.648 12.288 39.936 20.992 9.216 7.68 11.776 17.408 6.144 31.232z m26.112 44.544c-10.24-50.688-19.968-100.352-29.696-151.04 8.192-1.024 15.872-2.56 23.552-3.584 5.632-1.024 11.264-1.024 16.896-1.536 3.072-0.512 5.12 1.024 5.632 4.096 1.024 7.68 2.56 15.36 3.584 23.552 1.536 13.312 3.072 26.624 4.096 39.936 1.536 11.776 2.56 23.04 4.096 34.816 1.536 12.288 3.072 24.576 4.608 37.376 0.512 3.584 1.024 7.168 1.536 11.264-11.776 2.048-22.528 3.584-34.304 5.12zM445.44 655.36c-10.24-1.024-19.968-1.536-30.72-2.56-9.216-92.672-16.896-185.856-34.304-279.04 5.632-0.512 11.264-1.536 17.408-2.048 6.656-0.512 13.312-1.024 20.48-1.536 5.632-0.512 7.68 1.024 8.192 6.656l3.072 44.032c1.536 19.968 3.072 39.936 4.096 59.392 1.024 14.336 1.536 28.672 3.072 43.008 1.536 15.872 3.072 31.232 4.096 47.104 1.536 11.264 2.56 23.04 3.584 34.304 1.536 12.288 2.56 24.576 3.584 37.376l1.024 10.752c-0.512 2.56-1.536 3.072-3.584 2.56z m27.136-210.944c2.56 0 3.584 0.512 3.584 3.584-0.512 7.68 0 15.872 0 23.552v29.696c-5.632 0.512-11.264 1.024-16.384 1.536-1.536-18.944-3.072-37.888-4.608-58.368h17.408z m2.56 228.864c-1.024 0-2.56-1.536-2.56-2.56l-4.608-50.688c-1.536-15.36-3.072-31.744-4.096-47.104-1.536-15.872-3.072-32.256-4.096-48.64 0-1.024 0-2.048-0.512-3.584 4.096-1.024 7.168-1.536 11.264-1.536 10.24 0 20.48 0 30.72 0.512 2.56 0 4.096 2.048 4.096 4.096 0 5.12 0.512 10.24 0.512 15.36 0 23.552-0.512 48.128 0 71.68 0.512 19.456 1.024 37.888 1.536 57.344 0 1.024 0 2.56 0.512 4.608-11.264 0.512-22.016 1.024-32.768 0.512zM896 444.928h16.896c3.072 0 4.096 1.024 4.096 4.608v53.76c-6.656-0.512-13.824-0.512-20.992-1.024V444.928z m-116.224-6.656c2.048 0 3.072 1.024 3.584 3.584 1.536 17.92 3.584 35.328 5.12 53.76-6.656 1.024-13.312 1.024-20.48 1.536-2.56-19.456-5.12-38.912-7.168-58.368 6.656-0.512 12.8-0.512 18.944-0.512z m-27.648 3.072c2.56-0.512 2.048 2.048 2.56 3.584 0.512 6.656 1.536 12.8 2.048 19.456 1.024 10.24 2.56 19.968 3.584 30.208v4.096l-15.872 2.56c-3.584-18.944-6.656-37.376-10.24-56.832 5.632-1.024 11.776-2.56 17.92-3.072z m-3.072 183.808c-2.56 8.192-8.192 14.848-15.36 20.48-16.896 13.312-34.304 25.088-54.784 31.744-14.336 5.12-29.184 8.192-44.032 10.752-19.456 3.072-39.424 4.096-59.392 6.144-4.096 0.512-8.192 0-12.288 0-2.048 0-2.56-1.024-2.56-3.072 0-8.704-1.024-17.92-1.536-27.136-1.024-11.776-2.048-23.04-3.584-34.816-2.048-16.384-4.096-32.256-5.632-48.64-2.048-16.896-4.096-34.304-6.144-51.2-2.048-16.896-3.584-33.28-5.632-49.664-2.56-18.944-5.12-37.376-8.192-56.832-3.072-22.016-7.168-44.544-12.8-66.56 0-1.024 1.024-3.072 1.536-3.584 19.456-7.168 38.4-14.848 57.856-23.04 6.144-2.56 6.656-3.584 6.656 4.608 0 34.304 0 68.608 0.512 103.424 0.512 17.92 1.024 35.84 2.56 53.76 1.536 21.504 4.096 43.008 5.632 65.024 0 1.024 0.512 1.536 0.512 2.56 7.168-0.512 14.848-1.024 22.016-1.024 30.72-1.024 60.416 3.584 89.088 14.848 13.824 5.632 27.648 12.288 39.936 20.992 9.216 8.192 11.264 17.92 5.632 31.232z m26.112 45.056c-10.24-50.688-19.968-100.352-29.696-151.04 8.192-1.024 15.872-2.56 23.552-3.584 5.632-1.024 11.264-1.024 16.896-1.536 3.072-0.512 5.12 1.024 5.632 4.096 1.024 7.68 2.56 15.36 3.584 23.552 1.536 13.312 3.072 26.624 4.096 39.936 1.536 11.776 2.56 23.04 4.096 34.816 1.536 12.288 3.072 24.576 4.608 37.376 0.512 3.584 1.024 7.168 1.536 11.264-11.264 2.048-22.528 3.584-34.304 5.12z m83.456-14.848c-10.24-1.024-19.968-1.536-30.72-2.56-9.216-92.672-16.896-185.856-34.304-279.04 5.632-0.512 11.264-1.536 17.408-2.048 6.656-0.512 13.312-1.024 20.48-1.536 5.632-0.512 7.68 1.024 8.192 6.656l3.072 44.032c1.536 19.968 3.072 39.936 4.096 59.392 1.024 14.336 1.536 28.672 3.072 43.008 1.536 15.872 3.072 31.232 4.096 47.104 1.536 11.264 2.56 23.04 3.584 34.304 1.536 12.288 2.56 24.576 3.584 37.376l1.024 10.752c-1.024 2.56-1.536 3.072-3.584 2.56z m26.624-210.944c2.56 0 3.584 0.512 3.584 3.584-0.512 7.68 0 15.872 0 23.552v29.696c-5.632 0.512-11.264 1.024-16.384 1.536-1.536-18.944-3.072-37.888-4.608-58.368h17.408z m3.072 228.864c-1.024 0-2.56-1.536-2.56-2.56l-4.608-50.688c-1.536-15.36-3.072-31.744-4.096-47.104-1.536-15.872-3.072-32.256-4.096-48.64 0-1.024 0-2.048-0.512-3.584 4.096-1.024 7.168-1.536 11.264-1.536 10.24 0 20.48 0 30.72 0.512 2.56 0 4.096 2.048 4.096 4.096 0 5.12 0.512 10.24 0.512 15.36 0 23.552-0.512 48.128 0 71.68 0.512 19.456 1.024 37.888 1.536 57.344 0 1.024 0 2.56 0.512 4.608-11.776 0.512-22.528 1.024-32.768 0.512z" fill="#DD6DA6" p-id="16278"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543870835" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16387" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 1024C228.894118 1024 0 795.105882 0 512S228.894118 0 512 0s512 228.894118 512 512-228.894118 512-512 512" fill="#3296FA" p-id="16388"></path><path d="M733.866667 381.490196c-19.07451-8.031373-158.619608-58.227451-213.835294-79.309804C404.580392 261.019608 272.062745 197.772549 250.980392 187.733333c-3.011765-2.007843-7.027451-1.003922-10.039216 1.003922-3.011765 2.007843-3.011765 4.015686-4.015686 6.023529-7.027451 45.176471 12.047059 122.478431 49.192157 155.607843 18.070588 16.062745 253.992157 89.34902 253.992157 89.34902S291.137255 383.498039 286.117647 383.498039c-4.015686 0-6.023529 0-9.035294 3.011765-2.007843 2.007843-3.011765 5.019608-3.011765 9.035294 9.035294 50.196078 56.219608 115.45098 98.384314 123.482353 34.133333 7.027451 170.666667 2.007843 170.666667 2.007843s-165.647059 23.090196-169.662745 24.094118c-4.015686 1.003922-6.023529 3.011765-7.027451 5.019608-1.003922 2.007843-2.007843 5.019608 0 9.035294 7.027451 15.058824 29.113725 37.145098 29.113725 37.145098 45.176471 46.180392 94.368627 40.156863 106.415686 38.149019 12.047059-2.007843 44.172549-12.047059 59.231373-20.078431l-27.105882 99.388235 82.321568 1.003922-49.192157 157.615686 178.698039-225.882353h-87.341176S740.894118 510.996078 765.992157 461.803922c7.027451-13.05098 10.039216-32.12549 5.019608-46.180393-6.023529-14.054902-16.062745-25.098039-37.145098-34.133333" fill="#FFFFFF" p-id="16389"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543877388" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16609" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512.2 0.6C229.6 0.6 0.5 229.7 0.5 512.3S229.6 1024 512.2 1024s511.7-229.1 511.7-511.7S794.8 0.6 512.2 0.6z m132.7 341h-48.2c-17.6 0-29.5 3.7-35.6 11.1-6.1 7.4-9.2 18.5-9.2 33.2v58h89.9l-12 90.9h-78v233h-94v-233h-78.3v-90.9h78.3V377c0-38.1 10.6-67.6 31.9-88.6s49.6-31.5 85-31.5h0.2c30.1 0 53.4 1.2 70 3.7v81z" fill="#3162A2" p-id="16610"></path><path d="M551.7 534.8h0.1v233h-0.1zM561 352.7c-6.1 7.4-9.2 18.4-9.2 33.2v58h89.9l-12 90.9h0.1l12-90.9h-89.9v-58c0-14.7 3.1-25.8 9.1-33.2 6.2-7.4 18.1-11.1 35.7-11.1h-0.1c-17.6 0-29.5 3.7-35.6 11.1zM574.9 256.9c30 0 53.3 1.2 69.8 3.7v81h0.2v-81c-16.6-2.5-39.9-3.7-70-3.7z" fill="#FFFFFF" p-id="16611"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543883733" class="icon" style="" viewBox="0 0 1272 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16831" xmlns:xlink="http://www.w3.org/1999/xlink" width="496.875" height="400"><defs><style type="text/css"></style></defs><path d="M729.64116345 165.27693991L634.32650881 90.125l-99.5625 78.52693991-5.17887981 4.16056009 104.74137981 83.50215546 105.09051682-83.50215546-9.77586218-7.53556009z m361.21228445 291.47198236l-456.78879245 360.19396555-456.49784537-359.99030128L110.125 511.12715547l523.93965546 413.11745671 524.23060335-413.35021555-67.44181091-54.14547436z m-456.78879245 29.21120673L385.4784479 290.00646554 318.06573237 344.12284454l315.96982771 249.16810336 316.28987101-249.40086136-67.41271555-54.14547436-248.84806008 196.21551682z" fill="#006cff" p-id="16832"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543886845" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16941" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#18ACFC" p-id="16942"></path><path d="M500.113 228.39c118.396-1.518 178.924 61.004 201 156 3.497 15.048 0.15 34.807 0 50 27.142999999999997 5.682 33.087 60.106 10 75v1h1c8.26 14.329999999999998 19.04 28.125 26 44 7.332 16.723 9.305999999999997 35.16 14 55 4.024 17.01-2.287 51.505-10 57-0.7709999999999998 0.683-2.231 1.312-3 2-14.601-3.016-30.377-16.865-38-27-3.065-4.074-5.275-9.672-10-12-0.395 21.568-12.503 41.15-22 55-3.5139999999999993 5.123-14.073 13.216999999999999-14 18 3.690999999999999 2.836 8.305 2.955999999999999 13 5 10.513 4.577 25.449 13.168 32 22 2.333999999999999 3.146 5.548 7.554999999999999 7 11 16.193 38.414-36.527 48.314-63 54-27.185 5.839-77.818-10.224-92-19-8.749-5.414-16.863-18.573-29-19-3.666 2.3889999999999993-14.438 1.1319999999999997-20 1-16.829 32.804-101.913 47.867999999999995-148 31-14.061-5.146-43.398-17.695-38-40 4.437-18.327 19.947-29.224 35-37 5.759-2.975 18.915-4.419 22-10-13.141-8.988-24.521-28.659-31-44-3.412-8.077-4.193-25.775-9-32-7.789 12.245-32.097 36.91-52 33-3.071-4.553-7.213-9.097-9-15-4.792-15.835-1.81-40.379 2-54 8.117-29.02 16.965-50.62299999999998 32-72 4.672-6.643 11.425-12.135 16-19-8.945-9.733-6.951-37.535999999999994-1-49 4.002-7.709 9.701-7.413 10-20-1.9199999999999997-3.022-0.071-8.604-1-13-4.383-20.75 3.2729999999999997-47.552 9-63 19.8-53.420999999999985 53.712-90.466 105-112 11.986-5.033 25.833-7.783 39-11 5.322-1.3 11.969 0.518 16-2z" fill="#FFFFFF" p-id="16943"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543874130" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16498" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M511.8 0.6C229.2 0.6 0.1 229.7 0.1 512.3S229.2 1024 511.8 1024s511.7-229.1 511.7-511.7S794.4 0.6 511.8 0.6z m264.9 375.3c-0.1 0.1-0.1 0.2-0.2 0.3h0.2c-9 14.3-21.2 28.8-34.2 39.2-5.2 4.2-10.5 8.3-15.7 12.5v0.5c0.3 22.9-0.3 44.9-4.7 64.2-25.2 113.1-91.9 189.9-197.4 222.8-37.9 11.8-99.1 16.7-142.6 5.9-21.5-5.4-41-11.4-59.3-19.3-10.2-4.5-19.6-9.3-28.5-14.7l-8.8-5.3h0.5l-0.5-0.3c9.8 0.2 21.3 2.9 32.2 1.2 9.8-1.6 19.6-1.2 28.7-3.2 22.7-5 43-11.6 60.4-21.8 8.3-4.8 20.9-10.6 27-17.6-11.2 0.2-21.4-2.4-29.7-5.4-32.6-11.5-51.6-32.7-63.9-64.4h0.1c0-0.1-0.1-0.2-0.1-0.3 9.7 1.1 37.3 3.5 44.6-1.7-12.3-0.8-24.1-7.9-32.5-13.2-26.2-16.4-47.6-43.9-47.4-86.2v-0.3c3.5 1.7 6.9 3.3 10.4 4.9 6.6 2.8 13.3 4.3 21.1 5.9 3.1 0.7 9.3 2.4 13.2 1.4-5.1-5.8-13.2-9.7-18.4-16-14.7-18.3-28.7-45.3-25.2-77.4 0.5-4.7 1.3-9.4 2.6-14.3 2.5-9.7 6.5-18.3 10.8-26.2 0.2 0.1 0.4 0.2 0.5 0.3 2 4.2 6.4 7.2 9.1 10.6 8.6 10.7 19.2 20.3 30 28.7 36.7 28.7 69.9 46.4 123.1 59.5 13.5 3.3 29 5.9 45.1 5.9-1.9-5.8-2.7-13-2.7-20.5 0-9.7 1.3-19.6 3.3-26.8 9-32.1 28.5-55.2 57-67.6 6.9-3 14.4-5.2 22.4-6.9 4.1-0.6 8.2-1.1 12.3-1.6 39-0.7 59.8 13.5 79.6 31.6 16.8-1.4 38.7-10.8 51.6-17.4l12.6-6.9c0 0.1-0.1 0.3-0.1 0.4l0.1-0.1c-7.3 19.9-17.4 35.5-32.7 47.3-3.1 2.4-6.3 5.6-10 7.4 21.5-0.4 39.3-10 56.1-15.4v0.3z" fill="#2EB1EB" p-id="16499"></path><path d="M719.7 391.1s0.1 0 0.1-0.1c0 0-0.1 0-0.1 0.1zM726.8 428.4v-0.5 0.5zM336.4 479.9c3.3 0.7 9.9 2.7 13.8 1.2h-0.5l-0.1-0.1c-3.9 1-10.1-0.7-13.2-1.4-7.8-1.6-14.5-3.1-21.1-5.9-3.5-1.6-6.9-3.2-10.4-4.9v0.3c3.4 1.6 6.9 3.2 10.4 4.9 6.6 2.8 13.3 4.3 21.1 5.9zM719.6 391.4v0.2c21.9-0.2 39.8-10.1 56.9-15.4 0.1-0.1 0.1-0.2 0.2-0.3v-0.3c-16.9 5.3-34.6 14.9-56.1 15.4-0.4 0.1-0.7 0.3-1 0.4zM584.8 337.5c6.9-3 14.4-5.1 22.4-6.9 4.1-0.6 8.2-1 12.3-1.6 39-0.7 59.8 13.5 79.6 31.6 16.8-1.4 38.7-10.8 51.6-17.4 4.2-2.3 8.3-4.5 12.5-6.8 0-0.1 0.1-0.3 0.1-0.4l-12.6 6.9c-12.9 6.6-34.8 16-51.6 17.4-19.8-18.1-40.6-32.3-79.6-31.6-4.1 0.5-8.2 1-12.3 1.6-8 1.7-15.5 3.9-22.4 6.9-28.5 12.4-48 35.5-57 67.6-2 7.2-3.4 17.2-3.3 26.8 0-9.6 1.3-19.4 3.3-26.5 9-32.1 28.5-55.2 57-67.6zM385.2 568.5h-0.4c-7.3 5.2-34.9 2.8-44.6 1.7 0 0.1 0.1 0.2 0.1 0.3 10 1.1 38.2 3.6 44.9-2zM319.4 347.4c0.1 0.1 0.3 0.1 0.5 0.3 2 4.1 6.3 7.1 9.1 10.6 8.6 10.6 19.2 20.2 30 28.7 36.8 28.7 69.9 46.4 123.1 59.5 13.5 3.3 29.1 5.9 45.2 5.9 0-0.1-0.1-0.2-0.1-0.3-16.1 0-31.6-2.6-45.1-5.9-53.2-13.1-86.4-30.8-123.1-59.5-10.8-8.4-21.4-18-30-28.7-2.7-3.4-7.1-6.4-9.1-10.6-0.1-0.1-0.3-0.2-0.5-0.3-4.3 7.9-8.3 16.5-10.8 26.2-1.3 4.9-2.1 9.6-2.6 14.3 0.5-4.6 1.3-9.2 2.6-14 2.5-9.7 6.5-18.3 10.8-26.2zM317.7 683.2c9.9-1.6 19.6-1.2 28.7-3.2 22.8-5 43-11.6 60.4-21.8 8.4-4.9 21.3-10.8 27.3-17.9h-0.3c-6.1 7-18.7 12.8-27 17.6-17.4 10.2-37.7 16.8-60.4 21.8-9.1 2-18.9 1.6-28.7 3.2-10.9 1.7-22.4-1-32.2-1.2l0.5 0.3c9.7 0.4 21 2.9 31.7 1.2z" fill="#FFFFFF" p-id="16500"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543880869" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16720" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 1024C229.228089 1024 0 794.771911 0 512S229.228089 0 512 0s512 229.228089 512 512-229.228089 512-512 512z m-87.813689-750.933333C314.1632 273.066667 221.866667 351.744 221.866667 444.7232c0 57.230222 31.9488 103.708444 78.085689 135.896178h3.549866l-21.2992 60.802844 74.5472-39.344355h3.549867c21.2992 10.729244 42.587022 14.301867 63.886222 14.301866h14.199467c-3.549867-14.301867-7.099733-28.603733-7.099734-42.9056 0-85.833956 81.635556-157.354667 184.570312-157.354666h7.099733C608.756622 337.442133 527.132444 273.066667 424.186311 273.066667z m370.676622 306.722133c0-78.654578-78.791111-143.018667-171.906844-143.018667-96.688356 0-171.895467 64.364089-171.895467 143.018667s78.791111 143.018667 171.895467 143.018667c17.908622 0 35.817244-3.584 53.725867-7.156623h3.572622l60.882489 32.176356-17.908623-53.623467c46.557867-28.603733 71.634489-67.925333 71.634489-114.414933z" fill="#5FC948" p-id="16721"></path><path d="M358.4 420.977778a28.444444 28.444444 0 1 1 0-56.888889 28.444444 28.444444 0 0 1 0 56.888889z m204.8 136.533333a28.444444 28.444444 0 1 1 0-56.888889 28.444444 28.444444 0 0 1 0 56.888889z m-68.266667-136.533333a28.444444 28.444444 0 1 1 0-56.888889 28.444444 28.444444 0 0 1 0 56.888889z m193.422223 136.533333a28.444444 28.444444 0 1 1 0-56.888889 28.444444 28.444444 0 0 1 0 56.888889z" fill="#5FC948" p-id="16722"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543890412" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17052" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#F1DF55" p-id="17053"></path><path d="M455.876923 589.784615c-6.892308-2.953846-15.753846 0.984615-19.692308 7.876923-3.938462 6.892308-1.969231 14.769231 4.923077 17.723077 6.892308 2.953846 16.738462 0 20.676923-7.876923 2.953846-6.892308 0.984615-14.769231-5.907692-17.723077zM410.584615 608.492308c-18.707692-7.876923-42.338462 0-53.16923 17.723077-11.815385 17.723077-5.907692 39.384615 11.815384 47.261538 18.707692 8.861538 43.323077 0 54.153846-17.723077 10.830769-17.723077 4.923077-39.384615-12.8-47.261538z" fill="#FFFFFF" p-id="17054"></path><path d="M426.338462 466.707692C307.2 478.523077 217.6 551.384615 225.476923 629.169231c7.876923 77.784615 110.276923 131.938462 229.415385 120.123077 119.138462-11.815385 208.738462-84.676923 200.861538-162.461539-7.876923-78.769231-110.276923-131.938462-229.415384-120.123077zM530.707692 649.846154c-24.615385 55.138462-93.538462 83.692308-152.615384 64.984615-57.107692-18.707692-81.723077-74.830769-56.123077-126.030769 24.615385-50.215385 88.615385-77.784615 144.738461-63.015385 58.092308 15.753846 88.615385 70.892308 64 124.061539z" fill="#FFFFFF" p-id="17055"></path><path d="M692.184615 490.338462c-9.846154-2.953846-16.738462-4.923077-11.815384-18.707693 11.815385-28.553846 12.8-54.153846 0-71.876923-23.630769-33.476923-86.646154-31.507692-160.492308-0.984615 0 0-22.646154 9.846154-16.738461-7.876923 10.830769-36.430769 9.846154-65.969231-7.876924-83.692308-39.384615-39.384615-144.738462 1.969231-235.323076 91.569231-66.953846 67.938462-106.338462 139.815385-106.338462 201.846154 0 118.153846 151.630769 191.015385 301.292308 191.015384 194.953846 0 324.923077-113.230769 324.923077-202.830769-0.984615-55.138462-46.276923-85.661538-87.63077-98.461538zM454.892308 748.307692c-119.138462 11.815385-221.538462-42.338462-229.415385-120.123077-7.876923-77.784615 82.707692-150.646154 200.861539-162.461538 119.138462-11.815385 221.538462 42.338462 229.415384 120.123077 7.876923 78.769231-82.707692 151.630769-200.861538 162.461538z" fill="#D52B2A" p-id="17056"></path><path d="M822.153846 272.738462c-47.261538-52.184615-116.184615-71.876923-181.169231-58.092308-14.769231 2.953846-24.615385 17.723077-20.676923 32.492308 2.953846 14.769231 17.723077 24.615385 32.492308 20.676923 45.292308-9.846154 94.523077 4.923077 128 41.353846 33.476923 37.415385 42.338462 87.630769 28.553846 131.938461-4.923077 14.769231 2.953846 29.538462 17.723077 34.461539s29.538462-2.953846 34.461539-17.723077c19.692308-62.030769 6.892308-132.923077-39.384616-185.107692z" fill="#E89214" p-id="17057"></path><path d="M738.461538 444.061538c12.8 3.938462 25.6-2.953846 29.538462-14.76923 9.846154-30.523077 3.938462-64.984615-19.692308-90.584616-22.646154-25.6-57.107692-35.446154-87.630769-28.553846-12.8 2.953846-20.676923 15.753846-17.723077 28.553846 2.953846 12.8 15.753846 20.676923 27.569231 17.723077 15.753846-2.953846 31.507692 1.969231 43.323077 13.784616 10.830769 12.8 13.784615 29.538462 9.846154 44.307692-3.938462 11.815385 2.953846 25.6 14.76923 29.538461z" fill="#E89214" p-id="17058"></path><path d="M466.707692 526.769231c-56.123077-14.769231-120.123077 13.784615-144.738461 63.015384-24.615385 51.2-0.984615 107.323077 56.123077 126.03077 59.076923 18.707692 128.984615-9.846154 152.615384-64.984616 24.615385-54.153846-5.907692-109.292308-64-124.061538zM423.384615 655.753846c-11.815385 18.707692-36.430769 26.584615-54.153846 17.723077-18.707692-7.876923-23.630769-29.538462-11.815384-47.261538 11.815385-17.723077 35.446154-25.6 53.16923-17.723077 17.723077 7.876923 23.630769 29.538462 12.8 47.261538z m37.415385-48.246154c-3.938462 6.892308-13.784615 10.830769-20.676923 7.876923-6.892308-2.953846-8.861538-10.830769-4.923077-17.723077s12.8-9.846154 19.692308-7.876923c7.876923 2.953846 9.846154 10.830769 5.907692 17.723077z" fill="#040000" p-id="17059"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543863835" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16165" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 1024C229.236364 1024 0 794.763636 0 512S229.236364 0 512 0s512 229.236364 512 512-229.236364 512-512 512z m-129.861818-756.48s-36.212364 2.094545-48.989091 24.482909c-12.8 22.365091-54.318545 137.378909-54.318546 137.378909s13.847273 6.376727 37.28291-10.658909c23.435636-17.035636 30.882909-46.848 30.882909-46.848l42.589091-2.117818 1.070545 121.390545s-73.495273-1.070545-88.413091 0c-14.894545 1.047273-23.412364 40.448-23.412364 40.448h111.825455s-9.588364 67.095273-38.353455 116.084364c-28.741818 48.989091-83.060364 87.319273-83.060363 87.319273s39.424 15.965091 77.730909-6.4c38.353455-22.341818 66.629818-120.692364 66.629818-120.692364l89.925818 110.056727s8.192-52.386909-1.466182-67.188363c-9.658182-14.778182-62.208-74.286545-62.208-74.286546l-22.946909 20.247273 16.337455-65.117091h97.954909s0-38.353455-19.153455-40.494545c-19.176727-2.094545-78.801455 0-78.801454 0V371.898182h88.389818s-1.070545-39.400727-18.106182-39.400727h-143.755636l22.341818-64.954182z m169.984 61.184v358.562909h36.002909l13.102545 45.009455 63.348364-45.009455h89.064727V328.704h-201.518545z" fill="#0f84fd" p-id="16166"></path><path d="M594.781091 368.64h117.899636v277.876364h-41.890909l-53.364363 40.261818-11.636364-40.261818h-11.008V368.64z" fill="#0f84fd" p-id="16167"></path></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Group 5 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49" height="10"></rect>
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="44"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Group 5</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="44"></rect>
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49" height="10"></rect>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<Breadcrumb class="i-layout-header-breadcrumb" v-if="!isLimit" ref="breadcrumb">
<BreadcrumbItem>
<i-menu-head-title :item="topItem" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem v-for="item in items" :key="item.path">
<i-menu-head-title :item="item" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem>
<i-menu-head-title :item="siderMenuObject[activePath]" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
</Breadcrumb>
</template>
<script>
import { mapState } from 'vuex';
import menuSider from '@/menu/sider';
import { flattenSiderMenu } from '@/libs/system';
import iMenuHeadTitle from '../menu-head/title';
import { on, off } from 'view-design/src/utils/dom';
import { findComponentUpward, getStyle } from 'view-design/src/utils/assist';
import { throttle } from 'lodash';
export default {
name: 'iHeaderBreadcrumb',
components: { iMenuHeadTitle },
computed: {
...mapState('admin/layout', [
'showBreadcrumbIcon',
'menuCollapse'
]),
...mapState('admin/menu', [
'openNames',
'activePath',
'header',
'headerName'
]),
siderMenuObject () {
let obj = {};
this.allSiderMenu.forEach(item => {
if ('path' in item) {
obj[item.path] = item;
}
});
return obj;
},
items () {
let items = [...this.openNames];
let newItems = [];
items.forEach(i => {
newItems.push(this.siderMenuObject[i]);
});
return newItems;
},
// 第一级,默认是 menu/header.js 中的第一项
topItem () {
return this.header.find(item => item.name === this.headerName);
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标及显示对应内容
allSiderMenu: flattenSiderMenu(menuSider, []),
handleResize: () => {},
isLimit: false,
maxWidth: 560,
breadcrumbWidth: 0
}
},
methods: {
handleCheckWidth () {
const $header = findComponentUpward(this, 'Header');
if ($header) {
const headerWidth = parseInt(getStyle($header.$el, 'width'));
this.$nextTick(() => {
this.isLimit = headerWidth - this.maxWidth <= this.breadcrumbWidth;
});
}
},
handleGetWidth () {
this.isLimit = false;
this.$nextTick(() => {
const $breadcrumb = this.$refs.breadcrumb;
if ($breadcrumb) {
this.breadcrumbWidth = parseInt(getStyle($breadcrumb.$el, 'width'));
}
});
}
},
watch: {
topItem: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
items: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
activePath: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
}
},
mounted () {
this.handleResize = throttle(this.handleCheckWidth, 100, { leading: false });
on(window, 'resize', this.handleResize);
this.handleGetWidth();
this.handleCheckWidth();
},
beforeDestroy () {
off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showReload }" @click="handleToggleMenuSide">
<Icon custom="i-icon i-icon-menu-unfold" v-show="menuCollapse || isMobile" />
<Icon custom="i-icon i-icon-menu-fold" v-show="!menuCollapse && !isMobile" />
</span>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'iHeaderCollapse',
computed: {
...mapState('admin/layout', [
'isMobile',
'isTablet',
'isDesktop',
'menuCollapse',
'showReload'
])
},
methods: {
...mapMutations('admin/layout', [
'updateMenuCollapse'
]),
// 展开/收起侧边栏
handleToggleMenuSide (state) {
if (this.isMobile) {
this.updateMenuCollapse(false);
this.$emit('on-toggle-drawer', state);
} else {
this.updateMenuCollapse(!this.menuCollapse);
}
}
},
watch: {
// 切换页面时,在移动端自动收起侧边栏
// 强行传参 false 是因为有的路由不是在菜单栏发生的,toggle 会使其显示
'$route' () {
if (this.isMobile) this.handleToggleMenuSide(false);
},
// 在平板时自动收起菜单
isTablet (state) {
if (!this.isMobile && state) this.updateMenuCollapse(true);
},
// 在桌面时自动展开菜单
isDesktop (state) {
if (!this.isMobile && state) this.updateMenuCollapse(false);
}
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="toggleFullscreen">
<Icon custom="i-icon i-icon-full-screen" v-show="!isFullscreen" />
<Icon custom="i-icon i-icon-exit-full-screen" v-show="isFullscreen" />
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderFullscreen',
computed: {
...mapState('admin/layout', [
'isFullscreen'
])
},
methods: {
...mapActions('admin/layout', [
'toggleFullscreen'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-i18n" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Icon type="md-globe" />
<DropdownMenu slot="list">
<DropdownItem v-for="(item, key) in languages" :key="key" :name="key" :selected="locale === key">
<span>{{ item.language }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import Languages from '@/i18n/locale';
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderI18n',
data () {
return {
languages: Languages
}
},
computed: {
...mapState('admin/i18n', [
'locale'
]),
...mapState('admin/layout', [
'isMobile'
])
},
methods: {
...mapActions('admin/i18n', [
'setLocale'
]),
handleClick (locale) {
if (locale === this.locale) return;
this.setLocale({ locale, vm: this });
}
}
}
</script>
<template>
<Tooltip :content="tooltipContent" transfer>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="handleOpenLog">
<Badge :count="lengthError === 0 ? null : lengthError" :overflow-count="99" :dot="showDot" :offset="showDot ? [26, 2] : [20, 0]">
<Icon custom="i-icon i-icon-record" />
</Badge>
</span>
</Tooltip>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'iHeaderLog',
computed: {
...mapGetters('admin/log', [
'length',
'lengthError'
]),
showDot () {
return !!this.length && this.lengthError === 0;
},
tooltipContent () {
if (!this.length) {
return '没有日志或异常';
} else {
let text = `${this.length} 条日志`;
if (this.lengthError) text += ` | 包含 ${this.lengthError} 个异常`;
return text;
}
}
},
methods: {
handleOpenLog () {
this.$router.push({
name: 'log'
});
}
}
}
</script>
<template>
<i-link class="i-layout-header-logo" :class="{ 'i-layout-header-logo-stick': !isMobile }" to="/">
<img src="@/assets/images/logo-small.png" v-if="isMobile">
<img src="@/assets/images/logo.png" v-else-if="headerTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderLogo',
computed: {
...mapState('admin/layout', [
'isMobile',
'headerTheme'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in">
<Notification
:wide="isMobile"
:badge-props="badgeProps"
class="i-layout-header-notice"
:class="{ 'i-layout-header-notice-mobile': isMobile }">
<Icon slot="icon" custom="i-icon i-icon-notification" />
<NotificationTab title="通知">
</NotificationTab>
<NotificationTab title="消息">
</NotificationTab>
<NotificationTab title="待办">
</NotificationTab>
</Notification>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderNotice',
data () {
return {
badgeProps: {
offset: [20, 0]
}
}
},
computed: {
...mapState('admin/layout', [
'isMobile'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showSiderCollapse }" @click="handleReload">
<Icon custom="i-icon i-icon-refresh" />
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderReload',
computed: {
...mapState('admin/layout', [
'isMobile',
'showSiderCollapse'
])
},
methods: {
handleReload () {
this.$emit('on-reload');
}
}
}
</script>
<template>
<span v-if="isDesktop" class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-nohover">
<input class="i-layout-header-search" type="text" :placeholder="$t('basicLayout.search.placeholder')">
</span>
<span v-else class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown trigger="click" class="i-layout-header-search-drop" ref="dropdown">
<Icon type="ios-search" />
<DropdownMenu slot="list">
<div class="i-layout-header-search-drop-main">
<Input size="large" prefix="ios-search" type="text" :placeholder="$t('basicLayout.search.placeholder')" />
<span class="i-layout-header-search-drop-main-cancel" @click="handleCloseSearch">{{ $t('basicLayout.search.cancel') }}</span>
</div>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderSearch',
computed: {
...mapState('admin/layout', [
'isDesktop',
'headerMenu'
])
},
methods: {
handleCloseSearch () {
this.$refs.dropdown.handleClick();
}
}
}
</script>
This diff is collapsed.
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-user" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Avatar size="small" :src="info.avatar" v-if="info.avatar" />
<span class="i-layout-header-user-name" v-if="!isMobile">{{ info.name }}</span>
<DropdownMenu slot="list">
<i-link to="/setting/user">
<DropdownItem>
<Icon type="ios-contact-outline" />
<span>{{ $t('basicLayout.user.center') }}</span>
</DropdownItem>
</i-link>
<i-link to="/setting/account">
<DropdownItem>
<Icon type="ios-settings-outline" />
<span>{{ $t('basicLayout.user.setting') }}</span>
</DropdownItem>
</i-link>
<DropdownItem divided name="logout">
<Icon type="ios-log-out" />
<span>{{ $t('basicLayout.user.logOut') }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderUser',
computed: {
...mapState('admin/user', [
'info'
]),
...mapState('admin/layout', [
'isMobile',
'logoutConfirm'
])
},
methods: {
...mapActions('admin/account', [
'logout'
]),
handleClick (name) {
if (name === 'logout') {
this.logout({
confirm: this.logoutConfirm,
vm: this
});
}
}
}
}
</script>
// 默认布局使用的多语言
export default {
'zh-CN': {
basicLayout: {
search: {
placeholder: '搜索...',
cancel: '取消'
},
user: {
center: '个人中心',
setting: '设置',
logOut: '退出登录'
},
logout: {
confirmTitle: '退出登录确认',
confirmContent: '您确定退出登录当前账户吗?打开的标签页和个人设置将会保存。'
},
tabs: {
left: '关闭左侧',
right: '关闭右侧',
other: '关闭其它',
all: '全部关闭'
}
}
},
'en-US': {
basicLayout: {
search: {
placeholder: 'Search...',
cancel: 'Cancel'
},
user: {
center: 'My home',
setting: 'Setting',
logOut: 'Log out'
},
logout: {
confirmTitle: 'Logout confirmation',
confirmContent: 'Are you sure you are logged out of your current account? Open tabs and personal settings will be saved.'
},
tabs: {
left: 'Close left',
right: 'Close right',
other: 'Close other',
all: 'Close all'
}
}
}
}
This diff is collapsed.
<template>
<div class="i-layout-menu-head" :class="{ 'i-layout-menu-head-mobile': isMobile }">
<Menu mode="horizontal" :active-name="headerName" v-if="!isMobile && !isMenuLimit" ref="menu">
<MenuItem v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :name="item.name" :key="item.path" @click.native="handleClick(item.path, 'header')">
<i-menu-head-title :item="item" />
</MenuItem>
</Menu>
<div class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-no-height" v-else>
<Dropdown trigger="click" :class="{ 'i-layout-menu-head-mobile-drop': isMobile }">
<Icon type="ios-apps" />
<DropdownMenu slot="list">
<i-link v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :key="item.path" @click.native="handleClick(item.path, 'header')">
<DropdownItem>
<i-menu-head-title :item="item" />
</DropdownItem>
</i-link>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import iMenuHeadTitle from './title';
import { mapState, mapGetters } from 'vuex';
import { getStyle } from 'view-design/src/utils/assist';
import clickItem from '../mixins/click-item';
// import { on, off } from 'view-design/src/utils/dom';
// import { throttle } from 'lodash';
export default {
name: 'iMenuHead',
components: { iMenuHeadTitle },
mixins: [ clickItem ],
computed: {
...mapState('admin/layout', [
'isMobile'
]),
...mapState('admin/menu', [
'headerName'
]),
...mapGetters('admin/menu', [
'filterHeader'
])
},
data () {
return {
handleResize: () => {},
isMenuLimit: false,
menuMaxWidth: 0 // 达到这个值后,menu 就显示不下了
}
},
methods: {
handleGetMenuHeight () {
const menuWidth = parseInt(getStyle(this.$el, 'width'));
const $menu = this.$refs.menu;
if ($menu) {
const menuHeight = parseInt(getStyle(this.$refs.menu.$el, 'height'));
if (menuHeight > 64) {
if (!this.isMenuLimit) {
this.menuMaxWidth = menuWidth;
}
this.isMenuLimit = true;
}
} else if (menuWidth >= this.menuMaxWidth) {
this.isMenuLimit = false;
}
}
},
watch: {
filterHeader () {
this.handleGetMenuHeight();
},
isMobile () {
this.handleGetMenuHeight();
}
},
mounted () {
// this.handleResize = throttle(this.handleGetMenuHeight, 100, { leading: false });
// on(window, 'resize', this.handleResize);
this.handleGetMenuHeight();
},
beforeDestroy () {
// off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<div class="i-layout-menu-head-title">
<span class="i-layout-menu-head-title-icon" v-if="(item.icon || item.custom || item.img) && !hideIcon">
<Icon :type="item.icon" v-if="item.icon" />
<Icon :custom="item.custom" v-else-if="item.custom" />
<img :src="item.img" v-else-if="item.img" />
</span>
<span class="i-layout-menu-head-title-text">{{ tTitle(item.title) }}</span>
</div>
</template>
<script>
/**
* 该组件除了 Menu,也被 Breadcrumb 使用过
* */
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuHeadTitle',
mixins: [ tTitle ],
props: {
item: {
type: Object,
default () {
return {}
}
},
hideIcon: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<div>
<div class="i-layout-sider-logo" :class="{ 'i-layout-sider-logo-dark': siderTheme === 'dark' }">
<transition name="fade-quick">
<i-link to="/" v-show="!hideLogo">
<img src="@/assets/images/logo-small.png" v-if="menuCollapse">
<img src="@/assets/images/logo.png" v-else-if="siderTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</transition>
</div>
<Menu
ref="menu"
class="i-layout-menu-side i-scrollbar-hide"
:theme="siderTheme"
:accordion="menuAccordion"
:active-name="activePath"
:open-names="openNames"
width="auto">
<template v-if="!menuCollapse" v-for="(item, index) in filterSider">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
<template v-else>
<Tooltip :content="tTitle(item.title)" placement="right" v-if="item.children === undefined || !item.children.length" :key="index">
<i-menu-side-item :menu="item" hide-title />
</Tooltip>
<i-menu-side-collapse v-else :menu="item" :key="index" top-level />
</template>
</Menu>
</div>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideSubmenu from './submenu';
import iMenuSideCollapse from './menu-collapse';
import tTitle from '../mixins/translate-title';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'iMenuSide',
mixins: [ tTitle ],
components: { iMenuSideItem, iMenuSideSubmenu, iMenuSideCollapse },
props: {
hideLogo: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'menuAccordion',
'menuCollapse'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
...mapGetters('admin/menu', [
'filterSider'
])
},
watch: {
'$route': {
handler () {
this.handleUpdateMenuState();
},
immediate: true
},
// 在展开/收起侧边菜单栏时,更新一次 menu 的状态
menuCollapse () {
this.handleUpdateMenuState();
}
},
methods: {
handleUpdateMenuState () {
this.$nextTick(() => {
if (this.$refs.menu) {
this.$refs.menu.updateActiveName();
if (this.menuAccordion) this.$refs.menu.updateOpened();
}
});
}
}
}
</script>
<template>
<Dropdown placement="right-start" :class="dropdownClasses">
<li :class="menuItemClasses" v-if="topLevel">
<i-menu-side-title :menu="menu" hide-title />
</li>
<DropdownItem v-else>
<i-menu-side-title :menu="menu" :selected="openNames.indexOf(menu.path) >= 0" />
<Icon type="ios-arrow-forward" class="i-layout-menu-side-arrow" />
</DropdownItem>
<DropdownMenu slot="list">
<div class="i-layout-menu-side-collapse-title" v-if="showCollapseMenuTitle">
<i-menu-side-title :menu="menu" />
</div>
<template v-for="(item, index) in menu.children">
<i-link :to="item.path" :target="item.target" v-if="item.children === undefined || !item.children.length" :key="index" @click.native="handleClick(item.path)">
<DropdownItem :divided="item.divided" :class="{ 'i-layout-menu-side-collapse-item-selected': item.path === activePath }">
<i-menu-side-title :menu="item" />
</DropdownItem>
</i-link>
<i-menu-side-collapse v-else :menu="item" :key="index" />
</template>
</DropdownMenu>
</Dropdown>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
import { mapState } from 'vuex';
export default {
name: 'iMenuSideCollapse',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
// 是否是第一级,区分在于左侧和展开侧
topLevel: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'showCollapseMenuTitle'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
dropdownClasses () {
return {
'i-layout-menu-side-collapse-top': this.topLevel,
'i-layout-menu-side-collapse-dark': this.siderTheme === 'dark'
}
},
menuItemClasses () {
return [
'ivu-menu-item i-layout-menu-side-collapse-top-item',
{
'ivu-menu-item-selected ivu-menu-item-active': this.openNames.indexOf(this.menu.path) >= 0 // -active 在高亮时,有背景
}
]
}
}
}
</script>
<template>
<div>
<MenuItem :to="menu.path" :replace="menu.replace" :target="menu.target" :name="menu.path" @click.native="handleClick(menu.path)">
<i-menu-side-title :menu="menu" :hide-title="hideTitle" />
</MenuItem>
</div>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
export default {
name: 'iMenuSideItem',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<span class="i-layout-menu-side-title">
<span class="i-layout-menu-side-title-icon" :class="{ 'i-layout-menu-side-title-icon-single': hideTitle }" v-if="menu.icon || menu.custom || menu.img">
<Icon :type="menu.icon" v-if="menu.icon" />
<Icon :custom="menu.custom" v-else-if="menu.custom" />
<img :src="menu.img" v-else-if="menu.img" />
</span>
<span class="i-layout-menu-side-title-text" :class="{ 'i-layout-menu-side-title-text-selected': selected }" v-if="!hideTitle">{{ tTitle(menu.title) }}</span>
</span>
</template>
<script>
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuSideTitle',
mixins: [ tTitle ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
},
// 用于侧边栏收起 Dropdown 当前高亮
selected: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<Submenu :name="menu.path">
<template slot="title">
<i-menu-side-title :menu="menu" />
</template>
<template v-for="(item, index) in menu.children">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
</Submenu>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideTitle from './menu-title';
export default {
name: 'iMenuSideSubmenu',
components: { iMenuSideItem, iMenuSideTitle },
props: {
menu: {
type: Object,
default () {
return {}
}
}
}
}
</script>
import { findComponentUpward } from 'view-design/src/utils/assist';
import { mapState } from 'vuex';
export default {
computed: {
...mapState('admin/layout', [
'menuSiderReload',
'menuHeaderReload'
])
},
methods: {
handleClick (path, type = 'sider') {
const current = this.$route.path;
if (current === path) {
if (type === 'sider' && this.menuSiderReload) this.handleReload();
else if (type === 'header' && this.menuHeaderReload) this.handleReload();
}
},
handleReload () {
const $layout = findComponentUpward(this, 'BasicLayout');
if ($layout) $layout.handleReload();
}
}
}
export default {
methods: {
tTitle (title) {
if (title && title.indexOf('$t:') === 0) {
return this.$t(title.split('$t:')[1]);
} else {
return title;
}
}
}
}
<template>
<div class="i-layout-tabs" :class="classes" :style="styles">
<div class="i-layout-tabs-main">
<Tabs
type="card"
:value="current"
:animated="false"
closable
@on-click="handleClickTab"
@on-tab-remove="handleClickClose"
>
<TabPane
v-for="page in opened"
:key="page.fullPath"
:label="(h) => tabLabel(h, page)"
:name="page.fullPath"
:closable="page.meta && page.meta.closable"
/>
</Tabs>
<Dropdown class="i-layout-tabs-close" @on-click="handleClose">
<div class="i-layout-tabs-close-main">
<Icon type="ios-arrow-down" />
</div>
<DropdownMenu slot="list">
<DropdownItem name="left">
<Icon type="md-arrow-back" />
{{ $t('basicLayout.tabs.left') }}
</DropdownItem>
<DropdownItem name="right">
<Icon type="md-arrow-forward" />
{{ $t('basicLayout.tabs.right') }}
</DropdownItem>
<DropdownItem name="other">
<Icon type="md-close" />
{{ $t('basicLayout.tabs.other') }}
</DropdownItem>
<DropdownItem name="all">
<Icon type="md-close-circle" />
{{ $t('basicLayout.tabs.all') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import menuSider from '@/menu/sider';
import tTitle from '../mixins/translate-title';
import Setting from '@/setting';
import { getAllSiderMenu } from '@/libs/system';
export default {
name: 'iTabs',
mixins: [ tTitle ],
computed: {
...mapState('admin/page', [
'opened',
'current'
]),
...mapState('admin/layout', [
'showTabsIcon',
'tabsFix',
'tabsReload',
'headerFix',
'headerStick',
'isMobile',
'menuCollapse'
]),
...mapGetters('admin/menu', [
'hideSider'
]),
classes () {
return {
'i-layout-tabs-fix': this.tabsFix
}
},
isHeaderStick () {
return this.hideSider;
},
styles () {
let style = {};
if (this.tabsFix && !this.headerFix) {
style.top = `${64 - this.scrollTop}px`;
}
const menuWidth = this.isHeaderStick ? 0 : this.menuCollapse ? 80 : Setting.menuSideWidth;
if (!this.isMobile && this.tabsFix) {
style.width = `calc(100% - ${menuWidth}px)`;
style.left = `${menuWidth}px`;
}
return style;
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标用
allSiderMenu: getAllSiderMenu(menuSider),
scrollTop: 0
}
},
methods: {
...mapActions('admin/page', [
'close',
'closeLeft',
'closeRight',
'closeOther',
'closeAll'
]),
tabLabel (h, page) {
const title = h('span', this.tTitle(page.meta.title) || '未命名');
let slot = [];
if (this.showTabsIcon) {
const fullPathWithoutQuery = page.fullPath.indexOf('?') >= 0 ? page.fullPath.split('?')[0] : page.fullPath;
const currentMenu = this.allSiderMenu.find(menu => menu.path === fullPathWithoutQuery) || {};
let icon;
if (currentMenu.icon) {
icon = h('Icon', {
props: {
type: currentMenu.icon
}
});
} else if (currentMenu.custom) {
icon = h('Icon', {
props: {
custom: currentMenu.custom
}
});
} else if (currentMenu.img) {
icon = h('img', {
attrs: {
src: currentMenu.img
}
});
}
if (icon) slot.push(icon);
slot.push(title);
} else {
slot.push(title);
}
return h('div', {
class: 'i-layout-tabs-title'
}, slot);
},
handleClickTab (tabName) {
if (tabName === this.current) {
if (this.tabsReload) {
this.$emit('on-reload');
}
} else {
const page = this.opened.find(page => page.fullPath === tabName);
const { name, params, query } = page;
if (page) this.$router.push({ name, params, query }, () => {});
}
},
handleClickClose (tagName) {
this.close({
tagName
});
},
handleScroll () {
if (this.tabsFix && !this.headerFix) {
const scrollTop = document.body.scrollTop + document.documentElement.scrollTop;
this.scrollTop = scrollTop > 64 ? 64 : scrollTop;
}
},
handleClose (name) {
const params = {
pageSelect: this.current
};
switch (name) {
case 'left':
this.closeLeft(params);
break;
case 'right':
this.closeRight(params);
break;
case 'other':
this.closeOther(params);
break;
case 'all':
this.closeAll();
break;
}
}
},
mounted () {
document.addEventListener('scroll', this.handleScroll, { passive: true });
this.handleScroll();
},
beforeDestroy () {
document.removeEventListener('scroll', this.handleScroll);
}
}
</script>
<template>
<GlobalFooter class="i-copyright" :links="links" :copyright="copyright" />
</template>
<script>
export default {
name: 'i-copyright',
data () {
return {
links: [
{
title: '官网',
key: '官网',
href: 'https://iview.design',
blankTarget: true
},
{
title: '社区',
key: '社区',
href: 'https://dev.iviewui.com',
blankTarget: true
},
{
title: '专业版',
key: '专业版',
href: 'https://pro.iviewui.com',
blankTarget: true
}
],
copyright: 'Copyright © 2019 北京视图更新科技有限公司'
}
}
}
</script>
<style lang="less">
.i-copyright{
flex: 0 0 auto;
}
</style>
<template>
<a
:href="linkUrl"
:target="target"
class="i-link"
:class="{ 'i-link-color': !linkColor }"
@click.exact="handleClickItem($event, false)"
@click.ctrl="handleClickItem($event, true)"
@click.meta="handleClickItem($event, true)"
><slot></slot></a>
</template>
<script>
import mixinsLink from 'view-design/src/mixins/link';
export default {
name: 'i-link',
mixins: [ mixinsLink ],
props: {
disabled: {
type: Boolean,
default: false
},
// 开启后,链接颜色为默认的蓝色,默认关闭为继承效果
linkColor: {
type: Boolean,
default: false
}
},
methods: {
handleClickItem (event, new_window = false) {
if (this.disabled) return;
this.handleCheckClick(event, new_window);
}
}
}
</script>
<style lang="less">
.i-link{
cursor: pointer;
&-color{
&, &:hover, &:active{
color: inherit;
}
}
}
</style>
<template>
<div class="i-mde" :class="classes">
<textarea ref="mde"></textarea>
</div>
</template>
<script>
import SimpleMDE from 'simplemde';
import 'simplemde/dist/simplemde.min.css';
export default {
name: 'i-mde',
props: {
value: {
type: String,
default: ''
},
border: {
type: Boolean,
default: false
},
// 配置参数
config: {
type: Object,
default: () => ({})
}
},
data () {
return {
// 编辑器实例
mde: null,
// 编辑器默认参数
// 详见 https://github.com/sparksuite/simplemde-markdown-editor#configuration
defaultConfig: {
}
}
},
computed: {
classes () {
return [
{
'i-mde-no-border': !this.border
}
];
}
},
methods: {
// 初始化
init () {
// 合并参数
const config = Object.assign({}, this.defaultConfig, this.config);
// 初始化
this.mde = new SimpleMDE({
...config,
// 初始值
initialValue: this.value,
// 挂载元素
element: this.$refs.mde
});
this.mde.codemirror.on('change', () => {
this.$emit('input', this.mde.value());
this.$emit('on-change', this.mde.value());
});
},
// 增加内容
add (val) {
if (this.mde) {
this.mde.value(this.value + val);
}
},
// 替换内容
replace (val) {
if (this.mde) {
this.mde.value(val);
}
}
},
mounted () {
// 初始化
this.init();
},
beforeDestroy () {
// 在组件销毁后销毁实例
this.mde = null;
}
}
</script>
<style lang="less">
.i-mde{
.editor-toolbar.fullscreen, .CodeMirror-fullscreen, .editor-preview-side{
z-index: 100;
border-radius: 0;
}
&-no-border{
.editor-toolbar{
border: none;
border-bottom: 1px solid #e8eaec;
}
.CodeMirror{
border: none;
}
.editor-preview-side{
border: none;
border-left: 1px solid #e8eaec;
}
}
}
</style>
<template>
<div class="i-quill" :class="classes">
<div ref="editor" :style="styles"></div>
</div>
</template>
<script>
import Quill from 'quill';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
export default {
name: 'i-quill',
props: {
value: {
type: String,
default: ''
},
border: {
type: Boolean,
default: false
},
height: {
type: Number
},
minHeight: {
type: Number
}
},
data () {
return {
Quill: null,
currentValue: '',
options: {
theme: 'snow',
bounds: document.body,
debug: 'warn',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'color': [] }, { 'background': [] }],
['blockquote', 'code-block'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
// [{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'align': [] }],
[{ 'direction': 'rtl' }],
// [{ 'font': [] }],
['clean'],
['link', 'image']
]
},
placeholder: '内容...',
readOnly: false
}
}
},
computed: {
classes () {
return [
{
'i-quill-no-border': !this.border
}
];
},
styles () {
let style = {};
if (this.minHeight) {
style.minHeight = `${this.minHeight}px`;
}
if (this.height) {
style.height = `${this.height}px`;
}
return style;
}
},
watch: {
value: {
handler (val) {
if (val !== this.currentValue) {
this.currentValue = val;
if (this.Quill) {
this.Quill.pasteHTML(this.value);
}
}
},
immediate: true
}
},
methods: {
init () {
const editor = this.$refs.editor;
// 初始化编辑器
this.Quill = new Quill(editor, this.options);
// 默认值
this.Quill.pasteHTML(this.currentValue);
// 绑定事件
this.Quill.on('text-change', (delta, oldDelta, source) => {
const html = this.$refs.editor.children[0].innerHTML;
const text = this.Quill.getText();
const quill = this.Quill;
// 更新内部的值
this.currentValue = html;
// 发出事件 v-model
this.$emit('input', html);
// 发出事件
this.$emit('on-change', { html, text, quill });
});
// 将一些 quill 自带的事件传递出去
this.Quill.on('text-change', (delta, oldDelta, source) => {
this.$emit('on-text-change', delta, oldDelta, source);
});
this.Quill.on('selection-change', (range, oldRange, source) => {
this.$emit('on-selection-change', range, oldRange, source);
});
this.Quill.on('editor-change', (eventName, ...args) => {
this.$emit('on-editor-change', eventName, ...args);
});
}
},
mounted () {
this.init();
},
beforeDestroy () {
// 在组件销毁后销毁实例
this.Quill = null;
}
}
</script>
<style lang="less">
.i-quill-no-border{
.ql-toolbar.ql-snow{
border: none;
border-bottom: 1px solid #e8eaec;
}
.ql-container.ql-snow{
border: none;
}
}
</style>
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import store from '@/store/index';
import Languages from '@/i18n/locale';
store.dispatch('admin/i18n/getLocale');
const locale = store.state.admin.i18n.locale;
Vue.use(VueI18n);
export default new VueI18n({
locale,
messages: Languages
});
// 导入自有语言包
import zhCN from './locale/zh-CN';
import enUS from './locale/en-US';
// 导入 iView 语言包
import zhCNiView from 'view-design/dist/locale/zh-CN';
import enUSiView from 'view-design/dist/locale/en-US';
// 导入布局语言包
import layoutLocale from '@/layouts/basic-layout/i18n';
// 合并语言包
export default {
'zh-CN': Object.assign(zhCN, zhCNiView, layoutLocale['zh-CN']),
'en-US': Object.assign(enUS, enUSiView, layoutLocale['en-US'])
};
export default {
locale: 'en-US',
language: 'English',
menu: {
i18n: 'Internationalization'
},
page: {
login: {
title: 'Login',
remember: 'Remember me',
forgot: 'Forgot your password?',
submit: 'Login',
other: 'Sign in with',
signup: 'Sign up'
},
register: {
title: 'Register',
submit: 'Register',
other: 'Already have an account?'
},
exception: {
e403: 'Sorry, you don\'t have access to this page.',
e404: 'Sorry, the page you visited does not exist.',
e500: 'Sorry, the server is reporting an error.',
btn: 'Back to home'
},
i18n: {
content: 'Hello, nice to meet you!'
}
}
}
export default {
locale: 'zh-CN',
language: '简体中文',
menu: {
i18n: '多语言'
},
page: {
login: {
title: '登录',
remember: '自动登录',
forgot: '忘记密码',
submit: '登录',
other: '其它登录方式',
signup: '注册账户'
},
register: {
title: '注册',
submit: '注册',
other: '使用已有账户登录'
},
exception: {
e403: '抱歉,你无权访问该页面',
e404: '抱歉,你访问的页面不存在',
e500: '抱歉,服务器出错了',
btn: '返回首页'
},
i18n: {
content: '你好,很高兴认识你!'
}
}
}
<template>
<Breadcrumb class="i-layout-header-breadcrumb" v-if="!isLimit" ref="breadcrumb">
<BreadcrumbItem>
<i-menu-head-title :item="topItem" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem v-for="item in items" :key="item.path">
<i-menu-head-title :item="item" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem>
<i-menu-head-title :item="siderMenuObject[activePath]" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
</Breadcrumb>
</template>
<script>
import { mapState } from 'vuex';
import menuSider from '@/menu/sider';
import { flattenSiderMenu } from '@/libs/system';
import iMenuHeadTitle from '../menu-head/title';
import { on, off } from 'view-design/src/utils/dom';
import { findComponentUpward, getStyle } from 'view-design/src/utils/assist';
import { throttle } from 'lodash';
export default {
name: 'iHeaderBreadcrumb',
components: { iMenuHeadTitle },
computed: {
...mapState('admin/layout', [
'showBreadcrumbIcon',
'menuCollapse'
]),
...mapState('admin/menu', [
'openNames',
'activePath',
'header',
'headerName'
]),
siderMenuObject () {
let obj = {};
this.allSiderMenu.forEach(item => {
if ('path' in item) {
obj[item.path] = item;
}
});
return obj;
},
items () {
let items = [...this.openNames];
let newItems = [];
items.forEach(i => {
newItems.push(this.siderMenuObject[i]);
});
return newItems;
},
// 第一级,默认是 menu/header.js 中的第一项
topItem () {
return this.header.find(item => item.name === this.headerName);
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标及显示对应内容
allSiderMenu: flattenSiderMenu(menuSider, []),
handleResize: () => {},
isLimit: false,
maxWidth: 560,
breadcrumbWidth: 0
}
},
methods: {
handleCheckWidth () {
const $header = findComponentUpward(this, 'Header');
if ($header) {
const headerWidth = parseInt(getStyle($header.$el, 'width'));
this.$nextTick(() => {
this.isLimit = headerWidth - this.maxWidth <= this.breadcrumbWidth;
});
}
},
handleGetWidth () {
this.isLimit = false;
this.$nextTick(() => {
const $breadcrumb = this.$refs.breadcrumb;
if ($breadcrumb) {
this.breadcrumbWidth = parseInt(getStyle($breadcrumb.$el, 'width'));
}
});
}
},
watch: {
topItem: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
items: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
activePath: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
}
},
mounted () {
this.handleResize = throttle(this.handleCheckWidth, 100, { leading: false });
on(window, 'resize', this.handleResize);
this.handleGetWidth();
this.handleCheckWidth();
},
beforeDestroy () {
off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showReload }" @click="handleToggleMenuSide">
<Icon custom="i-icon i-icon-menu-unfold" v-show="menuCollapse || isMobile" />
<Icon custom="i-icon i-icon-menu-fold" v-show="!menuCollapse && !isMobile" />
</span>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'iHeaderCollapse',
computed: {
...mapState('admin/layout', [
'isMobile',
'isTablet',
'isDesktop',
'menuCollapse',
'showReload'
])
},
methods: {
...mapMutations('admin/layout', [
'updateMenuCollapse'
]),
// 展开/收起侧边栏
handleToggleMenuSide (state) {
if (this.isMobile) {
this.updateMenuCollapse(false);
this.$emit('on-toggle-drawer', state);
} else {
this.updateMenuCollapse(!this.menuCollapse);
}
}
},
watch: {
// 切换页面时,在移动端自动收起侧边栏
// 强行传参 false 是因为有的路由不是在菜单栏发生的,toggle 会使其显示
'$route' () {
if (this.isMobile) this.handleToggleMenuSide(false);
},
// 在平板时自动收起菜单
isTablet (state) {
if (!this.isMobile && state) this.updateMenuCollapse(true);
},
// 在桌面时自动展开菜单
isDesktop (state) {
if (!this.isMobile && state) this.updateMenuCollapse(false);
}
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="toggleFullscreen">
<Icon custom="i-icon i-icon-full-screen" v-show="!isFullscreen" />
<Icon custom="i-icon i-icon-exit-full-screen" v-show="isFullscreen" />
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderFullscreen',
computed: {
...mapState('admin/layout', [
'isFullscreen'
])
},
methods: {
...mapActions('admin/layout', [
'toggleFullscreen'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-i18n" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Icon type="md-globe" />
<DropdownMenu slot="list">
<DropdownItem v-for="(item, key) in languages" :key="key" :name="key" :selected="locale === key">
<span>{{ item.language }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import Languages from '@/i18n/locale';
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderI18n',
data () {
return {
languages: Languages
}
},
computed: {
...mapState('admin/i18n', [
'locale'
]),
...mapState('admin/layout', [
'isMobile'
])
},
methods: {
...mapActions('admin/i18n', [
'setLocale'
]),
handleClick (locale) {
if (locale === this.locale) return;
this.setLocale({ locale, vm: this });
}
}
}
</script>
<template>
<Tooltip :content="tooltipContent" transfer>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="handleOpenLog">
<Badge :count="lengthError === 0 ? null : lengthError" :overflow-count="99" :dot="showDot" :offset="showDot ? [26, 2] : [20, 0]">
<Icon custom="i-icon i-icon-record" />
</Badge>
</span>
</Tooltip>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'iHeaderLog',
computed: {
...mapGetters('admin/log', [
'length',
'lengthError'
]),
showDot () {
return !!this.length && this.lengthError === 0;
},
tooltipContent () {
if (!this.length) {
return '没有日志或异常';
} else {
let text = `${this.length} 条日志`;
if (this.lengthError) text += ` | 包含 ${this.lengthError} 个异常`;
return text;
}
}
},
methods: {
handleOpenLog () {
this.$router.push({
name: 'log'
});
}
}
}
</script>
<template>
<i-link class="i-layout-header-logo" :class="{ 'i-layout-header-logo-stick': !isMobile }" to="/">
<img src="@/assets/images/logo-small.png" v-if="isMobile">
<img src="@/assets/images/logo.png" v-else-if="headerTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderLogo',
computed: {
...mapState('admin/layout', [
'isMobile',
'headerTheme'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in">
<Notification
:wide="isMobile"
:badge-props="badgeProps"
class="i-layout-header-notice"
:class="{ 'i-layout-header-notice-mobile': isMobile }">
<Icon slot="icon" custom="i-icon i-icon-notification" />
<NotificationTab title="通知">
</NotificationTab>
<NotificationTab title="消息">
</NotificationTab>
<NotificationTab title="待办">
</NotificationTab>
</Notification>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderNotice',
data () {
return {
badgeProps: {
offset: [20, 0]
}
}
},
computed: {
...mapState('admin/layout', [
'isMobile'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showSiderCollapse }" @click="handleReload">
<Icon custom="i-icon i-icon-refresh" />
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderReload',
computed: {
...mapState('admin/layout', [
'isMobile',
'showSiderCollapse'
])
},
methods: {
handleReload () {
this.$emit('on-reload');
}
}
}
</script>
<template>
<span v-if="isDesktop" class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-nohover">
<input class="i-layout-header-search" type="text" :placeholder="$t('basicLayout.search.placeholder')">
</span>
<span v-else class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown trigger="click" class="i-layout-header-search-drop" ref="dropdown">
<Icon type="ios-search" />
<DropdownMenu slot="list">
<div class="i-layout-header-search-drop-main">
<Input size="large" prefix="ios-search" type="text" :placeholder="$t('basicLayout.search.placeholder')" />
<span class="i-layout-header-search-drop-main-cancel" @click="handleCloseSearch">{{ $t('basicLayout.search.cancel') }}</span>
</div>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderSearch',
computed: {
...mapState('admin/layout', [
'isDesktop',
'headerMenu'
])
},
methods: {
handleCloseSearch () {
this.$refs.dropdown.handleClick();
}
}
}
</script>
This diff is collapsed.
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-user" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Avatar size="small" :src="info.avatar" v-if="info.avatar" />
<span class="i-layout-header-user-name" v-if="!isMobile">{{ info.name }}</span>
<DropdownMenu slot="list">
<i-link to="/setting/user">
<DropdownItem>
<Icon type="ios-contact-outline" />
<span>{{ $t('basicLayout.user.center') }}</span>
</DropdownItem>
</i-link>
<i-link to="/setting/account">
<DropdownItem>
<Icon type="ios-settings-outline" />
<span>{{ $t('basicLayout.user.setting') }}</span>
</DropdownItem>
</i-link>
<DropdownItem divided name="logout">
<Icon type="ios-log-out" />
<span>{{ $t('basicLayout.user.logOut') }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderUser',
computed: {
...mapState('admin/user', [
'info'
]),
...mapState('admin/layout', [
'isMobile',
'logoutConfirm'
])
},
methods: {
...mapActions('admin/account', [
'logout'
]),
handleClick (name) {
if (name === 'logout') {
this.logout({
confirm: this.logoutConfirm,
vm: this
});
}
}
}
}
</script>
// 默认布局使用的多语言
export default {
'zh-CN': {
basicLayout: {
search: {
placeholder: '搜索...',
cancel: '取消'
},
user: {
center: '个人中心',
setting: '设置',
logOut: '退出登录'
},
logout: {
confirmTitle: '退出登录确认',
confirmContent: '您确定退出登录当前账户吗?打开的标签页和个人设置将会保存。'
},
tabs: {
left: '关闭左侧',
right: '关闭右侧',
other: '关闭其它',
all: '全部关闭'
}
}
},
'en-US': {
basicLayout: {
search: {
placeholder: 'Search...',
cancel: 'Cancel'
},
user: {
center: 'My home',
setting: 'Setting',
logOut: 'Log out'
},
logout: {
confirmTitle: 'Logout confirmation',
confirmContent: 'Are you sure you are logged out of your current account? Open tabs and personal settings will be saved.'
},
tabs: {
left: 'Close left',
right: 'Close right',
other: 'Close other',
all: 'Close all'
}
}
}
}
This diff is collapsed.
<template>
<div class="i-layout-menu-head" :class="{ 'i-layout-menu-head-mobile': isMobile }">
<Menu mode="horizontal" :active-name="headerName" v-if="!isMobile && !isMenuLimit" ref="menu">
<MenuItem v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :name="item.name" :key="item.path" @click.native="handleClick(item.path, 'header')">
<i-menu-head-title :item="item" />
</MenuItem>
</Menu>
<div class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-no-height" v-else>
<Dropdown trigger="click" :class="{ 'i-layout-menu-head-mobile-drop': isMobile }">
<Icon type="ios-apps" />
<DropdownMenu slot="list">
<i-link v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :key="item.path" @click.native="handleClick(item.path, 'header')">
<DropdownItem>
<i-menu-head-title :item="item" />
</DropdownItem>
</i-link>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import iMenuHeadTitle from './title';
import { mapState, mapGetters } from 'vuex';
import { getStyle } from 'view-design/src/utils/assist';
import clickItem from '../mixins/click-item';
// import { on, off } from 'view-design/src/utils/dom';
// import { throttle } from 'lodash';
export default {
name: 'iMenuHead',
components: { iMenuHeadTitle },
mixins: [ clickItem ],
computed: {
...mapState('admin/layout', [
'isMobile'
]),
...mapState('admin/menu', [
'headerName'
]),
...mapGetters('admin/menu', [
'filterHeader'
])
},
data () {
return {
handleResize: () => {},
isMenuLimit: false,
menuMaxWidth: 0 // 达到这个值后,menu 就显示不下了
}
},
methods: {
handleGetMenuHeight () {
const menuWidth = parseInt(getStyle(this.$el, 'width'));
const $menu = this.$refs.menu;
if ($menu) {
const menuHeight = parseInt(getStyle(this.$refs.menu.$el, 'height'));
if (menuHeight > 64) {
if (!this.isMenuLimit) {
this.menuMaxWidth = menuWidth;
}
this.isMenuLimit = true;
}
} else if (menuWidth >= this.menuMaxWidth) {
this.isMenuLimit = false;
}
}
},
watch: {
filterHeader () {
this.handleGetMenuHeight();
},
isMobile () {
this.handleGetMenuHeight();
}
},
mounted () {
// this.handleResize = throttle(this.handleGetMenuHeight, 100, { leading: false });
// on(window, 'resize', this.handleResize);
this.handleGetMenuHeight();
},
beforeDestroy () {
// off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<div class="i-layout-menu-head-title">
<span class="i-layout-menu-head-title-icon" v-if="(item.icon || item.custom || item.img) && !hideIcon">
<Icon :type="item.icon" v-if="item.icon" />
<Icon :custom="item.custom" v-else-if="item.custom" />
<img :src="item.img" v-else-if="item.img" />
</span>
<span class="i-layout-menu-head-title-text">{{ tTitle(item.title) }}</span>
</div>
</template>
<script>
/**
* 该组件除了 Menu,也被 Breadcrumb 使用过
* */
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuHeadTitle',
mixins: [ tTitle ],
props: {
item: {
type: Object,
default () {
return {}
}
},
hideIcon: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<div>
<div class="i-layout-sider-logo" :class="{ 'i-layout-sider-logo-dark': siderTheme === 'dark' }">
<transition name="fade-quick">
<i-link to="/" v-show="!hideLogo">
<img src="@/assets/images/logo-small.png" v-if="menuCollapse">
<img src="@/assets/images/logo.png" v-else-if="siderTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</transition>
</div>
<Menu
ref="menu"
class="i-layout-menu-side i-scrollbar-hide"
:theme="siderTheme"
:accordion="menuAccordion"
:active-name="activePath"
:open-names="openNames"
width="auto">
<template v-if="!menuCollapse" v-for="(item, index) in filterSider">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
<template v-else>
<Tooltip :content="tTitle(item.title)" placement="right" v-if="item.children === undefined || !item.children.length" :key="index">
<i-menu-side-item :menu="item" hide-title />
</Tooltip>
<i-menu-side-collapse v-else :menu="item" :key="index" top-level />
</template>
</Menu>
</div>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideSubmenu from './submenu';
import iMenuSideCollapse from './menu-collapse';
import tTitle from '../mixins/translate-title';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'iMenuSide',
mixins: [ tTitle ],
components: { iMenuSideItem, iMenuSideSubmenu, iMenuSideCollapse },
props: {
hideLogo: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'menuAccordion',
'menuCollapse'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
...mapGetters('admin/menu', [
'filterSider'
])
},
watch: {
'$route': {
handler () {
this.handleUpdateMenuState();
},
immediate: true
},
// 在展开/收起侧边菜单栏时,更新一次 menu 的状态
menuCollapse () {
this.handleUpdateMenuState();
}
},
methods: {
handleUpdateMenuState () {
this.$nextTick(() => {
if (this.$refs.menu) {
this.$refs.menu.updateActiveName();
if (this.menuAccordion) this.$refs.menu.updateOpened();
}
});
}
}
}
</script>
<template>
<Dropdown placement="right-start" :class="dropdownClasses">
<li :class="menuItemClasses" v-if="topLevel">
<i-menu-side-title :menu="menu" hide-title />
</li>
<DropdownItem v-else>
<i-menu-side-title :menu="menu" :selected="openNames.indexOf(menu.path) >= 0" />
<Icon type="ios-arrow-forward" class="i-layout-menu-side-arrow" />
</DropdownItem>
<DropdownMenu slot="list">
<div class="i-layout-menu-side-collapse-title" v-if="showCollapseMenuTitle">
<i-menu-side-title :menu="menu" />
</div>
<template v-for="(item, index) in menu.children">
<i-link :to="item.path" :target="item.target" v-if="item.children === undefined || !item.children.length" :key="index" @click.native="handleClick(item.path)">
<DropdownItem :divided="item.divided" :class="{ 'i-layout-menu-side-collapse-item-selected': item.path === activePath }">
<i-menu-side-title :menu="item" />
</DropdownItem>
</i-link>
<i-menu-side-collapse v-else :menu="item" :key="index" />
</template>
</DropdownMenu>
</Dropdown>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
import { mapState } from 'vuex';
export default {
name: 'iMenuSideCollapse',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
// 是否是第一级,区分在于左侧和展开侧
topLevel: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'showCollapseMenuTitle'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
dropdownClasses () {
return {
'i-layout-menu-side-collapse-top': this.topLevel,
'i-layout-menu-side-collapse-dark': this.siderTheme === 'dark'
}
},
menuItemClasses () {
return [
'ivu-menu-item i-layout-menu-side-collapse-top-item',
{
'ivu-menu-item-selected ivu-menu-item-active': this.openNames.indexOf(this.menu.path) >= 0 // -active 在高亮时,有背景
}
]
}
}
}
</script>
<template>
<div>
<MenuItem :to="menu.path" :replace="menu.replace" :target="menu.target" :name="menu.path" @click.native="handleClick(menu.path)">
<i-menu-side-title :menu="menu" :hide-title="hideTitle" />
</MenuItem>
</div>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
export default {
name: 'iMenuSideItem',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<span class="i-layout-menu-side-title">
<span class="i-layout-menu-side-title-icon" :class="{ 'i-layout-menu-side-title-icon-single': hideTitle }" v-if="menu.icon || menu.custom || menu.img">
<Icon :type="menu.icon" v-if="menu.icon" />
<Icon :custom="menu.custom" v-else-if="menu.custom" />
<img :src="menu.img" v-else-if="menu.img" />
</span>
<span class="i-layout-menu-side-title-text" :class="{ 'i-layout-menu-side-title-text-selected': selected }" v-if="!hideTitle">{{ tTitle(menu.title) }}</span>
</span>
</template>
<script>
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuSideTitle',
mixins: [ tTitle ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
},
// 用于侧边栏收起 Dropdown 当前高亮
selected: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<Submenu :name="menu.path">
<template slot="title">
<i-menu-side-title :menu="menu" />
</template>
<template v-for="(item, index) in menu.children">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
</Submenu>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideTitle from './menu-title';
export default {
name: 'iMenuSideSubmenu',
components: { iMenuSideItem, iMenuSideTitle },
props: {
menu: {
type: Object,
default () {
return {}
}
}
}
}
</script>
import { findComponentUpward } from 'view-design/src/utils/assist';
import { mapState } from 'vuex';
export default {
computed: {
...mapState('admin/layout', [
'menuSiderReload',
'menuHeaderReload'
])
},
methods: {
handleClick (path, type = 'sider') {
const current = this.$route.path;
if (current === path) {
if (type === 'sider' && this.menuSiderReload) this.handleReload();
else if (type === 'header' && this.menuHeaderReload) this.handleReload();
}
},
handleReload () {
const $layout = findComponentUpward(this, 'BasicLayout');
if ($layout) $layout.handleReload();
}
}
}
export default {
methods: {
tTitle (title) {
if (title && title.indexOf('$t:') === 0) {
return this.$t(title.split('$t:')[1]);
} else {
return title;
}
}
}
}
<template>
<div class="i-layout-tabs" :class="classes" :style="styles">
<div class="i-layout-tabs-main">
<Tabs
type="card"
:value="current"
:animated="false"
closable
@on-click="handleClickTab"
@on-tab-remove="handleClickClose"
>
<TabPane
v-for="page in opened"
:key="page.fullPath"
:label="(h) => tabLabel(h, page)"
:name="page.fullPath"
:closable="page.meta && page.meta.closable"
/>
</Tabs>
<Dropdown class="i-layout-tabs-close" @on-click="handleClose">
<div class="i-layout-tabs-close-main">
<Icon type="ios-arrow-down" />
</div>
<DropdownMenu slot="list">
<DropdownItem name="left">
<Icon type="md-arrow-back" />
{{ $t('basicLayout.tabs.left') }}
</DropdownItem>
<DropdownItem name="right">
<Icon type="md-arrow-forward" />
{{ $t('basicLayout.tabs.right') }}
</DropdownItem>
<DropdownItem name="other">
<Icon type="md-close" />
{{ $t('basicLayout.tabs.other') }}
</DropdownItem>
<DropdownItem name="all">
<Icon type="md-close-circle" />
{{ $t('basicLayout.tabs.all') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import menuSider from '@/menu/sider';
import tTitle from '../mixins/translate-title';
import Setting from '@/setting';
import { getAllSiderMenu } from '@/libs/system';
export default {
name: 'iTabs',
mixins: [ tTitle ],
computed: {
...mapState('admin/page', [
'opened',
'current'
]),
...mapState('admin/layout', [
'showTabsIcon',
'tabsFix',
'tabsReload',
'headerFix',
'headerStick',
'isMobile',
'menuCollapse'
]),
...mapGetters('admin/menu', [
'hideSider'
]),
classes () {
return {
'i-layout-tabs-fix': this.tabsFix
}
},
isHeaderStick () {
return this.hideSider;
},
styles () {
let style = {};
if (this.tabsFix && !this.headerFix) {
style.top = `${64 - this.scrollTop}px`;
}
const menuWidth = this.isHeaderStick ? 0 : this.menuCollapse ? 80 : Setting.menuSideWidth;
if (!this.isMobile && this.tabsFix) {
style.width = `calc(100% - ${menuWidth}px)`;
style.left = `${menuWidth}px`;
}
return style;
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标用
allSiderMenu: getAllSiderMenu(menuSider),
scrollTop: 0
}
},
methods: {
...mapActions('admin/page', [
'close',
'closeLeft',
'closeRight',
'closeOther',
'closeAll'
]),
tabLabel (h, page) {
const title = h('span', this.tTitle(page.meta.title) || '未命名');
let slot = [];
if (this.showTabsIcon) {
const fullPathWithoutQuery = page.fullPath.indexOf('?') >= 0 ? page.fullPath.split('?')[0] : page.fullPath;
const currentMenu = this.allSiderMenu.find(menu => menu.path === fullPathWithoutQuery) || {};
let icon;
if (currentMenu.icon) {
icon = h('Icon', {
props: {
type: currentMenu.icon
}
});
} else if (currentMenu.custom) {
icon = h('Icon', {
props: {
custom: currentMenu.custom
}
});
} else if (currentMenu.img) {
icon = h('img', {
attrs: {
src: currentMenu.img
}
});
}
if (icon) slot.push(icon);
slot.push(title);
} else {
slot.push(title);
}
return h('div', {
class: 'i-layout-tabs-title'
}, slot);
},
handleClickTab (tabName) {
if (tabName === this.current) {
if (this.tabsReload) {
this.$emit('on-reload');
}
} else {
const page = this.opened.find(page => page.fullPath === tabName);
const { name, params, query } = page;
if (page) this.$router.push({ name, params, query }, () => {});
}
},
handleClickClose (tagName) {
this.close({
tagName
});
},
handleScroll () {
if (this.tabsFix && !this.headerFix) {
const scrollTop = document.body.scrollTop + document.documentElement.scrollTop;
this.scrollTop = scrollTop > 64 ? 64 : scrollTop;
}
},
handleClose (name) {
const params = {
pageSelect: this.current
};
switch (name) {
case 'left':
this.closeLeft(params);
break;
case 'right':
this.closeRight(params);
break;
case 'other':
this.closeOther(params);
break;
case 'all':
this.closeAll();
break;
}
}
},
mounted () {
document.addEventListener('scroll', this.handleScroll, { passive: true });
this.handleScroll();
},
beforeDestroy () {
document.removeEventListener('scroll', this.handleScroll);
}
}
</script>
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
// 生成随机字符串
export default function (len = 32) {
const $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
const maxPos = $chars.length;
let str = '';
for (let i = 0; i < len; i++) {
str += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return str;
}
/**
* 系统内置方法集,正常情况下您不应该修改或移除此文件
* */
import { cloneDeep } from 'lodash';
/**
* @description 根据当前路由,找打顶部菜单名称
* @param {String} currentPath 当前路径
* @param {Array} menuList 所有路径
* */
function getHeaderName (currentPath, menuList) {
const allMenus = [];
menuList.forEach(menu => {
const headerName = menu.header || '';
const menus = transferMenu(menu, headerName);
allMenus.push({
path: menu.path,
header: headerName
});
menus.forEach(item => allMenus.push(item));
});
const currentMenu = allMenus.find(item => item.path === currentPath);
return currentMenu ? currentMenu.header : null;
}
function transferMenu (menu, headerName) {
if (menu.children && menu.children.length) {
return menu.children.reduce((all, item) => {
all.push({
path: item.path,
header: headerName
});
const foundChildren = transferMenu(item, headerName);
return all.concat(foundChildren);
}, []);
} else {
return [menu];
}
}
export { getHeaderName };
/**
* @description 根据当前顶栏菜单 name,找到对应的二级菜单
* @param {Array} menuList 所有的二级菜单
* @param {String} headerName 当前顶栏菜单的 name
* */
function getMenuSider (menuList, headerName = '') {
if (headerName) {
return menuList.filter(item => item.header === headerName);
} else {
return menuList;
}
}
export { getMenuSider };
/**
* @description 根据当前路由,找到其所有父菜单 path,作为展开侧边栏 open-names 依据
* @param {String} currentPath 当前路径
* @param {Array} menuList 所有路径
* */
function getSiderSubmenu (currentPath, menuList) {
const allMenus = [];
menuList.forEach(menu => {
const menus = transferSubMenu(menu, []);
allMenus.push({
path: menu.path,
openNames: []
});
menus.forEach(item => allMenus.push(item));
});
const currentMenu = allMenus.find(item => item.path === currentPath);
return currentMenu ? currentMenu.openNames : [];
}
function transferSubMenu (menu, openNames) {
if (menu.children && menu.children.length) {
const itemOpenNames = openNames.concat([menu.path]);
return menu.children.reduce((all, item) => {
all.push({
path: item.path,
openNames: itemOpenNames
});
const foundChildren = transferSubMenu(item, itemOpenNames);
return all.concat(foundChildren);
}, []);
} else {
return [menu].map(item => {
return {
path: item.path,
openNames: openNames
}
});
}
}
export { getSiderSubmenu };
/**
* @description 递归获取所有子菜单
* */
function getAllSiderMenu (menuList) {
let allMenus = [];
menuList.forEach(menu => {
if (menu.children && menu.children.length) {
const menus = getMenuChildren(menu);
menus.forEach(item => allMenus.push(item));
} else {
allMenus.push(menu);
}
});
return allMenus;
}
function getMenuChildren (menu) {
if (menu.children && menu.children.length) {
return menu.children.reduce((all, item) => {
const foundChildren = getMenuChildren(item);
return all.concat(foundChildren);
}, []);
} else {
return [menu];
}
}
export { getAllSiderMenu };
/**
* @description 将菜单转为平级
* */
function flattenSiderMenu (menuList, newList) {
menuList.forEach(menu => {
let newMenu = {};
for (let i in menu) {
if (i !== 'children') newMenu[i] = cloneDeep(menu[i]);
}
newList.push(newMenu);
menu.children && flattenSiderMenu(menu.children, newList);
});
return newList;
}
export { flattenSiderMenu };
/**
* @description 判断列表1中是否包含了列表2中的某一项
* 因为用户权限 access 为数组,includes 方法无法直接得出结论
* */
function includeArray (list1, list2) {
let status = false;
list2.forEach(item => {
if (list1.includes(item)) status = true;
});
return status;
}
export { includeArray };
import Cookies from 'js-cookie';
import Setting from '@/setting';
const cookies = {};
/**
* @description 存储 cookie 值
* @param {String} name cookie name
* @param {String} value cookie value
* @param {Object} cookieSetting cookie setting
*/
cookies.set = function (name = 'default', value = '', cookieSetting = {}) {
let currentCookieSetting = {
expires: Setting.cookiesExpires
};
Object.assign(currentCookieSetting, cookieSetting);
Cookies.set(`admin-${name}`, value, currentCookieSetting);
};
/**
* @description 拿到 cookie 值
* @param {String} name cookie name
*/
cookies.get = function (name = 'default') {
return Cookies.get(`admin-${name}`);
};
/**
* @description 拿到 cookie 全部的值
*/
cookies.getAll = function () {
return Cookies.get();
};
/**
* @description 删除 cookie
* @param {String} name cookie name
*/
cookies.remove = function (name = 'default') {
return Cookies.remove(`admin-${name}`);
};
export default cookies;
import low from 'lowdb';
import LocalStorage from 'lowdb/adapters/LocalStorage';
const adapter = new LocalStorage('admin');
const db = low(adapter);
db
.defaults({
sys: {},
database: {}
})
.write();
export default db;
import cookies from './util.cookies';
import log from './util.log';
import db from './util.db';
import Setting from '@/setting';
const util = {
cookies,
log,
db
};
function tTitle (title = '') {
if (window && window.$t) {
if (title.indexOf('$t:') === 0) {
return window.$t(title.split('$t:')[1]);
} else {
return title;
}
} else {
return title;
}
}
/**
* @description 更改标题
* @param {Object} title 标题
* @param {Object} count 未读消息数提示(可视情况选择使用或不使用)
*/
util.title = function ({ title, count }) {
title = tTitle(title);
let fullTitle = title ? `${title} - ${Setting.titleSuffix}` : Setting.titleSuffix;
if (count) fullTitle = `(${count}条消息)${fullTitle}`;
window.document.title = fullTitle;
};
function requestAnimation (task) {
if ('requestAnimationFrame' in window) {
return window.requestAnimationFrame(task);
}
setTimeout(task, 16);
}
export { requestAnimation };
export default util;
const log = {};
/**
* @description 返回这个样式的颜色值
* @param {String} type 样式名称 [ primary | success | warning | error | text ]
*/
function typeColor (type = 'default') {
let color = '';
switch (type) {
case 'default': color = '#515a6e'; break;
case 'primary': color = '#2d8cf0'; break;
case 'success': color = '#19be6b'; break;
case 'warning': color = '#ff9900'; break;
case 'error': color = '#ed4014'; break;
default:; break
}
return color;
}
/**
* @description 打印一个 [ title | text ] 样式的信息
* @param {String} title title text
* @param {String} info info text
* @param {String} type style
*/
log.capsule = function (title, info, type = 'primary') {
console.log(
`%c ${title} %c ${info} %c`,
'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;',
`background:${typeColor(type)}; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;`,
'background:transparent'
);
};
/**
* @description 打印彩色文字
*/
log.colorful = function (textArr) {
console.log(
`%c${textArr.map(t => t.text || '').join('%c')}`,
...textArr.map(t => `color: ${typeColor(t.type)};`)
);
};
/**
* @description 打印 default 样式的文字
*/
log.default = function (text) {
log.colorful([{ text }]);
};
/**
* @description 打印 primary 样式的文字
*/
log.primary = function (text) {
log.colorful([{ text, type: 'primary' }]);
};
/**
* @description 打印 success 样式的文字
*/
log.success = function (text) {
log.colorful([{ text, type: 'success' }]);
};
/**
* @description 打印 warning 样式的文字
*/
log.warning = function (text) {
log.colorful([{ text, type: 'warning' }]);
};
/**
* @description 打印 error 样式的文字
*/
log.error = function (text) {
log.colorful([{ text, type: 'error' }]);
};
export default log;
// 菜单,顶栏
export default [
{
path: '/',
title: '首页',
icon: 'md-home',
hideSider: false,
name: 'home'
},
{
path: '/log',
title: '日志',
icon: 'md-locate',
hideSider: true,
name: 'system'
}
];
const pre = '/dashboard/';
export default {
path: '/dashboard',
title: 'Dashboard',
header: 'home',
icon: 'md-speedometer',
children: [
{
path: `${pre}console`,
title: '主控台'
}
]
}
export default {
path: '/log',
title: '前端日志',
header: 'system',
icon: 'md-locate'
}
// 菜单,侧边栏
import dashboard from './modules/dashboard';
// 系统
import log from './modules/log';
export default [
dashboard,
log
];
/**
* 通用混合
* */
export default {
methods: {
// 当 $route 更新时触发
appRouteChange (to, from) {
}
}
}
import Mock from 'mockjs';
import qs from 'qs';
import withCredentials from './patch/withCredentials';
/* 补丁 */
withCredentials(Mock);
/* Mock 默认配置 */
Mock.setup({ timeout: '200-300' });
/* 扩展 [生成器] */
const Generator = (prop, template) => {
const obj = {};
obj[prop] = [template];
return Mock.mock(obj);
};
/* 扩展 [循环] */
const Repeat = (num, itemTemplate) => Generator(`data|${num}`, itemTemplate).data;
const CustomExtends = {
Generator,
Repeat,
Mock,
Random: Mock.Random
};
const extend = (prop, value) => {
CustomExtends[prop] = value;
};
/* 装配配置组 */
const wired = ({ url, type, body }) => ({
method: type,
params: qs.parse(url.split('?').length > 1 ? url.split('?')[1] : ''),
body: JSON.parse(body),
url: qs.parse(url.split('?')[0]),
...CustomExtends
});
const setup = (path, method, handle) => {
Mock.mock(
RegExp(path),
method,
typeof handle === 'function' ? o => handle(wired(o)) : handle
)
};
const load = (collection) => {
collection.map(({ path, method, handle }) => {
if (method === '*') {
method = [
'get',
'head',
'post',
'put',
'delete',
'connect',
'options',
'trace',
'patch'
]
}
if (typeof method === 'string' && method.indexOf('|') > -1) method = method.split('|')
if (method instanceof Array) {
method.map(item => setup(path, item, handle))
} else {
setup(path, method, handle)
}
})
};
export default { setup, load, extend };
export default function (Mock) {
// http://cnine.me/note/FrontEnd/mock-lose-cookies-dbg.html
Mock.XHR.prototype.__send = Mock.XHR.prototype.send;
Mock.XHR.prototype.send = function () {
if (this.custom.xhr) this.custom.xhr.withCredentials = this.withCredentials || false;
this.__send.apply(this, arguments);
}
}
const userDB = [
{
username: 'admin',
password: 'admin',
uuid: 'admin-uuid',
info: {
name: 'Aresn',
avatar: 'https://dev-file.iviewui.com/userinfoPDvn9gKWYihR24SpgC319vXY8qniCqj4/avatar',
access: ['admin']
}
}
];
export default [
{
path: '/api/login',
method: 'post',
handle ({ body }) {
const user = userDB.find(e => e.username === body.username && e.password === body.password);
if (user) {
return {
code: 0,
msg: '登录成功',
data: {
...user,
token: 'A68NUPaXVBJYRStwvd9frcUn8rlf30h6'
}
}
} else {
return {
code: 401,
msg: '用户名或密码错误',
data: {}
}
}
}
},
{
path: '/api/register',
method: 'post',
handle ({ body }) {
return {
code: 0,
msg: '注册成功',
data: {
username: 'admin',
uuid: 'admin-uuid',
info: {
name: 'Aresn',
avatar: 'https://dev-file.iviewui.com/userinfoPDvn9gKWYihR24SpgC319vXY8qniCqj4/avatar',
access: ['admin']
},
token: 'A68NUPaXVBJYRStwvd9frcUn8rlf30h6'
}
}
}
}
]
import adminMock from './admin-mock';
const req = context => context.keys().map(context);
const options = req(require.context('./api/', true, /\.js$/))
.filter(e => e.default)
.map(e => e.default);
options.forEach(option => {
adminMock.load(option);
});
<template>
<div class="page-account">
<div v-if="showI18n" class="page-account-header">
<i-header-i18n />
</div>
<div class="page-account-container">
<div class="page-account-top">
<div class="page-account-top-logo">
<img src="@/assets/images/logo.png" alt="logo">
</div>
<div class="page-account-top-desc">iView Admin Pro 企业级中台前端/设计解决方案</div>
</div>
<Login @on-submit="handleSubmit">
<UserName name="username" value="admin" />
<Password name="password" value="admin" enter-to-submit />
<div class="page-account-auto-login">
<Checkbox v-model="autoLogin" size="large">{{ $t('page.login.remember') }}</Checkbox>
<a href="">{{ $t('page.login.forgot') }}</a>
</div>
<Submit>{{ $t('page.login.submit') }}</Submit>
</Login>
<div class="page-account-other">
<span>{{ $t('page.login.other') }}</span>
<img src="@/assets/svg/icon-social-wechat.svg" alt="wechat">
<img src="@/assets/svg/icon-social-qq.svg" alt="qq">
<img src="@/assets/svg/icon-social-weibo.svg" alt="weibo">
<router-link class="page-account-register" :to="{ name: 'register' }">{{ $t('page.login.signup') }}</router-link>
</div>
</div>
<i-copyright />
</div>
</template>
<script>
import iCopyright from '@/components/copyright';
import { mapActions } from 'vuex';
import mixins from '../mixins';
export default {
mixins: [ mixins ],
components: { iCopyright },
data () {
return {
autoLogin: true
}
},
methods: {
...mapActions('admin/account', [
'login'
]),
/**
* @description 登录
* 表单校验已有 iView Pro 自动完成,如有需要修改,请阅读 iView Pro 文档
*/
handleSubmit (valid, values) {
if (valid) {
const { username, password } = values;
this.login({
username,
password
})
.then(() => {
// 重定向对象不存在则返回顶层路径
this.$router.replace(this.$route.query.redirect || '/');
});
}
}
}
};
</script>
import iHeaderI18n from '@/layouts/basic-layout/header-i18n';
import { mapState } from 'vuex';
export default {
components: { iHeaderI18n },
computed: {
...mapState('admin/layout', [
'showI18n'
])
}
}
<template>
<div class="page-account">
<div v-if="showI18n" class="page-account-header">
<i-header-i18n />
</div>
<div class="page-account-container">
<div class="page-account-top">
<div class="page-account-top-logo">
<img src="@/assets/images/logo.png" alt="logo">
</div>
<div class="page-account-top-desc">iView Admin Pro 企业级中台前端/设计解决方案</div>
</div>
<Login ref="form" @on-submit="handleSubmit">
<Email name="mail" />
<Poptip trigger="focus" placement="right" width="240">
<Password name="password" :rules="passwordRule" placeholder="至少6位密码,区分大小写" @on-change="handleChangePassword" />
<div slot="content" class="page-account-register-tip">
<div class="page-account-register-tip-title" :class="passwordTip.class">
强度:{{ passwordTip.strong }}
</div>
<Progress :percent="passwordTip.percent" hide-info :stroke-width="6" :stroke-color="passwordTip.color" />
<div class="page-account-register-tip-desc">
请至少输入 6 个字符。请不要使用容易被猜到的密码。
</div>
</div>
</Poptip>
<Password name="passwordConfirm" :rules="passwordConfirmRule" placeholder="确认密码" />
<Mobile name="mobile" />
<Captcha name="captcha" :field="['mobile']" enter-to-submit @on-get-captcha="handleGetCaptcha" />
<Submit>{{ $t('page.register.submit') }}</Submit>
</Login>
<div class="page-account-to-login">
<router-link :to="{ name: 'login' }">{{ $t('page.register.other') }}</router-link>
</div>
</div>
<i-copyright />
</div>
</template>
<script>
import iCopyright from '@/components/copyright';
import { mapActions } from 'vuex';
import mixins from '../mixins';
export default {
mixins: [ mixins ],
components: { iCopyright },
data () {
// 二次校验密码
// 因为 iView Pro 的表单控件省去了对数据的绑定,因此需要通过 ref 从 Login 组件中获取数据
// 下面的 formValidate.password 中的 password,指的是给 <Password> 组件设置的 name="password"
const validatePassCheck = (rule, value, callback) => {
if (value !== this.$refs.form.formValidate.password) {
callback(new Error('两次输入的密码不匹配!'));
} else {
callback();
}
};
return {
passwordRule: [
{
required: true, message: '密码不能为空!', trigger: 'change'
},
{
min: 6, message: '密码不能少于6位!', trigger: 'change'
}
],
passwordConfirmRule: [
{
required: true, message: '确认密码不能为空!', trigger: 'change'
},
{ validator: validatePassCheck, trigger: 'change' }
],
// 密码长度,在密码强度提示时作为判断依据
passwordLen: 0
}
},
computed: {
// 密码强度提示文案等
passwordTip () {
let strong = '强';
let className = 'strong';
let percent = this.passwordLen > 10 ? 10 : this.passwordLen;
let color = '#19be6b';
if (this.passwordLen < 6) {
strong = '太短';
className = 'low';
color = '#ed4014';
} else if (this.passwordLen < 10) {
strong = '中';
className = 'medium';
color = '#ff9900';
}
return {
strong,
class: `page-account-register-tip-${className}`,
percent: percent * 10,
color
}
}
},
methods: {
...mapActions('admin/account', [
'register',
'login'
]),
handleChangePassword (val) {
this.passwordLen = val.length;
},
/**
* @description 注册
* 表单校验已有 iView Pro 自动完成,如有需要修改,请阅读 iView Pro 文档
*/
handleSubmit (valid, values) {
if (valid) {
if (valid) {
const { mail, password, mobile, captcha } = values;
this.register({
mail,
password,
mobile,
captcha
})
.then(() => {
this.$router.replace({ name: 'register-result' });
});
}
}
},
/**
* @description 获取验证码
* */
handleGetCaptcha () {
}
}
};
</script>
<template>
<div class="page-account">
<div class="page-account-container page-account-container-result">
<div class="page-account-top">
<div class="page-account-top-logo">
<img src="@/assets/images/logo.png" alt="logo">
</div>
<div class="page-account-top-desc">iView Admin Pro 企业级中台前端/设计解决方案</div>
</div>
<Result type="success" :title="title" :desc="desc">
<div slot="actions">
<Button size="large" type="primary" to="https://mail.qq.com" target="_blank">查看邮箱</Button>
<Button size="large" to="/">返回首页</Button>
</div>
</Result>
</div>
<i-copyright />
</div>
</template>
<script>
import iCopyright from '@/components/copyright';
export default {
components: { iCopyright },
data () {
return {
title: '你的账户:admin 注册成功',
desc: '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。'
}
}
};
</script>
<template>
<div>
欢迎使用 iView Admin Pro
</div>
</template>
<script>
export default {
name: 'dashboard-console'
}
</script>
<template>
<div>
<Exception type="403" img-color :desc="$t('page.exception.e403')" :back-text="$t('page.exception.btn')" />
</div>
</template>
<script>
</script>
<template>
<div>
<Exception type="404" img-color :desc="$t('page.exception.e404')" :back-text="$t('page.exception.btn')" />
</div>
</template>
<script>
</script>
<template>
<div>
<Exception type="500" img-color :desc="$t('page.exception.e500')" :back-text="$t('page.exception.btn')" />
</div>
</template>
<script>
</script>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment