Full Code of lin-xin/vue-manage-system for AI

master 6a7019ec1a74 cached
76 files
228.2 KB
88.9k tokens
26 symbols
1 requests
Download .txt
Showing preview only (246K chars total). Download the full file or copy to clipboard to get everything.
Repository: lin-xin/vue-manage-system
Branch: master
Commit: 6a7019ec1a74
Files: 76
Total size: 228.2 KB

Directory structure:
gitextract_omct5e3d/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── auto-imports.d.ts
├── components.d.ts
├── index.html
├── package.json
├── public/
│   ├── mock/
│   │   ├── role.json
│   │   ├── table.json
│   │   └── user.json
│   └── template.xlsx
├── src/
│   ├── App.vue
│   ├── api/
│   │   └── index.ts
│   ├── assets/
│   │   └── css/
│   │       ├── icon.css
│   │       └── main.css
│   ├── components/
│   │   ├── countup.vue
│   │   ├── header.vue
│   │   ├── menu.ts
│   │   ├── sidebar.vue
│   │   ├── table-custom.vue
│   │   ├── table-detail.vue
│   │   ├── table-edit.vue
│   │   ├── table-search.vue
│   │   └── tabs.vue
│   ├── main.ts
│   ├── router/
│   │   └── index.ts
│   ├── store/
│   │   ├── permiss.ts
│   │   ├── sidebar.ts
│   │   ├── tabs.ts
│   │   └── theme.ts
│   ├── types/
│   │   ├── form-option.ts
│   │   ├── menu.ts
│   │   ├── role.ts
│   │   ├── table.ts
│   │   └── user.ts
│   ├── utils/
│   │   ├── china.ts
│   │   ├── index.ts
│   │   └── request.ts
│   ├── views/
│   │   ├── chart/
│   │   │   ├── echarts.vue
│   │   │   ├── options.ts
│   │   │   └── schart.vue
│   │   ├── dashboard.vue
│   │   ├── element/
│   │   │   ├── calendar.vue
│   │   │   ├── carousel.vue
│   │   │   ├── form.vue
│   │   │   ├── statistic.vue
│   │   │   ├── steps.vue
│   │   │   ├── tabs.vue
│   │   │   ├── tour.vue
│   │   │   ├── upload.vue
│   │   │   └── watermark.vue
│   │   ├── home.vue
│   │   ├── pages/
│   │   │   ├── 403.vue
│   │   │   ├── 404.vue
│   │   │   ├── editor.vue
│   │   │   ├── icon.vue
│   │   │   ├── login.vue
│   │   │   ├── markdown.vue
│   │   │   ├── register.vue
│   │   │   ├── reset-pwd.vue
│   │   │   ├── theme.vue
│   │   │   └── ucenter.vue
│   │   ├── system/
│   │   │   ├── menu.vue
│   │   │   ├── role-permission.vue
│   │   │   ├── role.vue
│   │   │   └── user.vue
│   │   └── table/
│   │       ├── basetable.vue
│   │       ├── export.vue
│   │       ├── import.vue
│   │       └── table-editor.vue
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://lin-xin.github.io/images/weixin.jpg


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/dist


# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2016-2023 vue-manage-system

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# vue-manage-system

  <a href="https://github.com/lin-xin/vue-manage-system/releases">
    <img src="https://img.shields.io/github/release/lin-xin/vue-manage-system.svg" alt="GitHub release">
  </a>
   <a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
    <img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
  </a>

基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上演示](https://lin-xin.github.io/example/vue-manage-system/)

> Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0),带后台功能请看 [tsrpc-manage-system](https://github.com/lin-xin/tsrpc-manage-system)

[文档地址](https://lin-xin.github.io/example/vuems-doc/)
[English document](https://github.com/lin-xin/manage-system/blob/master/README_EN.md)

## 赞助商

### 好问

[<img src="https://static.bestqa.net/logo/bestqa_haowen.png" width="220" height="100">](https://www.bestqa.net/home/index.html)

专业问卷服务,一对一客服,按需定制

## 支持作者

请作者喝杯咖啡吧!(微信号:linxin_20)

![微信扫一扫](https://lin-xin.github.io/images/weixin.jpg)

## 前言

该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统开发。基于 Vue3 + pinia + typescript,引用 Element Plus 组件库,方便开发。实现逻辑简单,适合外包项目,快速交付。

## 功能

-   [x] Element Plus
-   [x] vite 3
-   [x] pinia
-   [x] typescript
-   [x] 登录/注册
-   [x] Dashboard
-   [x] 表格/表单
-   [x] 图表 :bar_chart:
-   [x] 富文本/markdown 编辑器
-   [x] 图片拖拽/裁剪上传
-   [x] 权限管理
-   [x] 三级菜单
-   [x] 自定义图标
-   [x] 主题切换

## 安装步骤

> 因为使用 vite3,node 版本需要 14.18+

```
git clone https://github.com/lin-xin/vue-manage-system.git      // 把模板下载到本地
cd vue-manage-system    // 进入模板目录
npm install         // 安装项目依赖,等待安装完成之后,安装失败可用 cnpm 或 yarn

// 运行
npm run dev

// 执行构建命令,生成的dist文件夹放在服务器下即可访问
npm run build
```

## 项目截图

### 首页

![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)

### 登录

![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)

## License

[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)


================================================
FILE: README_EN.md
================================================
# vue-manage-system

<a href="https://github.com/vuejs/vue">
    <img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
  </a>
  <a href="https://github.com/ElemeFE/element">
    <img src="https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg" alt="element-ui">
  </a>
  <a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
    <img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
  </a>
  <a href="https://github.com/lin-xin/vue-manage-system/releases">
    <img src="https://img.shields.io/github/release/lin-xin/vue-manage-system.svg" alt="GitHub release">
  </a>
  <a href="https://lin-xin.gitee.io/example/work/#/donate">
    <img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
  </a>

The web management system solution based on Vue3 and ElementPlus。[live demo](https://lin-xin.gitee.io/example/work/)

Please check the version of vue2 in [tag V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)

## Donation

![WeChat](https://lin-xin.gitee.io/images/weixin.jpg)

## Preface

The scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue3 and ElementPlus. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.

## Function

-   [x] Element-UI
-   [x] Login/Logout
-   [x] Dashboard
-   [x] Table
-   [x] Tabs
-   [x] From
-   [x] Chart :bar_chart:
-   [x] Editor
-   [x] Markdown
-   [x] Upload pictures by clipping or dragging
-   [x] Permission
-   [x] Three level menu
-   [x] Custom icon

## Installation steps

    git clone https://github.com/lin-xin/vue-manage-system.git		// Clone templates
    cd vue-manage-system											// Enter template directory
    npm install													// Installation dependency

## Local development

    npm run dev

## Constructing production

    // Constructing project
    npm run build

## Component description and presentation

### vue-schart

Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/lin-xin/vue-schart#/)

```html
<template>
    <div>
        <schart class="wrapper" canvasId="myCanvas" :options="options"></schart>
    </div>
</template>
<script setup>
import { ref } from 'vue';
import Schart from "vue-schart"; // 导入Schart组件
const options = ref({
    type: "bar",
    title: {
        text: "最近一周各品类销售图",
    },
    labels: ["周一", "周二", "周三", "周四", "周五"],
    datasets: [
        {
            label: "家电",
            data: [234, 278, 270, 190, 230],
        },
        {
            label: "百货",
            data: [164, 178, 190, 135, 160],
        },
        {
            label: "食品",
            data: [144, 198, 150, 235, 120],
        },
    ],
})
</script>
<style>
    .wrapper {
        width: 7rem;
        height: 5rem;
    }
</style>
```

## Screenshot

### Default theme

![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)

### Login

![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms3.png)

## License

[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)


================================================
FILE: auto-imports.d.ts
================================================
// Generated by 'unplugin-auto-import'
export {}
declare global {

}


================================================
FILE: components.d.ts
================================================
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'

export {}

declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    Countup: typeof import('./src/components/countup.vue')['default']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCalendar: typeof import('element-plus/es')['ElCalendar']
    ElCard: typeof import('element-plus/es')['ElCard']
    ElCarousel: typeof import('element-plus/es')['ElCarousel']
    ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
    ElCascader: typeof import('element-plus/es')['ElCascader']
    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
    ElCountdown: typeof import('element-plus/es')['ElCountdown']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDivider: typeof import('element-plus/es')['ElDivider']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
    ElForm: typeof import('element-plus/es')['ElForm']
    ElFormItem: typeof import('element-plus/es')['ElFormItem']
    ElIcon: typeof import('element-plus/es')['ElIcon']
    ElImage: typeof import('element-plus/es')['ElImage']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
    ElLink: typeof import('element-plus/es')['ElLink']
    ElMenu: typeof import('element-plus/es')['ElMenu']
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElProgress: typeof import('element-plus/es')['ElProgress']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRate: typeof import('element-plus/es')['ElRate']
    ElResult: typeof import('element-plus/es')['ElResult']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElSlider: typeof import('element-plus/es')['ElSlider']
    ElSpace: typeof import('element-plus/es')['ElSpace']
    ElStatistic: typeof import('element-plus/es')['ElStatistic']
    ElStep: typeof import('element-plus/es')['ElStep']
    ElSteps: typeof import('element-plus/es')['ElSteps']
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElTimeline: typeof import('element-plus/es')['ElTimeline']
    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
    ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTour: typeof import('element-plus/es')['ElTour']
    ElTourStep: typeof import('element-plus/es')['ElTourStep']
    ElTransfer: typeof import('element-plus/es')['ElTransfer']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    ElWatermark: typeof import('element-plus/es')['ElWatermark']
    Header: typeof import('./src/components/header.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    Sidebar: typeof import('./src/components/sidebar.vue')['default']
    TableCustom: typeof import('./src/components/table-custom.vue')['default']
    TableDetail: typeof import('./src/components/table-detail.vue')['default']
    TableEdit: typeof import('./src/components/table-edit.vue')['default']
    TableSearch: typeof import('./src/components/table-search.vue')['default']
    Tabs: typeof import('./src/components/tabs.vue')['default']
  }
}


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>vue-manage-system后台管理系统</title>
  <link rel="stylesheet" href="//at.alicdn.com/t/c/font_830376_92o68tc95je.css">
</head>

<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
        Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <script type="module" src="/src/main.ts"></script>
  <!-- built files will be auto injected -->
</body>

</html>

================================================
FILE: package.json
================================================
{
	"name": "vue-manage-system",
	"version": "5.5.0",
	"private": true,
	"scripts": {
		"dev": "vite",
		"build": "vue-tsc --noEmit && vite build",
		"serve": "vite preview"
	},
	"dependencies": {
		"@element-plus/icons-vue": "*",
		"@wangeditor/editor": "^5.1.23",
		"@wangeditor/editor-for-vue": "^5.1.12",
		"axios": "^1.6.3",
		"countup.js": "^2.8.0",
		"echarts": "^5.5.0",
		"echarts-wordcloud": "^2.1.0",
		"element-plus": "^2.6.3",
		"md-editor-v3": "^2.11.2",
		"nprogress": "^0.2.0",
		"pinia": "^2.1.7",
		"vue": "^3.4.5",
		"vue-cropper": "1.1.1",
		"vue-echarts": "^6.6.9",
		"vue-router": "^4.2.5",
		"vue-schart": "^2.0.0",
		"xlsx": "^0.18.5"
	},
	"devDependencies": {
		"@vitejs/plugin-vue": "^3.0.0",
		"@vue/compiler-sfc": "^3.1.2",
		"typescript": "^4.6.4",
		"unplugin-auto-import": "^0.11.2",
		"unplugin-vue-components": "^0.22.4",
		"vite": "^3.0.0",
		"vite-plugin-vue-setup-extend": "^0.4.0",
		"vue-tsc": "^0.38.4"
	},
	"browserslist": [
		"> 1%",
		"last 2 versions",
		"not dead"
	]
}


================================================
FILE: public/mock/role.json
================================================
{
    "list": [
        {
            "id": 1,
            "name": "管理员",
            "key": "admin",
            "status": true,
            "permiss": [
                "0",
                "1",
                "11",
                "12",
                "13",
                "2",
                "21",
                "22",
                "23",
                "24",
                "3",
                "31",
                "32",
                "33",
                "331",
                "332",
                "4",
                "41",
                "42",
                "5"
            ]
        },
        {
            "id": 2,
            "name": "普通用户",
            "key": "user",
            "status": true,
            "permiss": [
                "0",
                "1",
                "11",
                "12",
                "13"
            ]
        }
    ],
    "pageTotal": 2
}

================================================
FILE: public/mock/table.json
================================================
{
    "list": [
        {
            "id": 1,
            "name": "张三",
            "money": 123,
            "address": "广东省东莞市长安镇",
            "state": true,
            "date": "2019-11-1",
            "thumb": "https://lin-xin.gitee.io/images/post/wms.png"
        },
        {
            "id": 2,
            "name": "李四",
            "money": 456,
            "address": "广东省广州市白云区",
            "state": true,
            "date": "2019-10-11",
            "thumb": "https://lin-xin.gitee.io/images/post/node3.png"
        },
        {
            "id": 3,
            "name": "王五",
            "money": 789,
            "address": "湖南省长沙市",
            "state": false,
            "date": "2019-11-11",
            "thumb": "https://lin-xin.gitee.io/images/post/parcel.png"
        },
        {
            "id": 4,
            "name": "赵六",
            "money": 1011,
            "address": "福建省厦门市鼓浪屿",
            "state": true,
            "date": "2019-10-20",
            "thumb": "https://lin-xin.gitee.io/images/post/notice.png"
        }
    ],
    "pageTotal": 4
}

================================================
FILE: public/mock/user.json
================================================
{
    "list": [
        {
            "id": 1,
            "name": "张三",
            "password": "123",
            "email": "123@qq.com",
            "phone": "12345678944",
            "date": "2024-01-01",
            "role": "管理员"
        },
        {
            "id": 2,
            "name": "李四",
            "password": "123",
            "email": "1234@qq.com",
            "phone": "12345678945",
            "date": "2024-01-01",
            "role": "普通用户"
        }
    ],
    "pageTotal": 2
}

================================================
FILE: src/App.vue
================================================
<template>
	<el-config-provider :locale="zhCn">
		<router-view />
	</el-config-provider>
</template>

<script setup lang="ts">
import { ElConfigProvider } from 'element-plus';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import { useThemeStore } from './store/theme';

const theme = useThemeStore();
theme.initTheme();
</script>
<style>
@import './assets/css/main.css';
</style>


================================================
FILE: src/api/index.ts
================================================
import request from '../utils/request';

export const fetchData = () => {
    return request({
        url: './mock/table.json',
        method: 'get'
    });
};

export const fetchUserData = () => {
    return request({
        url: './mock/user.json',
        method: 'get'
    });
};

export const fetchRoleData = () => {
    return request({
        url: './mock/role.json',
        method: 'get'
    });
};


================================================
FILE: src/assets/css/icon.css
================================================
[class*=" el-icon-lx"],
[class^=el-icon-lx] {
    font-family: lx-iconfont !important;
}

================================================
FILE: src/assets/css/main.css
================================================
* {
	margin: 0;
	padding: 0;
	outline: 0 !important;
}


body {
	font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
}

a {
	text-decoration: none;
}
i {
	font-style: normal;
}

.container {
	padding: 30px;
	background: #fff;
	border: 1px solid #ddd;
	border-radius: 5px;
}

.el-table th {
	background-color: #f5f7fa !important;
}

.plugins-tips {
	padding: 20px 10px;
	margin-bottom: 20px;
	background: #eef1f6;
}

.plugins-tips a {
	color: var(--el-color-primary);
}

.el-button + .el-tooltip {
	margin-left: 10px;
}

.mgb20 {
	margin-bottom: 20px;
}
.mgb10 {
	margin-bottom: 10px;
}
.mr10 {
	margin-right: 10px;
}

.move-enter-active,
.move-leave-active {
	transition: opacity 0.1s ease;
}

.move-enter-from,
.move-leave-to {
	opacity: 0;
}

.el-time-panel__content::after,
.el-time-panel__content::before {
	margin-top: -7px;
}

.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
	padding-bottom: 0;
}

[hidden] {
	display: none !important;
}

.flex-center {
	display: flex;
	justify-content: center;
	align-items: center;
}

:root {
	--header-bg-color: #242f42;
	--header-text-color: #fff;
	--active-color: var(--el-color-primary);
}


================================================
FILE: src/components/countup.vue
================================================
<template>
    <span ref="countRef"></span>
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { CountUp } from 'countup.js';

const props = defineProps({
    end: {
        type: Number,
        required: true,
    },
    options: {
        type: Object,
        default: () => ({}),
        required: false,
    },
});

const countRef = ref<any>(null);
let countUp: any;
onMounted(() => {
    countUp = new CountUp(countRef.value, props.end, props.options);
    if (countUp.error) {
        console.error(countUp.error);
        return;
    }
    countUp.start();
});

watch(() => props.end, (newVal) => {
    if (countUp) {
        countUp.update(newVal);
    }
});


</script>

================================================
FILE: src/components/header.vue
================================================
<template>
    <div class="header">
        <!-- 折叠按钮 -->
        <div class="header-left">
            <img class="logo" src="../assets/img/logo.svg" alt="" />
            <div class="web-title">后台管理系统</div>
            <div class="collapse-btn" @click="collapseChage">
                <el-icon v-if="sidebar.collapse">
                    <Expand />
                </el-icon>
                <el-icon v-else>
                    <Fold />
                </el-icon>
            </div>
        </div>
        <div class="header-right">
            <div class="header-user-con">
                <div class="btn-icon" @click="router.push('/theme')">
                    <el-tooltip effect="dark" content="设置主题" placement="bottom">
                        <i class="el-icon-lx-skin"></i>
                    </el-tooltip>
                </div>
                <div class="btn-icon" @click="router.push('/ucenter')">
                    <el-tooltip
                        effect="dark"
                        :content="message ? `有${message}条未读消息` : `消息中心`"
                        placement="bottom"
                    >
                        <i class="el-icon-lx-notice"></i>
                    </el-tooltip>
                    <span class="btn-bell-badge" v-if="message"></span>
                </div>
                <div class="btn-icon" @click="setFullScreen">
                    <el-tooltip effect="dark" content="全屏" placement="bottom">
                        <i class="el-icon-lx-full"></i>
                    </el-tooltip>
                </div>
                <!-- 用户头像 -->
                <el-avatar class="user-avator" :size="30" :src="imgurl" />
                <!-- 用户名下拉菜单 -->
                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
                    <span class="el-dropdown-link">
                        {{ username }}
                        <el-icon class="el-icon--right">
                            <arrow-down />
                        </el-icon>
                    </span>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
                                <el-dropdown-item>项目仓库</el-dropdown-item>
                            </a>
                            <a href="https://lin-xin.gitee.io/example/vuems-doc/" target="_blank">
                                <el-dropdown-item>官方文档</el-dropdown-item>
                            </a>
                            <el-dropdown-item command="user">个人中心</el-dropdown-item>
                            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { useSidebarStore } from '../store/sidebar';
import { useRouter } from 'vue-router';
import imgurl from '../assets/img/img.jpg';

const username: string | null = localStorage.getItem('vuems_name');
const message: number = 2;

const sidebar = useSidebarStore();
// 侧边栏折叠
const collapseChage = () => {
    sidebar.handleCollapse();
};

onMounted(() => {
    if (document.body.clientWidth < 1500) {
        collapseChage();
    }
});

// 用户名下拉菜单选择事件
const router = useRouter();
const handleCommand = (command: string) => {
    if (command == 'loginout') {
        localStorage.removeItem('vuems_name');
        router.push('/login');
    } else if (command == 'user') {
        router.push('/ucenter');
    }
};

const setFullScreen = () => {
    if (document.fullscreenElement) {
        document.exitFullscreen();
    } else {
        document.body.requestFullscreen.call(document.body);
    }
};
</script>
<style scoped>
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-sizing: border-box;
    width: 100%;
    height: 70px;
    color: var(--header-text-color);
    background-color: var(--header-bg-color);
    border-bottom: 1px solid #ddd;
}

.header-left {
    display: flex;
    align-items: center;
    padding-left: 20px;
    height: 100%;
}

.logo {
    width: 35px;
}

.web-title {
    margin: 0 40px 0 10px;
    font-size: 22px;
}

.collapse-btn {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    padding: 0 10px;
    cursor: pointer;
    opacity: 0.8;
    font-size: 22px;
}

.collapse-btn:hover {
    opacity: 1;
}

.header-right {
    float: right;
    padding-right: 50px;
}

.header-user-con {
    display: flex;
    height: 70px;
    align-items: center;
}

.btn-fullscreen {
    transform: rotate(45deg);
    margin-right: 5px;
    font-size: 24px;
}

.btn-icon {
    position: relative;
    width: 30px;
    height: 30px;
    text-align: center;
    cursor: pointer;
    display: flex;
    align-items: center;
    color: var(--header-text-color);
    margin: 0 5px;
    font-size: 20px;
}

.btn-bell-badge {
    position: absolute;
    right: 4px;
    top: 0px;
    width: 8px;
    height: 8px;
    border-radius: 4px;
    background: #f56c6c;
    color: var(--header-text-color);
}

.user-avator {
    margin: 0 10px 0 20px;
}

.el-dropdown-link {
    color: var(--header-text-color);
    cursor: pointer;
    display: flex;
    align-items: center;
}

.el-dropdown-menu__item {
    text-align: center;
}
</style>


================================================
FILE: src/components/menu.ts
================================================
import { Menus } from '@/types/menu';

export const menuData: Menus[] = [
    {
        id: '0',
        title: '系统首页',
        index: '/dashboard',
        icon: 'Odometer',
    },
    {
        id: '1',
        title: '系统管理',
        index: '1',
        icon: 'HomeFilled',
        children: [
            {
                id: '11',
                pid: '1',
                index: '/system-user',
                title: '用户管理',
            },
            {
                id: '12',
                pid: '1',
                index: '/system-role',
                title: '角色管理',
            },
            {
                id: '13',
                pid: '1',
                index: '/system-menu',
                title: '菜单管理',
            },
        ],
    },
    {
        id: '2',
        title: '组件',
        index: '2-1',
        icon: 'Calendar',
        children: [
            {
                id: '21',
                pid: '3',
                index: '/form',
                title: '表单',
            },
            {
                id: '22',
                pid: '3',
                index: '/upload',
                title: '上传',
            },
            {
                id: '23',
                pid: '2',
                index: '/carousel',
                title: '走马灯',
            },
            {
                id: '24',
                pid: '2',
                index: '/calendar',
                title: '日历',
            },
            {
                id: '25',
                pid: '2',
                index: '/watermark',
                title: '水印',
            },
            {
                id: '26',
                pid: '2',
                index: '/tour',
                title: '分布引导',
            },
            {
                id: '27',
                pid: '2',
                index: '/steps',
                title: '步骤条',
            },
            {
                id: '28',
                pid: '2',
                index: '/statistic',
                title: '统计',
            },
            {
                id: '29',
                pid: '3',
                index: '29',
                title: '三级菜单',
                children: [
                    {
                        id: '291',
                        pid: '29',
                        index: '/editor',
                        title: '富文本编辑器',
                    },
                    {
                        id: '292',
                        pid: '29',
                        index: '/markdown',
                        title: 'markdown编辑器',
                    },
                ],
            },
        ],
    },
    {
        id: '3',
        title: '表格',
        index: '3',
        icon: 'Calendar',
        children: [
            {
                id: '31',
                pid: '3',
                index: '/table',
                title: '基础表格',
            },
            {
                id: '32',
                pid: '3',
                index: '/table-editor',
                title: '可编辑表格',
            },
            {
                id: '33',
                pid: '3',
                index: '/import',
                title: '导入Excel',
            },
            {
                id: '34',
                pid: '3',
                index: '/export',
                title: '导出Excel',
            },
        ],
    },
    {
        id: '4',
        icon: 'PieChart',
        index: '4',
        title: '图表',
        children: [
            {
                id: '41',
                pid: '4',
                index: '/schart',
                title: 'schart图表',
            },
            {
                id: '42',
                pid: '4',
                index: '/echarts',
                title: 'echarts图表',
            },
        ],
    },
    {
        id: '5',
        icon: 'Guide',
        index: '/icon',
        title: '图标',
        permiss: '5',
    },
    {
        id: '7',
        icon: 'Brush',
        index: '/theme',
        title: '主题',
    },
    {
        id: '6',
        icon: 'DocumentAdd',
        index: '6',
        title: '附加页面',
        children: [
            {
                id: '61',
                pid: '6',
                index: '/ucenter',
                title: '个人中心',
            },
            {
                id: '62',
                pid: '6',
                index: '/login',
                title: '登录',
            },
            {
                id: '63',
                pid: '6',
                index: '/register',
                title: '注册',
            },
            {
                id: '64',
                pid: '6',
                index: '/reset-pwd',
                title: '重设密码',
            },
            {
                id: '65',
                pid: '6',
                index: '/403',
                title: '403',
            },
            {
                id: '66',
                pid: '6',
                index: '/404',
                title: '404',
            },
        ],
    },
];


================================================
FILE: src/components/sidebar.vue
================================================
<template>
    <div class="sidebar">
        <el-menu
            class="sidebar-el-menu"
            :default-active="onRoutes"
            :collapse="sidebar.collapse"
            :background-color="sidebar.bgColor"
            :text-color="sidebar.textColor"
            router
        >
            <template v-for="item in menuData">
                <template v-if="item.children">
                    <el-sub-menu :index="item.index" :key="item.index" v-permiss="item.id">
                        <template #title>
                            <el-icon>
                                <component :is="item.icon"></component>
                            </el-icon>
                            <span>{{ item.title }}</span>
                        </template>
                        <template v-for="subItem in item.children">
                            <el-sub-menu
                                v-if="subItem.children"
                                :index="subItem.index"
                                :key="subItem.index"
                                v-permiss="item.id"
                            >
                                <template #title>{{ subItem.title }}</template>
                                <el-menu-item
                                    v-for="(threeItem, i) in subItem.children"
                                    :key="i"
                                    :index="threeItem.index"
                                >
                                    {{ threeItem.title }}
                                </el-menu-item>
                            </el-sub-menu>
                            <el-menu-item v-else :index="subItem.index" v-permiss="item.id">
                                {{ subItem.title }}
                            </el-menu-item>
                        </template>
                    </el-sub-menu>
                </template>
                <template v-else>
                    <el-menu-item :index="item.index" :key="item.index" v-permiss="item.id">
                        <el-icon>
                            <component :is="item.icon"></component>
                        </el-icon>
                        <template #title>{{ item.title }}</template>
                    </el-menu-item>
                </template>
            </template>
        </el-menu>
    </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useSidebarStore } from '../store/sidebar';
import { useRoute } from 'vue-router';
import { menuData } from '@/components/menu';

const route = useRoute();
const onRoutes = computed(() => {
    return route.path;
});

const sidebar = useSidebarStore();
</script>

<style scoped>
.sidebar {
    display: block;
    position: absolute;
    left: 0;
    top: 70px;
    bottom: 0;
    overflow-y: scroll;
}

.sidebar::-webkit-scrollbar {
    width: 0;
}

.sidebar-el-menu:not(.el-menu--collapse) {
    width: 250px;
}

.sidebar-el-menu {
    min-height: 100%;
}
</style>


================================================
FILE: src/components/table-custom.vue
================================================
<template>
    <div>
        <div class="table-toolbar" v-if="hasToolbar">
            <div class="table-toolbar-left">
                <slot name="toolbarBtn"></slot>
            </div>
            <div class="table-toolbar-right flex-center">
                <template v-if="multipleSelection.length > 0">
                    <el-tooltip effect="dark" content="删除选中" placement="top">
                        <el-icon class="columns-setting-icon" @click="delSelection(multipleSelection)">
                            <Delete />
                        </el-icon>
                    </el-tooltip>
                    <el-divider direction="vertical" />
                </template>
                <el-tooltip effect="dark" content="刷新" placement="top">
                    <el-icon class="columns-setting-icon" @click="refresh">
                        <Refresh />
                    </el-icon>
                </el-tooltip>
                <el-divider direction="vertical" />
                <el-tooltip effect="dark" content="列设置" placement="top">
                    <el-dropdown :hide-on-click="false" size="small" trigger="click">
                        <el-icon class="columns-setting-icon">
                            <Setting />
                        </el-icon>
                        <template #dropdown>
                            <el-dropdown-menu>
                                <el-dropdown-item v-for="c in columns">
                                    <el-checkbox v-model="c.visible" :label="c.label" />
                                </el-dropdown-item>
                            </el-dropdown-menu>
                        </template>
                    </el-dropdown>
                </el-tooltip>
            </div>
        </div>
        <el-table class="mgb20" :style="{ width: '100%' }" border :data="tableData" :row-key="rowKey"
            @selection-change="handleSelectionChange" table-layout="auto">
            <template v-for="item in columns" :key="item.prop">
                <el-table-column v-if="item.visible" :prop="item.prop" :label="item.label" :width="item.width"
                    :type="item.type" :align="item.align || 'center'">

                    <template #default="{ row, column, $index }" v-if="item.type === 'index'">
                        {{ getIndex($index) }}
                    </template>
                    <template #default="{ row, column, $index }" v-if="!item.type">
                        <slot :name="item.prop" :rows="row" :index="$index">
                            <template v-if="item.prop == 'operator'">
                                <el-button type="warning" size="small" :icon="View" @click="viewFunc(row)">
                                    查看
                                </el-button>
                                <el-button type="primary" size="small" :icon="Edit" @click="editFunc(row)">
                                    编辑
                                </el-button>
                                <el-button type="danger" size="small" :icon="Delete" @click="handleDelete(row)">
                                    删除
                                </el-button>
                            </template>
                            <span v-else-if="item.formatter">
                                {{ item.formatter(row[item.prop]) }}
                            </span>
                            <span v-else>
                                {{ row[item.prop] }}
                            </span>
                        </slot>
                    </template>
                </el-table-column>
            </template>
        </el-table>
        <el-pagination v-if="hasPagination" :current-page="currentPage" :page-size="pageSize" :background="true"
            :layout="layout" :total="total" @current-change="handleCurrentChange" />
    </div>
</template>

<script setup lang="ts">
import { toRefs, PropType, ref } from 'vue'
import { Delete, Edit, View, Refresh } from '@element-plus/icons-vue';
import { ElMessageBox } from 'element-plus';

const props = defineProps({
    // 表格相关
    tableData: {
        type: Array,
        default: []
    },
    columns: {
        type: Array as PropType<any[]>,
        default: []
    },
    rowKey: {
        type: String,
        default: 'id'
    },
    hasToolbar: {
        type: Boolean,
        default: true
    },
    //  分页相关
    hasPagination: {
        type: Boolean,
        default: true
    },
    total: {
        type: Number,
        default: 0
    },
    currentPage: {
        type: Number,
        default: 1
    },
    pageSize: {
        type: Number,
        default: 10
    },

    layout: {
        type: String,
        default: 'total, prev, pager, next'
    },
    delFunc: {
        type: Function,
        default: () => { }
    },
    viewFunc: {
        type: Function,
        default: () => { }
    },
    editFunc: {
        type: Function,
        default: () => { }
    },
    delSelection: {
        type: Function,
        default: () => { }
    },
    refresh: {
        type: Function,
        default: () => { }
    },
    changePage: {
        type: Function,
        default: () => { }
    }
})

let {
    tableData,
    columns,
    rowKey,
    hasToolbar,
    hasPagination,
    total,
    currentPage,
    pageSize,
    layout,
} = toRefs(props)

columns.value.forEach((item) => {
    if (item.visible === undefined) {
        item.visible = true
    }
})

// 当选择项发生变化时会触发该事件
const multipleSelection = ref([])
const handleSelectionChange = (selection: any[]) => {
    multipleSelection.value = selection
}

// 当前页码变化的事件
const handleCurrentChange = (val: number) => {
    props.changePage(val)
}

const handleDelete = (row) => {
    ElMessageBox.confirm('确定要删除吗?', '提示', {
        type: 'warning'
    })
        .then(async () => {
            props.delFunc(row);
        })
        .catch(() => { });
};

const getIndex = (index: number) => {
    return index + 1 + (currentPage.value - 1) * pageSize.value
}

</script>

<style scoped>
.table-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    margin-bottom: 10px;
}

.columns-setting-icon {
    display: block;
    font-size: 18px;
    cursor: pointer;
    color: #676767;
}
</style>
<style>
.table-header .cell {
    color: #333;
}
</style>

================================================
FILE: src/components/table-detail.vue
================================================
<template>
	<el-descriptions :title="title" :column="column" border>
		<el-descriptions-item v-for="item in list" :span="item.span">
			<template #label> {{ item.label }} </template>
			<slot :name="item.prop" :rows="row">
				{{ item.value || row[item.prop] }}
			</slot>
		</el-descriptions-item>
	</el-descriptions>
</template>

<script lang="ts" setup>
const props = defineProps({
	data: {
		type: Object,
		required: true,
	}
});
const { row, title, column = 2, list } = props.data;

</script>


================================================
FILE: src/components/table-edit.vue
================================================
<template>
	<el-form ref="formRef" :model="form" :rules="rules" :label-width="options.labelWidth">
		<el-row>
			<el-col :span="options.span" v-for="item in options.list">
				<el-form-item :label="item.label" :prop="item.prop">
					<!-- 文本框、数字框、下拉框、日期框、开关、上传 -->
					<el-input v-if="item.type === 'input'" v-model="form[item.prop]" :disabled="item.disabled"
						:placeholder="item.placeholder" clearable></el-input>
					<el-input-number v-else-if="item.type === 'number'" v-model="form[item.prop]"
						:disabled="item.disabled" controls-position="right"></el-input-number>
					<el-select v-else-if="item.type === 'select'" v-model="form[item.prop]" :disabled="item.disabled"
						:placeholder="item.placeholder" clearable>
						<el-option v-for="opt in item.opts" :label="opt.label" :value="opt.value"></el-option>
					</el-select>
					<el-date-picker v-else-if="item.type === 'date'" type="date" v-model="form[item.prop]"
						:value-format="item.format"></el-date-picker>
					<el-switch v-else-if="item.type === 'switch'" v-model="form[item.prop]"
						:active-value="item.activeValue" :inactive-value="item.inactiveValue"
						:active-text="item.activeText" :inactive-text="item.inactiveText"></el-switch>
					<el-upload v-else-if="item.type === 'upload'" class="avatar-uploader" action="#"
						:show-file-list="false" :on-success="handleAvatarSuccess">
						<img v-if="form[item.prop]" :src="form[item.prop]" class="avatar" />
						<el-icon v-else class="avatar-uploader-icon">
							<Plus />
						</el-icon>
					</el-upload>
					<slot :name="item.prop" v-else>

					</slot>
				</el-form-item>
			</el-col>
		</el-row>

		<el-form-item>
			<el-button type="primary" @click="saveEdit(formRef)">保 存</el-button>
		</el-form-item>
	</el-form>
</template>

<script lang="ts" setup>
import { FormOption } from '@/types/form-option';
import { FormInstance, FormRules, UploadProps } from 'element-plus';
import { PropType, ref } from 'vue';

const { options, formData, edit, update } = defineProps({
	options: {
		type: Object as PropType<FormOption>,
		required: true
	},
	formData: {
		type: Object,
		required: true
	},
	edit: {
		type: Boolean,
		required: false
	},
	update: {
		type: Function,
		required: true
	}
});


const form = ref({ ...(edit ? formData : {}) });

const rules: FormRules = options.list.map(item => {
	if (item.required) {
		return { [item.prop]: [{ required: true, message: `${item.label}不能为空`, trigger: 'blur' }] };
	}
	return {};
}).reduce((acc, cur) => ({ ...acc, ...cur }), {});


const formRef = ref<FormInstance>();
const saveEdit = (formEl: FormInstance | undefined) => {
	if (!formEl) return;
	formEl.validate(valid => {
		if (!valid) return false;
		update(form.value);
	});
};

const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
	form.value.thumb = URL.createObjectURL(uploadFile.raw!);
};

</script>

<style>
.avatar-uploader .el-upload {
	border: 1px dashed var(--el-border-color);
	border-radius: 6px;
	cursor: pointer;
	position: relative;
	overflow: hidden;
	transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
	border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
	font-size: 28px;
	color: #8c939d;
	width: 178px;
	height: 178px;
	text-align: center;
}
</style>


================================================
FILE: src/components/table-search.vue
================================================
<template>
	<div class="search-container">
		<el-form ref="searchRef" :model="query" :inline="true">
			<el-form-item :label="item.label" :prop="item.prop" v-for="item in options">
				<!-- 文本框、下拉框、日期框 -->
				<el-input v-if="item.type === 'input'" v-model="query[item.prop]" :disabled="item.disabled"
					:placeholder="item.placeholder" clearable></el-input>
				<el-select v-else-if="item.type === 'select'" v-model="query[item.prop]" :disabled="item.disabled"
					:placeholder="item.placeholder" clearable>
					<el-option v-for="opt in item.opts" :label="opt.label" :value="opt.value"></el-option>
				</el-select>
				<el-date-picker v-else-if="item.type === 'date'" type="date" v-model="query[item.prop]"
					:value-format="item.format"></el-date-picker>
			</el-form-item>
			<el-form-item>
				<el-button type="primary" :icon="Search" @click="search">搜索</el-button>
				<el-button :icon="Refresh" @click="resetForm(searchRef)">重置</el-button>
			</el-form-item>
		</el-form>
	</div>
</template>

<script lang="ts" setup>
import { FormInstance } from 'element-plus';
import { Search, Refresh } from '@element-plus/icons-vue';
import { PropType, ref } from 'vue';
import { FormOptionList } from '@/types/form-option';

const props = defineProps({
	query: {
		type: Object,
		required: true
	},
	options: {
		type: Array as PropType<Array<FormOptionList>>,
		required: true
	},
	search: {
		type: Function,
		default: () => { }
	}
});

const searchRef = ref<FormInstance>();
const resetForm = (formEl: FormInstance | undefined) => {
	if (!formEl) return
	formEl.resetFields()
	props.search();
}
</script>

<style scoped>
.search-container {
	padding: 20px 30px 0;
	background-color: #fff;
	margin-bottom: 10px;
	border: 1px solid #ddd;
	border-radius: 5px
}
</style>


================================================
FILE: src/components/tabs.vue
================================================
<template>
    <div class="tabs-container">
        <el-tabs v-model="activePath" class="tabs" type="card" closable @tab-click="clickTabls" @tab-remove="closeTabs">
            <el-tab-pane
                v-for="item in tabs.list"
                :key="item.path"
                :label="item.title"
                :name="item.path"
                @click="setTags(item)"
            ></el-tab-pane>
        </el-tabs>
        <div class="Tabs-close-box">
            <el-dropdown @command="handleTags">
                <el-button size="small" type="primary" plain>
                    标签选项
                    <el-icon class="el-icon--right">
                        <arrow-down />
                    </el-icon>
                </el-button>
                <template #dropdown>
                    <el-dropdown-menu size="small">
                        <el-dropdown-item command="other">关闭其他</el-dropdown-item>
                        <el-dropdown-item command="current">关闭当前</el-dropdown-item>
                        <el-dropdown-item command="all">关闭所有</el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';
import { useTabsStore } from '../store/tabs';
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';

const route = useRoute();
const router = useRouter();
const activePath = ref(route.fullPath);
const tabs = useTabsStore();
// 设置标签
const setTags = (route: any) => {
    const isExist = tabs.list.some((item) => {
        return item.path === route.fullPath;
    });
    if (!isExist) {
        tabs.setTabsItem({
            name: route.name,
            title: route.meta.title,
            path: route.fullPath,
        });
    }
};
setTags(route);
onBeforeRouteUpdate((to) => {
    setTags(to);
});

// 关闭全部标签
const closeAll = () => {
    tabs.clearTabs();
    router.push('/');
};
// 关闭其他标签
const closeOther = () => {
    const curItem = tabs.list.filter((item) => {
        return item.path === route.fullPath;
    });
    tabs.closeTabsOther(curItem);
};
const handleTags = (command: string) => {
    switch (command) {
        case 'current':
            // 关闭当前页面的标签页
            tabs.closeCurrentTag({
                $router: router,
                $route: route,
            });
            break;
        case 'all':
            closeAll();
            break;

        case 'other':
            closeOther();
            break;
    }
};

const clickTabls = (item: any) => {
    router.push(item.props.name);
};
const closeTabs = (path: string) => {
    const index = tabs.list.findIndex((item) => item.path === path);
    tabs.delTabsItem(index);
    const item = tabs.list[index] || tabs.list[index - 1];
    router.push(item ? item.path : '/');
};

watch(
    () => route.fullPath,
    (newVal, oldVal) => {
        activePath.value = newVal;
    }
);
</script>

<style scss>
.tabs-container {
    position: relative;
    overflow: hidden;
    background: #fff;
    padding: 2px 120px 0 0;
}

.tabs {
    .el-tabs__header {
        margin-bottom: 0;
    }

    .el-tabs__nav {
        height: 28px;
    }

    .el-tabs__nav-next,
    .el-tabs__nav-prev {
        line-height: 32px;
    }

    &.el-tabs {
        --el-tabs-header-height: 28px;
    }
}

.Tabs-close-box {
    position: absolute;
    right: 0;
    top: 0;
    box-sizing: border-box;
    padding-top: 1px;
    text-align: center;
    width: 110px;
    height: 30px;
    background: #fff;
    box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
    z-index: 10;
}
</style>


================================================
FILE: src/main.ts
================================================
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import App from './App.vue';
import router from './router';
import { usePermissStore } from './store/permiss';
import 'element-plus/dist/index.css';
import './assets/css/icon.css';

const app = createApp(App);
app.use(createPinia());
app.use(router);

// 注册elementplus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component);
}
// 自定义权限指令
const permiss = usePermissStore();
app.directive('permiss', {
    mounted(el, binding) {
        if (binding.value && !permiss.key.includes(String(binding.value))) {
            el['hidden'] = true;
        }
    },
});

app.mount('#app');


================================================
FILE: src/router/index.ts
================================================
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import { usePermissStore } from '../store/permiss';
import Home from '../views/home.vue';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

const routes: RouteRecordRaw[] = [
    {
        path: '/',
        redirect: '/dashboard',
    },
    {
        path: '/',
        name: 'Home',
        component: Home,
        children: [
            {
                path: '/dashboard',
                name: 'dashboard',
                meta: {
                    title: '系统首页',
                    noAuth: true,
                },
                component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard.vue'),
            },
            {
                path: '/system-user',
                name: 'system-user',
                meta: {
                    title: '用户管理',
                    permiss: '11',
                },
                component: () => import(/* webpackChunkName: "system-user" */ '../views/system/user.vue'),
            },
            {
                path: '/system-role',
                name: 'system-role',
                meta: {
                    title: '角色管理',
                    permiss: '12',
                },
                component: () => import(/* webpackChunkName: "system-role" */ '../views/system/role.vue'),
            },
            {
                path: '/system-menu',
                name: 'system-menu',
                meta: {
                    title: '菜单管理',
                    permiss: '13',
                },
                component: () => import(/* webpackChunkName: "system-menu" */ '../views/system/menu.vue'),
            },
            {
                path: '/table',
                name: 'basetable',
                meta: {
                    title: '基础表格',
                    permiss: '31',
                },
                component: () => import(/* webpackChunkName: "table" */ '../views/table/basetable.vue'),
            },
            {
                path: '/table-editor',
                name: 'table-editor',
                meta: {
                    title: '可编辑表格',
                    permiss: '32',
                },
                component: () => import(/* webpackChunkName: "table-editor" */ '../views/table/table-editor.vue'),
            },
            {
                path: '/schart',
                name: 'schart',
                meta: {
                    title: 'schart图表',
                    permiss: '41',
                },
                component: () => import(/* webpackChunkName: "schart" */ '../views/chart/schart.vue'),
            },
            {
                path: '/echarts',
                name: 'echarts',
                meta: {
                    title: 'echarts图表',
                    permiss: '42',
                },
                component: () => import(/* webpackChunkName: "echarts" */ '../views/chart/echarts.vue'),
            },

            {
                path: '/icon',
                name: 'icon',
                meta: {
                    title: '图标',
                    permiss: '5',
                },
                component: () => import(/* webpackChunkName: "icon" */ '../views/pages/icon.vue'),
            },
            {
                path: '/ucenter',
                name: 'ucenter',
                meta: {
                    title: '个人中心',
                },
                component: () => import(/* webpackChunkName: "ucenter" */ '../views/pages/ucenter.vue'),
            },
            {
                path: '/editor',
                name: 'editor',
                meta: {
                    title: '富文本编辑器',
                    permiss: '291',
                },
                component: () => import(/* webpackChunkName: "editor" */ '../views/pages/editor.vue'),
            },
            {
                path: '/markdown',
                name: 'markdown',
                meta: {
                    title: 'markdown编辑器',
                    permiss: '292',
                },
                component: () => import(/* webpackChunkName: "markdown" */ '../views/pages/markdown.vue'),
            },
            {
                path: '/export',
                name: 'export',
                meta: {
                    title: '导出Excel',
                    permiss: '34',
                },
                component: () => import(/* webpackChunkName: "export" */ '../views/table/export.vue'),
            },
            {
                path: '/import',
                name: 'import',
                meta: {
                    title: '导入Excel',
                    permiss: '33',
                },
                component: () => import(/* webpackChunkName: "import" */ '../views/table/import.vue'),
            },
            {
                path: '/theme',
                name: 'theme',
                meta: {
                    title: '主题设置',
                    permiss: '7',
                },
                component: () => import(/* webpackChunkName: "theme" */ '../views/pages/theme.vue'),
            },
            {
                path: '/calendar',
                name: 'calendar',
                meta: {
                    title: '日历',
                    permiss: '24',
                },
                component: () => import(/* webpackChunkName: "calendar" */ '../views/element/calendar.vue'),
            },
            {
                path: '/watermark',
                name: 'watermark',
                meta: {
                    title: '水印',
                    permiss: '25',
                },
                component: () => import(/* webpackChunkName: "watermark" */ '../views/element/watermark.vue'),
            },
            {
                path: '/carousel',
                name: 'carousel',
                meta: {
                    title: '走马灯',
                    permiss: '23',
                },
                component: () => import(/* webpackChunkName: "carousel" */ '../views/element/carousel.vue'),
            },
            {
                path: '/tour',
                name: 'tour',
                meta: {
                    title: '分步引导',
                    permiss: '26',
                },
                component: () => import(/* webpackChunkName: "tour" */ '../views/element/tour.vue'),
            },
            {
                path: '/steps',
                name: 'steps',
                meta: {
                    title: '步骤条',
                    permiss: '27',
                },
                component: () => import(/* webpackChunkName: "steps" */ '../views/element/steps.vue'),
            },
            {
                path: '/form',
                name: 'forms',
                meta: {
                    title: '表单',
                    permiss: '21',
                },
                component: () => import(/* webpackChunkName: "form" */ '../views/element/form.vue'),
            },
            {
                path: '/upload',
                name: 'upload',
                meta: {
                    title: '上传',
                    permiss: '22',
                },
                component: () => import(/* webpackChunkName: "upload" */ '../views/element/upload.vue'),
            },
            {
                path: '/statistic',
                name: 'statistic',
                meta: {
                    title: '统计',
                    permiss: '28',
                },
                component: () => import(/* webpackChunkName: "statistic" */ '../views/element/statistic.vue'),
            },
        ],
    },
    {
        path: '/login',
        meta: {
            title: '登录',
            noAuth: true,
        },
        component: () => import(/* webpackChunkName: "login" */ '../views/pages/login.vue'),
    },
    {
        path: '/register',
        meta: {
            title: '注册',
            noAuth: true,
        },
        component: () => import(/* webpackChunkName: "register" */ '../views/pages/register.vue'),
    },
    {
        path: '/reset-pwd',
        meta: {
            title: '重置密码',
            noAuth: true,
        },
        component: () => import(/* webpackChunkName: "reset-pwd" */ '../views/pages/reset-pwd.vue'),
    },
    {
        path: '/403',
        meta: {
            title: '没有权限',
            noAuth: true,
        },
        component: () => import(/* webpackChunkName: "403" */ '../views/pages/403.vue'),
    },
    {
        path: '/404',
        meta: {
            title: '找不到页面',
            noAuth: true,
        },
        component: () => import(/* webpackChunkName: "404" */ '../views/pages/404.vue'),
    },
    { path: '/:path(.*)', redirect: '/404' },
];

const router = createRouter({
    history: createWebHashHistory(),
    routes,
});

router.beforeEach((to, from, next) => {
    NProgress.start();
    const role = localStorage.getItem('vuems_name');
    const permiss = usePermissStore();

    if (!role && to.meta.noAuth !== true) {
        next('/login');
    } else if (typeof to.meta.permiss == 'string' && !permiss.key.includes(to.meta.permiss)) {
        // 如果没有权限,则进入403
        next('/403');
    } else {
        next();
    }
});

router.afterEach(() => {
    NProgress.done();
});

export default router;


================================================
FILE: src/store/permiss.ts
================================================
import { defineStore } from 'pinia';

interface ObjectList {
    [key: string]: string[];
}

export const usePermissStore = defineStore('permiss', {
    state: () => {
        const defaultList: ObjectList = {
            admin: [
                '0',
                '1',
                '11',
                '12',
                '13',
                '2',
                '21',
                '22',
                '23',
                '24',
                '25',
                '26',
                '27',
                '28',
                '29',
                '291',
                '292',
                '3',
                '31',
                '32',
                '33',
                '34',
                '4',
                '41',
                '42',
                '5',
                '7',
                '6',
                '61',
                '62',
                '63',
                '64',
                '65',
                '66',
            ],
            user: ['0', '1', '11', '12', '13'],
        };
        const username = localStorage.getItem('vuems_name');
        console.log(username);
        return {
            key: (username == 'admin' ? defaultList.admin : defaultList.user) as string[],
            defaultList,
        };
    },
    actions: {
        handleSet(val: string[]) {
            this.key = val;
        },
    },
});


================================================
FILE: src/store/sidebar.ts
================================================
import { defineStore } from 'pinia';

export const useSidebarStore = defineStore('sidebar', {
	state: () => {
		return {
			collapse: false,
			bgColor: localStorage.getItem('sidebar-bg-color') || '#324157',
			textColor: localStorage.getItem('sidebar-text-color') || '#bfcbd9'
		};
	},
	getters: {},
	actions: {
		handleCollapse() {
			this.collapse = !this.collapse;
		},
		setBgColor(color: string) {
			this.bgColor = color;
			localStorage.setItem('sidebar-bg-color', color);
		},
		setTextColor(color: string) {
			this.textColor = color;
			localStorage.setItem('sidebar-text-color', color);
		}
	}
});


================================================
FILE: src/store/tabs.ts
================================================
import { defineStore } from 'pinia';

interface ListItem {
	name: string;
	path: string;
	title: string;
}

export const useTabsStore = defineStore('tabs', {
	state: () => {
		return {
			list: <ListItem[]>[]
		};
	},
	getters: {
		show: state => {
			return state.list.length > 0;
		},
		nameList: state => {
			return state.list.map(item => item.name);
		}
	},
	actions: {
		delTabsItem(index: number) {
			this.list.splice(index, 1);
		},
		setTabsItem(data: ListItem) {
			this.list.push(data);
		},
		clearTabs() {
			this.list = [];
		},
		closeTabsOther(data: ListItem[]) {
			this.list = data;
		},
		closeCurrentTag(data: any) {
			for (let i = 0, len = this.list.length; i < len; i++) {
				const item = this.list[i];
				if (item.path === data.$route.fullPath) {
					if (i < len - 1) {
						data.$router.push(this.list[i + 1].path);
					} else if (i > 0) {
						data.$router.push(this.list[i - 1].path);
					} else {
						data.$router.push('/');
					}
					this.list.splice(i, 1);
					break;
				}
			}
		}
	}
});


================================================
FILE: src/store/theme.ts
================================================
import { mix, setProperty } from '@/utils';
import { defineStore } from 'pinia';

export const useThemeStore = defineStore('theme', {
    state: () => {
        return {
            primary: '',
            success: '',
            warning: '',
            danger: '',
            info: '',
            headerBgColor: '#242f42',
            headerTextColor: '#fff',
        };
    },
    getters: {},
    actions: {
        initTheme() {
            ['primary', 'success', 'warning', 'danger', 'info'].forEach((type) => {
                const color = localStorage.getItem(`theme-${type}`) || '';
                if (color) {
                    this.setPropertyColor(color, type); // 设置主题色
                }
            });
            const headerBgColor = localStorage.getItem('header-bg-color');
            headerBgColor && this.setHeaderBgColor(headerBgColor);
            const headerTextColor = localStorage.getItem('header-text-color');
            headerTextColor && this.setHeaderTextColor(headerTextColor);
        },
        resetTheme() {
            ['primary', 'success', 'warning', 'danger', 'info'].forEach((type) => {
                this.setPropertyColor('', type); // 重置主题色
            });
        },
        setPropertyColor(color: string, type: string = 'primary') {
            this[type] = color;
            setProperty(`--el-color-${type}`, color);
            localStorage.setItem(`theme-${type}`, color);
            this.setThemeLight(type);
        },
        setThemeLight(type: string = 'primary') {
            [3, 5, 7, 8, 9].forEach((v) => {
                setProperty(`--el-color-${type}-light-${v}`, mix('#ffffff', this[type], v / 10));
            });
            setProperty(`--el-color-${type}-dark-2`, mix('#ffffff', this[type], 0.2));
        },
        setHeaderBgColor(color: string) {
            this.headerBgColor = color;
            setProperty('--header-bg-color', color);
            localStorage.setItem(`header-bg-color`, color);
        },
        setHeaderTextColor(color: string) {
            this.headerTextColor = color;
            setProperty('--header-text-color', color);
            localStorage.setItem(`header-text-color`, color);
        }
    }
});

================================================
FILE: src/types/form-option.ts
================================================
export interface FormOption {
    list: FormOptionList[];
    labelWidth?: number | string;
    span?: number;

}

export interface FormOptionList {
    prop: string;
    label: string;
    type: string;
    placeholder?: string;
    disabled?: boolean;
    opts?: any[];
    format?: string;
    activeValue?: any;
    inactiveValue?: any;
    activeText?: string;
    inactiveText?: string;
    required?: boolean;
}

================================================
FILE: src/types/menu.ts
================================================
export interface Menus {
    id: string;
    pid?: string;
    icon?: string;
    index: string;
    title: string;
    permiss?: string;
    children?: Menus[];
}

================================================
FILE: src/types/role.ts
================================================

export interface Role {
    id: number;
    name: string;
    key: string;
    status: boolean;
    permiss: string[]
}

================================================
FILE: src/types/table.ts
================================================
export interface TableItem {
    id: number;
    name: string;
    thumb: string;
    money: number;
    state: string;
    date: string;
    address: string;
}

================================================
FILE: src/types/user.ts
================================================

export interface User {
    id: number;
    name: string;
    password: string;
    email: string;
    phone: string;
    role: string;
    date: string;
}

export interface Register {
    username: string;
    password: string;
    email: string;
}

================================================
FILE: src/utils/china.ts
================================================
let chinaMap:any = {"type":"FeatureCollection","features":[{"id":"710000","geometry":{"type":"MultiPolygon","coordinates":[["@@°Ü¯Û","@@ƛĴÕƊÉɼģºðʀ\\ƎsÆNŌÔĚäœnÜƤɊĂǀĆĴžĤNJŨxĚĮǂƺòƌ‚–âÔ®ĮXŦţƸZûЋƕƑGđ¨ĭMó·ęcëƝɉlÝƯֹÅŃ^Ó·śŃNjƏďíåɛGɉ™¿IċããF¥ĘWǬÏĶñÄ","@@\\p|WoYG¿¥I†j@ž","@@…¡‰@ˆV^RqˆBbAŒnTXe„†žQr™©C","@@ÆEE—„kWqë Iœ"]],"encodeOffsets":[[[122886,24033],[123335,22980],[122375,24193],[122518,24117],[124427,22618]]]},"properties":{"cp":[121.509062,25.044332],"name":"台湾","childNum":5}},{"id":"130000","geometry":{"type":"MultiPolygon","coordinates":[["@@\\a“M`ǽÓnUK…Ĝēs¤­©yrý§uģŒc†JŠ»eIˆ€P]‚ªr‰ºc_ħ²G¼s`jΟnüsœľP","@@U`Ts¿mĂ","@@FŸƒ•›Oh‡đ©OŸ›iÃ`ww^ƒÌkŸ‘ÑH«ƇǤŗĺtFu…{Z}Ö@U‡´…ʚLg®¯Oı°ÃwŸ ^˜—€VbÉs‡ˆmA…ê]]w„§›RRl£‡ŭuw›N—Á`ÇFēÝčȻŽuT¡Ĺ—¯Õ¯sŗő£YªhV’ƍ£ƅnëYNgƒq¼ś¿µı²UºÝUąŽąŖóŒxV@tˆƯŒJ”]eƒR¾fe|rHA˜|h~Ėƍl§ÏŠjVë` ØoˆÅbbx³^zÃ͚¶Sj®A”yÂhðk`š«P€”ˈµEF†Û¬Y¨Ļrõqi¼‰Wi°§’б²°`[ˆÀ|ĠO@ÆxO\\tŽa\\p_Zõ^û{ġŒȧXýĪÓjùÎRb›š^λj{íděYfíÙTyމmńŵōHim½’éŅ­aVcř§ax¹XŻác‡žWU£ôãºQ¨÷Ñws¥qEH‰Ù|‰›šYQoŕÇyáĂ£MðoťÊ‰P¡mšWO¡€v†{ôvîēÜISpÌhp¨ ‘j†deŔQÖj˜X³à™Ĉ[n`Yp@Už–cM`’RKhŒEbœ”pŞlNut®Etq‚nsÁŠgA‹iú‹oH‡qCX‡”hfgu“~ϋWP½¢G^}¯ÅīGCŸÑ^ãziMáļMTÃƘrMc|O_ž¯Ŏ´|‡morDkO\\mĆJfl@c̬¢aĦtRıҙXòë¬WP{ŵǫƝ…›īÛ÷ąV×qƥV¿aȉd³B›qPBm›aËđŻģm“Å®Vйd^K‡KoŸnYg“¯Xhqa”Ldu¥•ÍpDž¡KąÅƒkĝęěhq‡}HyÓ]¹ǧ£…Í÷¿qá•gPmoeœi‰¤o^á¾ZE‡˜Y^…Ný{n•ƒOl±Í“@M’ċèk§da‹‘NaÇį¿]ø‰RiiñE‰€ū‹i„DZàUtėGylƒ}ŒÓM}€jpEC~¡FtoQi‘šHkk{ILgĽxqÈƋÄd–eVŽDJj£€J|Ådz•Ft~žKŨ¸IÆv|”‡¢r}膎onb˜}`RÎÄn°ÒdÞ²„^®’lnÐèĄlðӜ×]ªÆ}LiĂ±Ö`^°Ç¶p®đDcœŋ`–ZÔ’¶êqvFƚ†N®ĆTH®¦O’¾ŠIbÐã´BĐɢŴÆíȦp–ĐÞXR€·nndOž¤’OÀĈƒ­Qg˜µFo|gȒęSWb©osx|hYh•gŃfmÖĩnº€T̒Sp›¢dYĤ¶UĈjl’ǐpäðëx³kÛfw²Xjz~ÂqbTŠÑ„ěŨ@|oM‡’zv¢ZrÃVw¬ŧˏfŒ°ÐT€ªqŽs{Sž¯r æÝl¼ÖĞ džiGʂJ™¼lr}~K¨ŸƐÌWö€™¼œÞ°nÞoĦLš†|C~“D©|q]SvK€ÑcwpÏρ†ĿćènĪWlĄkT}¬Tpš~ƒ®Hgd„‰†˒劔ŽBVt„EÀ¢ôPĎƗè@~‚k–ü\\rÊĔÖæW_§¼F˜†´©òDòj’ˆYÈrbĞāøŀG{ƀ|¦ðrb|ÀH`pʞkv‚GpuARhÞÆǶgƊTǼƹS£¨¡ù³ŘÍ]¿Ây™ôEP xX¶¹܇O¡“gÚ¡IwÃ鑦ÅB‡Ï|ǰ…N«úmH¯‹âŸbę†U~xĈbȒ{^xÖlDž•¸dɂ‡„~"]],"encodeOffsets":[[[120023,41045],[121616,39981],[122102,42307]]]},"properties":{"cp":[114.502461,38.045474],"name":"河北","childNum":3}},{"id":"140000","geometry":{"type":"Polygon","coordinates":["@@žħÜ_ªlìwGkÛÃǏok‘ćiµVZģ¡coœ‘TS˹ĪmnÕńe–hZg{gtwªpXaĚThȑp{¶Eh—®RćƑP¿£‘PmcªaJyý{ƒýȥoÅîɡųAďä³aωJ‘½¥PG­ąSM­sWz½µÛ€‘YӀŖgxoOkĒCo­Èµ]¯_²ÕjāŽK~©ÅØ^ԛkïçămϑk]­±ƒcݯÑÃmQÍ~_a—pm…~ç¡q“ˆu{JÅŧ·Ls}–EyÁÆcI{¤IiCfUc•ƌÃp§]웫vD@¡SÀ‘µM‚ÅwuŽYY‡¡DbÑc¡hƒ×]nkoQdaMç~eD•ÛtT‰©±@¥ù@É¡‰ZcW|WqOJmĩl«ħşvOÓ«IqăV—¥ŸD[mI~Ó¢cehiÍ]Ɠ~ĥqXŠ·eƷœn±“}v•[ěďŽŕ]_‘œ•`‰¹ƒ§ÕōI™o©b­s^}Ét±ū«³p£ÿ¥WÑxçÁ«h×u׌¥ř„‹¾dÒ{ºvĴÎêÌɊ²¶€ü¨|ÞƸµȲ‘LLúÉƎ¤ϊęĔV`„_bª‹S^|ŸdŠzY|dz¥p†ZbÆ£¶ÒK}tĦÔņƠ‚PYzn€ÍvX¶Ěn ĠÔ„zý¦ª˜÷žÑĸَUȌ¸‚dòÜJð´’ìúNM¬ŒXZ´‘¤ŊǸ_tldIš{¦ƀðĠȤ¥NehXnYG‚‡R° ƬDj¬¸|CĞ„Kq‚ºfƐiĺ©ª~ĆOQª ¤@ìǦɌ²æBŒÊ”TœĞšHƘÁĪËĖ’šĴŞ–ȀœÆÿȄlŤĒö„t”νî¼ĨXhŒ‘˜|ªM¤ÐzÞĩ҃S‰rao³"],"encodeOffsets":[[117016,41452]]},"properties":{"cp":[112.549248,37.857014],"name":"山西","childNum":1}},{"id":"150000","geometry":{"type":"MultiPolygon","coordinates":[["@@ǪƫÌÛM…Ă[`՞Cn}¶Vc…ê“sƒ–¯‹PqƒFB…‰|S•³C|kñ•H‹d‘iÄ¥sˆʼnő…PóÑÑE^‘ÅPpy_YtS™hQ·aHwsOnʼnÚs©iqj›‰€USiº]ïWš‰«gW¡A–R붛ijʕ…Œů`çõh]y»ǃŸǛҤxÒm~zf}pf|ÜroÈzrKÈĵSƧ„ż؜Ġu~è¬vîS¼™Ăh–šĖMÈÄw‚\\fŦ°W ¢¾luŸD„wŠ\\Ŗĝ","@@ƒGVu»A—ylßí¹ãe’“]Eāò³C¹ð“¾ˆ²iŒÒAdkò^P“²CǜңDŽ z¼g^èöŰ_‹‚–†IJĕꄜ}gÁnUI«m‰…„‹]j‡vV¼euhwqA„aW˜ƒ_µj…»çjioQR¹ēÃßt@r³[ÛlćË^ÍÉáG“›OUۗOB±•XŸkŇ¹£k|e]ol™ŸkVͼÕqtaÏõjgÁ£§U^Œ”RLˆËnX°Ç’Bz†^~wfvˆypV ¯„ƫĉ˭ȫƗŷɿÿĿƑ˃ĝÿÃǃßËőó©ǐȍŒĖM×ÍEyx‹þp]Évïè‘vƀnÂĴÖ@‚‰†V~Ĉ™Š³MEˆĸÅĖt—ējyÄDXÄxGQuv_›i¦aBçw‘˛wD™©{ŸtāmQ€{EJ§KPśƘƿ¥@‰sCT•É}ɃwˆƇy±ŸgÑ“}T[÷kÐ禫…SÒ¥¸ëBX½‰HáŵÀğtSÝÂa[ƣ°¯¦P]£ġ“–“Òk®G²„èQ°óMq}EŠóƐÇ\\ƒ‡@áügQ͋u¥Fƒ“T՛¿Jû‡]|mvāÎYua^WoÀa·­ząÒot×¶CLƗi¯¤mƎHNJ¤îìɾŊìTdåwsRÖgĒųúÍġäÕ}Q¶—ˆ¿A•†‹[¡Œ{d×uQAƒ›M•xV‹vMOmăl«ct[wº_šÇʊŽŸjb£ĦS_é“QZ“_lwgOiýe`YYJq¥IÁˆdz£ÙË[ÕªuƏ³ÍT—s·bÁĽäė[›b[ˆŗfãcn¥îC¿÷µ[ŏÀQ­ōšĉm¿Á^£mJVm‡—L[{Ï_£›F¥Ö{ŹA}…×Wu©ÅaųijƳhB{·TQqÙIķˑZđ©Yc|M¡…L•eVUóK_QWk’_ĥ‘¿ãZ•»X\\ĴuUƒè‡lG®ěłTĠğDє›žG‚ÆÍz]‹±…ŭ©ŸÅ’]ŒÅÐ}UË¥©Tċ™ïxgckfWgi\\ÏĒ¥HkµE˜ë{»ÏetcG±ahUiñiWsɁˆ·c–C‚Õk]wȑ|ća}w…VaĚ᠞ŒG°ùnM¬¯†{ÈˆÐÆA’¥ÄêJxÙ¢”hP¢Ûˆº€µwWOŸóFŽšÁz^ÀŗÎú´§¢T¤ǻƺSė‰ǵhÝÅQgvBHouʝl_o¿Ga{ïq{¥|ſĿHĂ÷aĝÇq‡Z‘ñiñC³ª—…»E`¨åXēÕqÉû[l•}ç@čƘóO¿¡ƒFUsA‰“ʽīccšocƒ‚ƒÇS}„“£‡IS~ălkĩXçmĈ…ŀЂoÐdxÒuL^T{r@¢‘žÍƒĝKén£kQ™‰yšÅõËXŷƏL§~}kqš»IHėDžjĝŸ»ÑÞoŸå°qTt|r©ÏS‹¯·eŨĕx«È[eMˆ¿yuˆ‘pN~¹ÏyN£{©’—g‹ħWí»Í¾s“əšDž_ÃĀɗ±ą™ijĉʍŌŷ—S›É“A‹±åǥɋ@럣R©ąP©}ĹªƏj¹erƒLDĝ·{i«ƫC½ÉshVz…GS|úþX”gp›{ÁX¿Ÿć{ƱȏñZáĔyoÁhA™}ŅĆfdʼn„_¹„Y°ėǩÑ¡H¯¶oMQqð¡Ë™|‘Ñ`ƭŁX½·óۓxğįÅcQ‡ˆ“ƒs«tȋDžF“Ÿù^i‘t«Č¯[›hAi©á¥ÇĚ×l|¹y¯Kȝqgů{ñǙµï‚ċ™Ĺz—Śȭ¶¡˜›oŽäÕG\\ďT¿Òõr¯œŸLguÏYęRƩšɷŌO\\İТæ^Ŋ IJȶȆbÜGŽĝ¬¿ĚVĎgª^íu½jÿĕęjık@Ľƒ]ėl¥Ë‡ĭûÁ„ƒėéV©±ćn©­ȇžÍq¯½•YÃÔʼn“ÉNѝÅÝy¹NqáʅDǡËñ­ƁYÅy̱os§ȋµʽǘǏƬɱà‘ưN¢ƔÊuľýľώȪƺɂļžxœZĈ}ÌʼnŪ˜ĺœŽĭFЛĽ̅ȣͽÒŵìƩÇϋÿȮǡŏçƑůĕ~Ǎ›¼ȳÐUf†dIxÿ\\G ˆzâɏÙOº·pqy£†@ŒŠqþ@Ǟ˽IBäƣzsÂZ†ÁàĻdñ°ŕzéØűzșCìDȐĴĺf®ŽÀľưø@ɜÖÞKĊŇƄ§‚͑těï͡VAġÑÑ»d³öǍÝXĉĕÖ{þĉu¸ËʅğU̎éhɹƆ̗̮ȘNJ֥ड़ࡰţાíϲäʮW¬®ҌeרūȠkɬɻ̼ãüfƠSצɩςåȈHϚÎKdzͲOðÏȆƘ¼CϚǚ࢚˼ФԂ¤ƌžĞ̪Qʤ´¼mȠJˀŸƲÀɠmɆŠDŽĜƠ´ǠN~€ʢĜ‚¶ƌĆĘźʆȬ˪ĚǏĞGȖƴƀj`ĢçĶāàŃºē̃ĖćšYŒÀŎüôQÐÂŎŞdžŞêƖš˜oˆDĤÕºÑǘÛˤ³̀gńƘĔÀ^žªƂ`ªt¾äƚêĦĀ¼Ð€Ĕǎ¨Ȕ»͠^ˮÊȦƤøxRrŜH¤¸ÂxDĝŒ|ø˂˜ƮÐ¬ɚwɲFjĔ²Äw°dždÀɞ_ĸdîàŎjʜêTĞªŌ‡ŜWÈ|tqĢUB~´°ÎFC•ŽU¼pĀēƄN¦¾O¶ŠłKĊOj“Ě”j´ĜYp˜{¦„ˆSĚÍ\\Tš×ªV–÷Ší¨ÅDK°ßtŇĔKš¨ǵÂcḷ̌ĚǣȄĽF‡lġUĵœŇ‹ȣFʉɁƒMğįʏƶɷØŭOǽ«ƽū¹Ʊő̝Ȩ§ȞʘĖiɜɶʦ}¨֪ࠜ̀ƇǬ¹ǨE˦ĥªÔêFŽxúQ„Er´W„rh¤Ɛ \\talĈDJ˜Ü|[Pll̚¸ƎGú´Pž¬W¦†^¦–H]prR“n|or¾wLVnÇIujkmon£cX^Bh`¥V”„¦U¤¸}€xRj–[^xN[~ªŠxQ„‚[`ªHÆÂExx^wšN¶Ê˜|¨ì†˜€MrœdYp‚oRzNy˜ÀDs~€bcfÌ`L–¾n‹|¾T‚°c¨È¢a‚r¤–`[|òDŞĔöxElÖdH„ÀI`„Ď\\Àì~ƎR¼tf•¦^¢ķ¶e”ÐÚMŒptgj–„ɡČÅyġLû™ŇV®ŠÄÈƀ†Ď°P|ªVV†ªj–¬ĚÒêp¬–E|ŬÂ_~¼rƐK fˆ{ĘFǜƌXưăkÃĄ}nµ–oŸ×q£ç­kX‘{uĩ«āíÓUŅÝVUŌ]€Ť¥lyň[€oi{¦‹L‡ĸ…Ħ^ôâJˆ¨^UZðڔĒL„¿Ì‹ˆfŒ£K£ʺ™oqNŸƒwğc`ue—tOj×°KJ±qƒÆġm‰Ěŗos¬…qehqsuœƒH{¸kH¡Š…ÊRǪÇƌbȆ¢´ä܍¢NìÉʖ¦â©Ɨؗ"]],"encodeOffsets":[[[128500,52752],[127089,51784]]]},"properties":{"cp":[111.670801,40.818311],"name":"内蒙古","childNum":2}},{"id":"210000","geometry":{"type":"MultiPolygon","coordinates":[["@@L–Ž@@s‘]","@@MnNm","@@d†c","@@eÀ‚C@b‚“‰","@@f‡…Xwkbr–Ä`qg","@@^jtWQ","@@~ Y[c","@@I`ĖN^_¿Z‚ÁM","@@Ïxnj{q_×^Gigp","@@iX¶B…Y","@@„Y…Z","@@L_yG`b","@@^WqCTZ","@@\\[“‹§t|”ž]","@@m`p[","@@@œé^B†‡ntˆaÊU—˜Ÿ]x ¯ÄPIJ­°h€ʙK³†VˆÕ@Y~†|EvĹsDŽ¦­L^p²ŸÒG ’Ël]„xxÄ_˜fT¤Ď¤cŽœP„–C¨¸TVjbgH²sdÎdHt`Bˆ—²¬GJję¶[ÐhjeXdlwhšðSȦªVÊπ‹Æ‘Z˜ÆŶ®²†^ŒÎyÅ‚Hœń“ĚDMħĜŁH­ˆk„çvV[ij¼W–‚YÀäĦ’‘`XlžR`žôLUVžfK–¢†{NZdĒª’YĸÌÚJRr¸SA|ƴgŴĴÆbvªØX~†źBŽ|¦ÕœEž¤Ð`\\|Kˆ˜UnnI]¤ÀÂĊnŎ™R®Ő¿¶\\ÀøíDm¦ÎbŨab‰œaĘ\\ľã‚¸a˜tÎSƐ´©v\\ÖÚÌǴ¤Â‡¨JKr€Z_Z€fjþhPkx€`Y”’RIŒjJcVf~sCN¤ ˆE‚œhæm‰–sHy¨SðÑÌ\\\\ŸĐRÊwS¥fqŒßýáЍÙÉÖ[^¯ǤŲ„ê´\\¦¬ĆPM¯£Ÿˆ»uïpùzEx€žanµyoluqe¦W^£ÊL}ñrkqWňûP™‰UP¡ôJŠoo·ŒU}£Œ„[·¨@XŒĸŸ“‹‹DXm­Ûݏº‡›GU‹CÁª½{íĂ^cj‡k“¶Ã[q¤“LÉö³cux«|Zdƒ²BWÇ®Yß½ve±ÃC•ý£W{Ú^’q^sÑ·¨‹ËMƒr“¹·C¥‡GD›rí@wÕKţ݋˜Ÿ«V·i}xËÍ÷‘i©ĝ‡ɝǡ]ƒˆ{c™±OW‹³Ya±Ÿ‰_穂Hžĕoƫ€Ňqƒr³‰Lys[„ñ³¯OS–ďOMisZ†±ÅFC¥Pq{‚Ã[Pg}\\—¿ghćO…•k^ĩÁXaĕËĥM­oEqqZûěʼn³F‘¦oĵ—hŸÕP{¯~TÍlª‰N‰ßY“Ð{Ps{ÃVU™™eĎwk±ʼnVÓ½ŽJãÇÇ»Jm°dhcÀff‘dF~ˆ€ĀeĖ€d`sx² šƒ®EĦ¦–šdQ‹Âd^~ăÔHˆ¦\\›LKpĄVez¤NP ǹӗR™ÆąJSh­a[¦´Âghwm€BÐ¨źhI|žVVŽ—Ž|p] Â¼èNä¶ÜBÖ¼“L`‚¼bØæŒKV”ŸpoœúNZÞÒKxpw|ÊEMnzEQšŽIZ”ŽZ‡NBˆčÚFÜçmĩ‚WĪñt‘ÞĵÇñZ«uD‚±|ƏlǗw·±PmÍa‰–da‡ CL‡Ǒkùó¡³Ï«QaċϑOÃ¥ÕđQȥċƭy‹³ÁA"]],"encodeOffsets":[[[123686,41445],[126019,40435],[124393,40128],[126117,39963],[125322,40140],[126686,40700],[126041,40374],[125584,40168],[125509,40217],[125453,40165],[125362,40214],[125280,40291],[125774,39997],[125976,40496],[125822,39993],[122731,40949]]]},"properties":{"cp":[123.429096,41.796767],"name":"辽宁","childNum":16}},{"id":"220000","geometry":{"type":"Polygon","coordinates":["@@ñr½ÉKāGÁ¤ia É‰™È¹`\\xs€¬dĆkNnuNUŒ–wœNx¶c¸‹|\\¢…ŒGªóĄ~RãÖÎĢù‚đŴÕhQŽxtcæëSɽʼníëlj£ƍG£nj°KƘµDsØÑpyƸ®¿bXp‚]vbÍZuĂ{nˆ^IüœÀSք”¦EŒvRÎûh@℈[‚Əȉô~FNr¯ôçR±ƒ­HÑl•’Ģ–^¤¢‚OðŸŽætxsŒ]ÞÁTĠs¶¿âƊGW¾ìA¦·TѬ†è¥€ÏÐJ¨¼ÒÖ¼ƒƦɄxÊ~S–tD@ŠĂ¼Ŵ¡jlºWžvЉˆzƦZЎ²CH— „Axiukd‹ŒGgetqmcžÛ£Ozy¥cE}|…¾cZ…k‚‰¿uŐã[oxGikfeäT@…šSUwpiÚFM©’£è^ڟ‚`@v¶eň†f h˜eP¶žt“äOlÔUgƒÞzŸU`lœ}ÔÆUvØ_Ō¬Öi^ĉi§²ÃŠB~¡Ĉ™ÚEgc|DC_Ȧm²rBx¼MÔ¦ŮdĨÃâYx‘ƘDVÇĺĿg¿cwÅ\\¹˜¥Yĭlœ¤žOv†šLjM_a W`zļMž·\\swqÝSA‡š—q‰Śij¯Š‘°kŠRē°wx^Đkǂғ„œž“œŽ„‹\\]˜nrĂ}²ĊŲÒøãh·M{yMzysěnĒġV·°“G³¼XÀ““™¤¹i´o¤ŃšŸÈ`̃DzÄUĞd\\i֚ŒˆmÈBĤÜɲDEh LG¾ƀľ{WaŒYÍȏĢĘÔRîĐj‹}Ǟ“ccj‡oUb½š{“h§Ǿ{K‹ƖµÎ÷žGĄØŜçưÌs«l›•yiē«‹`姝H¥Ae^§„GK}iã\\c]v©ģZ“mÃ|“[M}ģTɟĵ‘Â`À–çm‰‘FK¥ÚíÁbXš³ÌQґHof{‰]e€pt·GŋĜYünĎųVY^’˜ydõkÅZW„«WUa~U·Sb•wGçǑ‚“iW^q‹F‚“›uNĝ—·Ew„‹UtW·Ýďæ©PuqEzwAV•—XR‰ãQ`­©GŠY…Yhc•UGorBd}ģɇb¡·µMicF«—YƅŒ»…é\\ƒɹ~ǙG³mØ©BšuT§Ĥ½¢Ã_ý‘L¡‘ûŸsT\\rke™\\PnwAK‚y}’ywdS™efµ]UhĿD@mÿvašÙNSkCun…cÿ`l‚‰W‹„ėVâ¦÷~^fÏ~œvwHCŽį„`xqT­­ƒlW«ï¸skm‹‹ßEG“qd¯•‹R…©Ýޝ¯S†\\cZ¹iűƏCuƍÓX‡oR}“M^o•£…R}oªU­F…uuXHlEŕ‡€Ï©¤ßgXˆþ¤D–²ÄufàÀ­XXȱAc„{Yw¬dvõ´KÊ£”\\rµÄl”iˆdā]|DÂVŒœH¹ˆÞ®ÜWnŒC”Œķ W‹§@\\¸‹ƒ~¤‹Vp¸‰póIO¢ŠVOšŇürXql~òÉK]¤¥Xrfkvzpm¶bwyFoúvð‡¼¤ N°ąO¥«³[ƒéǣű]°Õ\\ÚÊĝŽôîŇÔaâŸBYlďQ[ Ë[ïÒ¥RI|‘`jž]P"],"encodeOffsets":[[126831,44503]]},"properties":{"cp":[125.3245,43.886841],"name":"吉林","childNum":1}},{"id":"230000","geometry":{"type":"MultiPolygon","coordinates":[["@@UƒµNÿ¥īè灋•HÍøƕ¶LŒǽ|g¨|”™Ža¾pViˆdd”~ÈiŒíďÓQġėǐZ΋ŽXb½|ſÃH½ŸKFgɱCģÛÇA‡n™‹jÕc[VĝDZÃ˄Ç_™ £ń³pŽj£º”š¿”»WH´¯”U¸đĢmžtĜyzzNN|g¸÷äűѱĉā~mq^—Œ[ƒ”››”ƒǁÑďlw]¯xQĔ‰¯l‰’€°řĴrŠ™˜BˆÞTxr[tޏĻN_yŸX`biN™Ku…P›£k‚ZĮ—¦[ºxÆÀdhŽĹŀUÈƗCw’áZħÄŭcÓ¥»NAw±qȥnD`{ChdÙFćš}¢‰A±Äj¨]ĊÕjŋ«×`VuÓś~_kŷVÝyh„“VkÄãPs”Oµ—fŸge‚Ň…µf@u_Ù ÙcŸªNªÙEojVx™T@†ãSefjlwH\\pŏäÀvŠŽlY†½d{†F~¦dyz¤PÜndsrhf‹HcŒvlwjFœ£G˜±DύƥY‡yϊu¹XikĿ¦ÏqƗǀOŜ¨LI|FRĂn sª|Cš˜zxAè¥bœfudTrFWÁ¹Am|˜ĔĕsķÆF‡´Nš‰}ć…UŠÕ@Áijſmužç’uð^ÊýowŒFzØÎĕNőžǏȎôªÌŒDŽàĀÄ˄ĞŀƒʀĀƘŸˮȬƬĊ°ƒUŸzou‡xe]}Ž…AyȑW¯ÌmK‡“Q]‹Īºif¸ÄX|sZt|½ÚUΠlkš^p{f¤lˆºlÆW –€A²˜PVܜPH”Êâ]ÎĈÌÜk´\\@qàsĔÄQºpRij¼èi†`¶—„bXƒrBgxfv»ŽuUiˆŒ^v~”J¬mVp´£Œ´VWrnP½ì¢BX‚¬h™ŠðX¹^TjVœŠriªj™tŊÄm€tPGx¸bgRšŽsT`ZozÆO]’ÒFô҆Oƒ‡ŊŒvŞ”p’cGŒêŠsx´DR–Œ{A†„EOr°Œ•žx|íœbˆ³Wm~DVjºéNN†Ëܲɶ­GƒxŷCStŸ}]ûō•SmtuÇÃĕN•™āg»šíT«u}ç½BĵÞʣ¥ëÊ¡Mێ³ãȅ¡ƋaǩÈÉQ‰†G¢·lG|›„tvgrrf«†ptęŘnŠÅĢr„I²¯LiØsPf˜_vĠd„xM prʹšL¤‹¤‡eˌƒÀđK“žïÙVY§]I‡óáĥ]ķ†Kˆ¥Œj|pŇ\\kzţ¦šnņäÔVĂîά|vW’®l¤èØr‚˜•xm¶ă~lÄƯĄ̈́öȄEÔ¤ØQĄ–Ą»ƢjȦOǺ¨ìSŖÆƬy”Qœv`–cwƒZSÌ®ü±DŽ]ŀç¬B¬©ńzƺŷɄeeOĨS’Œfm Ċ‚ƀP̎ēz©Ċ‚ÄÕÊmgŸÇsJ¥ƔˆŊśæ’΁Ñqv¿íUOµª‰ÂnĦÁ_½ä@ê텣P}Ġ[@gġ}g“ɊדûÏWXá¢užƻÌsNͽƎÁ§č՛AēeL³àydl›¦ĘVçŁpśdžĽĺſʃQíÜçÛġԏsĕ¬—Ǹ¯YßċġHµ ¡eå`ļƒrĉŘóƢFì“ĎWøxÊk†”ƈdƬv|–I|·©NqńRŀƒ¤é”eŊœŀ›ˆàŀU²ŕƀB‚Q£Ď}L¹Îk@©ĈuǰųǨ”Ú§ƈnTËÇéƟÊcfčŤ^Xm‡—HĊĕË«W·ċëx³ǔķÐċJā‚wİ_ĸ˜Ȁ^ôWr­°oú¬Ħ…ŨK~”ȰCĐ´Ƕ£’fNÎèâw¢XnŮeÂÆĶŽ¾¾xäLĴĘlļO¤ÒĨA¢Êɚ¨®‚ØCÔ ŬGƠ”ƦYĜ‡ĘÜƬDJ—g_ͥœ@čŅĻA“¶¯@wÎqC½Ĉ»NŸăëK™ďÍQ“Ùƫ[«Ãí•gßÔÇOÝáW‘ñuZ“¯ĥ€Ÿŕā¡ÑķJu¤E Ÿå¯°WKɱ_d_}}vyŸõu¬ï¹ÓU±½@gÏ¿rýD‰†g…Cd‰µ—°MFYxw¿CG£‹Rƛ½Õ{]L§{qqąš¿BÇƻğëšܭNJË|c²}Fµ}›ÙRsÓpg±ŠQNqǫŋRwŕnéÑÉKŸ†«SeYR…ŋ‹@{¤SJ}šD Ûǖ֍Ÿ]gr¡µŷjqWÛham³~S«“„›Ü[","@@ƨƒĶTLÇyqpÇÛqe{~oyen}s‰`q‡iXG”ù]Ëp½“©lɇÁp]Þñ´FÔ^f‘äîºkà˜z¼BUv¬D"]],"encodeOffsets":[[[134456,44547],[127123,51780]]]},"properties":{"cp":[126.642464,45.756967],"name":"黑龙江","childNum":2}},{"id":"320000","geometry":{"type":"Polygon","coordinates":["@@Õg^vÁbnÀ‹`Jnĝ¬ŽòM¶ĘšTÖŒb‚˜e¦¦€{¸ZâćNpŒ©žHp|`ˆmjhŠSEb\\afv`sz^lkŽlj‹Ätg‹¤D˜­¾Xš¿À’|ДiZ„ȀåB·î}GL¢õcßjaŸyBFµÏC^ĭ•cÙt¿sğH]j{s©HM¢ƒQnDÀ©DaÜތ·jgàiDbPufjDk`dPOîƒhw¡ĥ‡¥šG˜ŸP²ĐobºrY†„î¶aHŢ´ ]´‚rılw³r_{£DB_Ûdåuk|ˆŨ¯F Cºyr{XFy™e³Þċ‡¿Â™kĭB¿„MvÛpm`rÚã”@ƹhågËÖƿxnlč¶Åì½Ot¾dJlŠVJʜǀœŞqvnOŠ^ŸJ”Z‘ż·Q}ê͎ÅmµÒ]Žƍ¦Dq}¬R^èĂ´ŀĻĊIԒtžIJyQŐĠMNtœR®òLh‰›Ěs©»œ}OӌGZz¶A\\jĨFˆäOĤ˜HYš†JvÞHNiÜaϚɖnFQlšNM¤ˆB´ĄNöɂtp–ŬdZÅgl•muÇUšŽ“Ş‡Úb¤uŃJŴu»¹Ą•lȖħŴw̌ŵ²ǹǠ͛hĭłƕrçü±Y™rřl¥’i`ã__¢ćSÅr[Çq^ùzWmOĈaŐÝɞï²ʯʊáĘijĒǭPħ͍ôƋĝÄ͎ī‰çÛɈǥ£­ÛmY`ó£Z«§°Ó³QafusNıDž_k}¢m[ÝóDµ—¡RLčiXy‡ÅNïă¡¸iĔϑNÌķoıdōîåŤûHcs}~Ûwbù¹£¦ÓCt‹OPrƒE^ÒoŠg™ĉIµžÛÅʹK…¤½phMˆú`m”R¸¦Pƚg†ÉLRŠs`£¯ãhD„¨|³¤‰C"],"encodeOffsets":[[121451,32518]]},"properties":{"cp":[118.767413,32.041544],"name":"江苏","childNum":1}},{"id":"330000","geometry":{"type":"MultiPolygon","coordinates":[["@@jX^n…","@@sfˆdM‰","@@qP\\xz[_i","@@o\\V’zRZ}mECy","@@‘Rƒ¢‚FX}°[m]","@@Cbœ\\•}","@@e|v\\laus","@@v~s{","@@QxÂF©}","@@¹nŒvÞs©m","@@rQgYIh","@@bi«Z„X","@@p[}ILd","@@À¿|","@@¹dnb’…","@@rS}[Kl","@@g~h}","@@FlCk","@@ůTG°ĄLHm°UF‰","@@OdRe","@@v[u\\","@@FjâL~wyoo~›sµLŒZ","@@¬e¹aH‚","@@\\nÔ¡q]L³ë\\ÿ®ŒQ̆","@@ÊA­©]ª","@@KxŒv{­","@@@hlIk_","@@pWc‡rxp","@@Md|_iA","@@¢…X£½z\\ðpN","@@hlÜ[LykAvyfw^Ež ","@@fp¤MusH","@@®_ma~•LÁ¬’`","@@†@°¡mۛGĕ¨§Ianá[ýƤjfæ‡ÐNž—äGp—","@@iM„t\\","@@Zc[b","@@™X®±GrưZæĉm","@@Z~dOSo|A¿qZv","@@@`”EN£p","@@|–s—","@@@nDi","@@n…a£¾u‰YL¯‰Qª…mĉÅdMˆ•gÇjcº«•ęœ¬­K­´ƒB«Âącoċ\\xK`cįŧ«®á’[~ıxu·Å”KsËɏc¢Ù\\ĭƛëbf¹­ģSƒĜkáƉÔ­ĈZB{ŠaM‘µ‰fzʼnfÓÔŹŁƋǝÊĉ{ğč±g³ne{ç­ií´S¬‚\\ßðK¦w\\™iqªĭiAu‡A­µ”_W¥ƣO\\lċĢttC¨£t`ˆ™PZäuXßBs‡Ļyek€OđġĵHuXBšµ]׌‡­­\\›°®¬F¢¾pµ¼kŘó¬Wät’¸|@ž•L¨¸µr“ºù³Ù~§WI‹ŸZWŽ®’±Ð¨ÒÉx€`‰²pĜ•rOògtÁZ{üÙ[|˜ûŒK‚wsPlU[}¦Rvn`hsª^–nQ´ĘRWb”‚_ rtČFI֊kŠŠĦPJ¶ÖÀÖJĈĄTĚòžC ²@Pú…Øzœ©PœCÈÚœĒ±„hŖ‡l¬â~nm¨f©–iļ«m‡nt–qŒÒTÜÄj“ŠLŽ®E̜Fª²iÊxبžIÈhhst’ˆ’[Ôx†}dtüGæţŔïĬaĸpMËВj碷ðĄÆMzˆjWKĎ¢Q¶˜À_꒖_@ı€i«pZ€gf€¤Nrq]§ĂN®«H±‡yƳí¾×ŊďŀĐÏŴǝĂíÀBŖÕªˆŠÁŐTFqĉ¯³ËCĕģi¨hÜ·ƒñt»¯Ï","@@ºwšZRkĕWK "]],"encodeOffsets":[[[125785,31436],[125729,31431],[125513,31380],[125329,30690],[125223,30438],[125115,30114],[124815,29155],[124419,28746],[124095,28635],[124005,28609],[125000,30713],[125111,30698],[125078,30682],[125150,30684],[124014,28103],[125008,31331],[125411,31468],[125329,31479],[125369,31139],[125626,30916],[125417,30956],[125254,30976],[125199,30997],[125095,31058],[125083,30915],[124885,31015],[125218,30798],[124867,30838],[124755,30788],[124802,30809],[125267,30657],[125218,30578],[125200,30562],[125192,30787],[124968,30474],[125167,30396],[125115,30363],[124955,29879],[124714,29781],[124762,29462],[124325,28754],[124863,30077],[125366,31477]]]},"properties":{"cp":[120.153576,30.287459],"name":"浙江","childNum":43}},{"id":"340000","geometry":{"type":"MultiPolygon","coordinates":[["@@^iuLV\\","@@‚e©Edh","@@´CE¶zAXœêeödK¡~H¸íæAˆȽ—d{ďő“À½W—®£ChŒÃsiŒkkly]_teu[bFa‰Tig‡n{]Gqªo‹ĈMYá|·¥f¥—őaSÕė™NµñĞ«ImŒ_m¿Âa]uĜp …Z_§{Cƒäg¤°r[_Yj‰ÆOdý“[ŽI[á·¥“Q_n‡ùgL¾mz›ˆDÜÆ¶ĊJhšp“c¹˜O]iŠ]œ¥ jtsggDÑ¡“w×jÉ©±›EFˍ­‰Ki”ÛÃÕYv…s•ˆm¬njĻª•§emná}k«ŕˆƒgđ²Ù›DǤ›í¡ªOy›†×Où±@DŸñSęćăÕIÕ¿IµĥO‰‰‰lJÕÍR›Í|JìĻÒåyķrĕq§ÄĩsWÆßŽF¶žX®¿‰mŒ™w…RIޓfßoG‘³¾©uyH‘į{Ɓħ¯AFnuP…ÍÔzšŒV—dàôº^Ðæd´€‡oG¤{S‰¬ćxã}›ŧ×Kǥĩ«žÕOEзÖdÖsƘѨ[’Û^Xr¢¼˜§xvěƵ`K”§ tÒ´Cvlo¸fzŨð¾NY´ı~ÉĔē…ßúLÃϖ_ÈÏ|]ÂÏHl’g`bšežž€n¾¢pU‚h~ƴ˶_‚r sĄ~cž”ƈ]|r c~`¼{À{ȒiJjz`îÀT¥Û³…]’u}›f…ïQl{skl“oNdŸjŸäËzDvčoQŠďHI¦rb“rHĖ~BmlNž“Ra„ĥTX\\{fÁKÁ®T‚œL‘ŠĄMt›ÊgĀD–ŠĄXœƔvDcÎJbt[¤€D@®hh~kt°ǾzÖ@¾ªdb„YhüóV´ŮŒ¨Üc”±r@J|àuYÇԋG·ĚąĐlŪÚpSJ¨ĸˆLvÞcPæķŨŽ®mАˆál‹sgd×mQ¨ųƩޖ¤IΖs’°ŒKZpĄ|XwWdĎµmkǀwÌÕæhºgBĝâqÙĊz›ÖgņtÀÁÊÆá’hEz|WzqD¹€Ÿ°E‡ŧl{ævÜcA`¤C`|´qžxIJkq^³³ŸGšµbƒíZ…¹qpa±ď OH—¦™Ħˆx¢„gPícOl_iCveaOjCh߸i݋bÛªCC¿€m„RV§¢A|tbkĜEÀtîm‚^g´fÄ"]],"encodeOffsets":[[[121722,32278],[119475,30423],[121606,33646]]]},"properties":{"cp":[117.283042,31.86119],"name":"安徽","childNum":3}},{"id":"350000","geometry":{"type":"MultiPolygon","coordinates":[["@@“zht´}[","@@aj^~ĆGå","@@edœŒH…se","@@@vˆPGsyQ","@@‰sBz‚ddW[O","@@SލQy","@@NŽVucW","@@qptB@q","@@‰’¸[iu","@@Q\\pD[_","@@jSwUappI","@@eXª~•","@@AjvFoo","@@fT–›_Çí\\Ÿ™—v|ba¦jZÆy|®","@@IjLg","@@wJI€ˆxš«¼AoNe{M¥Œ","@@K‰±¡Óˆ”Č~N¾™","@@k¡¹Eh~c®uDq‰Zì¡I•~Māe£bN¨gZý¡a±Öcp©PhžI”Ÿ¢Qq…ÇGj‹|¥U™ g[Ky¬ŏ–v@OpˆtÉEŸF„\\@ åA¬ˆV{Xģ‰ĐBy…cpě…¼³Ăp·¤ƒ¥o“hqqÚ¡ŅLsƒ^ᗞ§qlŸÀhH¨MCe»åÇGD¥zPO£čÙkJA¼ß–ėu›ĕeûҍiÁŧS[¡œUŠûŗ½ùěcݧSùĩąSWó«íęACµ›eR—åǃRCÒÇZÍ¢‹ź±^dlsŒtjD¸•‚ZpužÔâÒH¾oLUêÃÔjjēò´ĄW‚ƛ…^Ñ¥‹ĦŸ@Çò–ŠmŒƒOw¡õyJ†yD}¢ďÑÈġfŠZd–a©º²z£šN–ƒjD°Ötj¶¬ZSÎ~¾c°¶Ðm˜x‚O¸¢Pl´žSL|¥žA†ȪĖM’ņIJg®áIJČĒü` ŽQF‡¬h|ÓJ@zµ |ê³È ¸UÖŬŬÀCtrĸr‚]€˜ðŽM¤ĶIJHtÏ A’†žĬkvsq‡^aÎbvŒd–™fÊòSD€´Z^’xPsÞrv‹ƞŀ˜jJd×ŘÉ ®A–ΦĤd€xĆqAŒ†ZR”ÀMźŒnĊ»ŒİÐZ— YX–æJŠyĊ²ˆ·¶q§·–K@·{s‘Xãô«lŗ¶»o½E¡­«¢±¨Yˆ®Ø‹¶^A™vWĶGĒĢžPlzfˆļŽtàAvWYãšO_‡¤sD§ssČġ[kƤPX¦Ž`¶“ž®ˆBBvĪjv©šjx[L¥àï[F…¼ÍË»ğV`«•Ip™}ccÅĥZE‹ãoP…´B@ŠD—¸m±“z«Ƴ—¿å³BRضˆœWlâþäą`“]Z£Tc— ĹGµ¶H™m@_©—kŒ‰¾xĨ‡ôȉðX«½đCIbćqK³Á‹Äš¬OAwã»aLʼn‡ËĥW[“ÂGI—ÂNxij¤D¢ŽîĎÎB§°_JœGsƒ¥E@…¤uć…P‘å†cuMuw¢BI¿‡]zG¹guĮI‹"]],"encodeOffsets":[[[123250,27563],[122541,27268],[123020,27189],[122916,27125],[122887,26845],[122808,26762],[122568,25912],[122778,26197],[122515,26757],[122816,26587],[123388,27005],[122450,26243],[122578,25962],[121255,25103],[120987,24903],[122339,25802],[121042,25093],[122439,26024]]]},"properties":{"cp":[119.306239,26.075302],"name":"福建","childNum":18}},{"id":"360000","geometry":{"type":"Polygon","coordinates":["@@ÖP¬ǦĪØLœŨä~ĈwŠ«|TH£ˆp€c³Ïå¹]ĉđxe{ÎӐ†vOEm°BƂĨİ|G’vz½ª´€H’àp”eJ݆Qšxn‹ÀŠW­žEµàXÅĪt¨ÃĖrÄwÀFÎ|Ă¡”‡WÕ¸cf¥—‘XaęST±m[“r«_gŽmQu~¥V\\OkxtL E¢‹ƒ‘Ú^~ýØkbē–qo슱_Êw§Ñ²ÏƟ뼋mĉŹ‹¿NQ“…YB‹ąrwģcÍ¥B•Ÿ­ŗÊcØiI—žƝĿuŒqtāwO]‘³YCñTeɕš‹caub͈]trlu€ī…B‘ПGsĵıN£ï—^ķqsq¿DūūV՟·´Ç{éĈý‰ÿ›OEˆR_ŸđûIċâJh­ŅıN‘ȩĕB…¦K{Tk³¡OP·wn—µÏd¯}½TÍ«YiµÕsC¯„iM•¤™­•¦¯P|ÿUHv“he¥oFTu‰õ\\ŽOSs‹MòđƇiaºćXŸĊĵà·çhƃ÷ǜ{‘ígu^›đg’m[ÙxiIN‘¶Õ»lđÕwZSƉv©_ÈëJbVk„ĔVÀ¤P¾ºÈMÖxlò~ªÚàGĂ¢B„±’ÌŒK˜y’ñ`w²¹€·Ÿ…`g›ŸsÙfI›ěxŕeykpŽŒudjˆuTfb·hh„¿JdŠ[\\˜„L‚áƔĨƐAĈepˆÀÂMD~ņªe^\\^§„ý©j׍cZ†Ø¨zdÒa¶ˆlҍJŒìõ`oz÷@¤u޸´†ôęöY¼‰HČƶajlÞƩ¥éZ[”|h}^U Œ ¥p„ĄžƦO lt¸Æ €Q\\€ŠaÆ|CnÂOjt­ĚĤd’ÈŒF`’¶„@Ð딠¦ōҞ¨Sêv†HĢÛ@[ƅQoxHŒ—W[ŰîÀt¦DŽ~NĠ¢l–•ĄtZoœCƞÔºCxrpČN˜pj¢{f_Y`_ƒeq’’®Aot`@o‚DXfkp¨|Šs¬\\D‘ÄSfè©Hn¬…^DhÆyøJh“ØxĢĀLʈ„ƠPżċĄwĮ”¶ž"],"encodeOffsets":[[118923,30536]]},"properties":{"cp":[115.892151,28.676493],"name":"江西","childNum":1}},{"id":"370000","geometry":{"type":"MultiPolygon","coordinates":[["@@Xjd]mE","@@itnq","@@Dl@k","@@T‚ŒG—w","@@K¬˜•‰U","@@Wd`c","@@PtMs","@@•LnXlc","@@ppVƒu]Qn","@@cdzAU_","@@udRhnCE…","@@ˆoIƒpP„","@@M{Ŀčwbxƨî’Kš–ÎMĮ]†—ZFˆ½Y]â£ph’™š¶¨râøÀ†ÎǨ¤^ºÄ”Gzˆ~grĚĜlĞÆ„LĆdž¢Îo¦–cv“Kb€gr°Wh”mZp ˆL]LºcU‰Æ­n”żĤÌǜbAnrOAœ´žȊcÀbƦUØrĆUÜøœĬƞ†ŶǬĴóò_A̈«ªdÎɜnb²ĦhņBĖ›žįĦåXćì@L¯´ywƕCéõė ƿ¸‘lµ‚Zæyj|BíÂKN„NnoƈfÈMZwšnŐNàúĂsT„JUš›‚L„îVj„ǎ¾Ē؍‚Dz²XPn±ŴPè¸ŔLƔÜƺ_T‘üÃĤBBċȉöA´fa„˜M¨{«M`‡¶d¡ô‰Ö°šmȰBÔjjŒ´PM|”c^d¤u•ƒ¤Û´Œä«ƢfPk¶Môlˆ]Lb„}su^ke{lC‘…M•rDŠÇ­]NÑFsmoõľH‰yGă{{çrnÓE‰‹ƕZGª¹Fj¢ÿ©}ÌCǷ돡ąuhÛ¡^Kx•C`C\\bÅxì²ĝÝ¿_N‰īCȽĿåB¥¢·IŖÕy\\‡¹kx‡Ã£ČáKµË¤ÁçFQ¡„KtŵƋ]CgÏAùSed‡cÚź—ŠuYfƒyMmhUWpSyGwMPqŀ—›Á¼zK›¶†G•­Y§Ëƒ@–´śÇµƕBmœ@Io‚g——Z¯u‹TMx}C‘‰VK‚ï{éƵP—™_K«™pÛÙqċtkkù]gŽ‹Tğwo•ɁsMõ³ă‡AN£™MRkmEʕč™ÛbMjÝGu…IZ™—GPģ‡ãħE[iµBEuŸDPԛ~ª¼ętŠœ]ŒûG§€¡QMsğNPŏįzs£Ug{đJĿļā³]ç«Qr~¥CƎÑ^n¶ÆéÎR~ݏY’I“] P‰umŝrƿ›‰›Iā‹[x‰edz‹L‘¯v¯s¬ÁY…~}…ťuٌg›ƋpÝĄ_ņī¶ÏSR´ÁP~ž¿Cyžċßdwk´Ss•X|t‰`Ä Èð€AªìÎT°¦Dd–€a^lĎDĶÚY°Ž`ĪŴǒˆ”àŠv\\ebŒZH„ŖR¬ŢƱùęO•ÑM­³Fۃaj"]],"encodeOffsets":[[[123806,39303],[123821,39266],[123742,39256],[123702,39203],[123649,39066],[123847,38933],[123580,38839],[123894,37288],[123043,36624],[123344,38676],[123522,38857],[123628,38858],[118267,36772]]]},"properties":{"cp":[117.000923,36.675807],"name":"山东","childNum":13}},{"id":"410000","geometry":{"type":"MultiPolygon","coordinates":[["@@dXD}~Hgq~ÔNŽ‹„~zkĘHVsDzßjƒŬŒŠŢ`Pûàl¢˜\\ÀœEhŽİgÞē X¼`kš„h•ÍL™ùµP³swIÓzeŠĠð†´E®žÚPt†ºIŊ–ʺ˜L«šŕQGƒ‹Yfa[şu“ßǑ‡ĩų_Z¯ĵÙčC]kbc•¥CS¯ëÍB©ïŽÇߊ_{s–WTtž³xlàcȂzÀD}ÂOQ³ÐTĬµ‚ƑпŸghœł‹Ŧv~††}ÂZž«¤lPǕ£ªÝŴÅR§ØnhcŒtâk‡nύ­ľŹUÓÝdKuķ‡I§oTũÙďkęĆH¸ÓŒ\\ăŒ¿PcnS{wBIvɘĽ[GqµuŸŇôYgûƒZcaŽ©@½Õǽys¯}lgg@­C\\£as€IdÍuCQñ[L±ęk·‹ţb¨©kK—’»›KC²‘òGKmĨS`ƒ˜UQ™nk}AGē”sqaJ¥ĐGR‰ĎpCuÌy ã iMc”plk|tRk†ðœev~^‘´†¦ÜŽSí¿_iyjI|ȑ|¿_»d}qŸ^{“Ƈdă}Ÿtqµ`ŷ飩V¡om½ZÙϋÁRD|JOÈpÀ—Rs’•I{ùÓjuµ{t}uËR‘iŸvGŠçJFjµŠåkWꖴMƒHewixGw½Yŷpµú³XU›½ġy™łå‰kÚwZXˆ·l„¢Á¢K”zO„Λ΀jc¼htoDHr…|­J“½}JZ_¯iPq{tę½ĕ¦Zpĵø«kQ…Ťƒ]MÛfaQpě±ǽ¾]u­Fu‹÷nƒ™čįADp}AjmcEǒaª³o³ÆÍSƇĈÙDIzçƒñİŸ^ˆKNœ™i—Þñ€[œƒaA²zz‰Ì÷Dœ|[šíijgf‚ÕÞd®|`ƒĆ~„oĠƑô³Ŋ‘D×°¯CsˆøÂ«ì‰UMhTº¨¸ǝêWšÔ„DruÂÇZ£Ćš”PZ„žW”~؋Øv¬gèÂÒw¦X¤Ā´oŬ¬Ž²Ês~€€]®tªašpŎJ¨Öº„_ŠŔ–f”Ő\\Ѝ\\Ĝu–”~m²Ƹ›¸fW‰ĦrƔ}Î^gjdfÔ¡J}\\n C˜¦þWxªJRÔŠu¬ĨĨmF†dM{\\d\\ŠYÊ¢ú@@¦ª²SŠÜsC–}fNècbpRmlØ^g„d¢aÒ¢CZˆZxvÆ¶N¿’¢T@€uCœ¬^ĊðÄn|žlIlŽ—Xhun€[","@@hzUq"]],"encodeOffsets":[[[116744,37216],[116480,33048]]]},"properties":{"cp":[113.665412,34.757975],"name":"河南","childNum":2}},{"id":"420000","geometry":{"type":"MultiPolygon","coordinates":[["@@ASd","@@ls{d","@@¾«}{ra®pîÃ\\™›{øCŠËyyB±„b\\›ò˜Ý˜jK›‡L ]ĎĽÌ’JyÚCƈćÎT´Å´pb©È‘dFin~BCo°BĎĚømvŒ®E^vǾ½Ĝ²Ro‚bÜeNŽ„^ĺ£R†¬lĶ÷YoĖ¥Ě¾|sOr°jY`~I”¾®I†{GqpCgyl{‡£œÍƒÍyPL“¡ƒ¡¸kW‡xYlÙæŠšŁĢzœ¾žV´W¶ùŸo¾ZHxjwfx„GNÁ•³Xéæl¶‰EièIH‰ u’jÌQ~v|sv¶Ôi|ú¢Fh˜Qsğ¦ƒSiŠBg™ÐE^ÁÐ{–čnOÂȞUÎóĔ†ÊēIJ}Z³½Mŧïeyp·uk³DsѨŸL“¶_œÅuèw»—€¡WqÜ]\\‘Ò§tƗcÕ¸ÕFÏǝĉăxŻČƟO‡ƒKÉġÿ×wg”÷IÅzCg†]m«ªGeçÃTC’«[‰t§{loWeC@ps_Bp‘­r‘„f_``Z|ei¡—oċMqow€¹DƝӛDYpûs•–‹Ykıǃ}s¥ç³[§ŸcYЧHK„«Qy‰]¢“wwö€¸ïx¼ņ¾Xv®ÇÀµRĠЋžHMž±cÏd„ƒǍũȅȷ±DSyúĝ£ŤĀàtÖÿï[îb\\}pĭÉI±Ñy…¿³x¯N‰o‰|¹H™ÏÛm‹júË~Tš•u˜ęjCöAwě¬R’đl¯ Ñb­‰ŇT†Ŀ_[Œ‘IčĄʿnM¦ğ\\É[T·™k¹œ©oĕ@A¾w•ya¥Y\\¥Âaz¯ãÁ¡k¥ne£Ûw†E©Êō¶˓uoj_Uƒ¡cF¹­[Wv“P©w—huÕyBF“ƒ`R‹qJUw\\i¡{jŸŸEPïÿ½fć…QÑÀQ{ž‚°‡fLԁ~wXg—ītêݾ–ĺ‘Hdˆ³fJd]‹HJ²…E€ƒoU¥†HhwQsƐ»Xmg±çve›]Dm͂PˆoCc¾‹_h”–høYrŊU¶eD°Č_N~øĹĚ·`z’]Äþp¼…äÌQŒv\\rCŒé¾TnkžŐڀÜa‡“¼ÝƆ̶Ûo…d…ĔňТJq’Pb ¾|JŒ¾fXŠƐîĨ_Z¯À}úƲ‹N_ĒĊ^„‘ĈaŐyp»CÇĕKŠšñL³ŠġMŒ²wrIÒŭxjb[œžn«øœ˜—æˆàƒ ^²­h¯Ú€ŐªÞ¸€Y²ĒVø}Ā^İ™´‚LŠÚm„¥ÀJÞ{JVŒųÞŃx×sxxƈē ģMř–ÚðòIf–Ċ“Œ\\Ʈ±ŒdʧĘD†vČ_Àæ~DŒċ´A®µ†¨ØLV¦êHÒ¤"]],"encodeOffsets":[[[113712,34000],[115612,30507],[113649,34054]]]},"properties":{"cp":[114.298572,30.584355],"name":"湖北","childNum":3}},{"id":"430000","geometry":{"type":"MultiPolygon","coordinates":[["@@—n„FZw","@@かÆá‰½ÔXr—†CO™“…ËR‘ïÿĩ­TooQyšÓ[‹ŅBE¬–ÎÓXa„į§Ã¸G °ITxp‰úxÚij¥Ïš–̾ŠedžÄ©ĸG…œàGh‚€M¤–Â_U}Ċ}¢pczfŠþg¤€’ÇôAV‘","@@ȴÚŠĖÁĐiO“Ĝ«BxDõĚiv—ž–S™Ì}iùŒžÜnšÐºGŠ{Šp°M°yŠÂÒzJ²Ì ÂcXëöüiáÿñŽőФ‚ùTz²CȆȸǎۃƑÐc°dPÎŸğ˶[Ƚu¯½WM¡­Éž“’B·rížnZŸÒ `‡¨GA¾\\pē˜XhÆRC­üWGġu…T靧Ŏѝ©êL•M³}_‘‹E‘Çģc®ęisÁPDmÅ{‰b[Rşs·€kPŸŽƥƒóRo”O‹ŸVŸ~]{g\\“êYƪ¦kÝbiċƵŠGZ»Ěõ…ó·³vŝž£ø@pyö_‹ëŽIkѵ‡bcѧy…×dY؎ªiþžˆUjŸŅ³C}ÁN‡»hĻħƏâƓK—ƒA·³CQ±µ§¿AUŠƑ¹AŠtćOw™D]ŒJUÖgk¯b£‘ylƒ›ZƒFËѱH™­}˜•EbóľA–¡»Ku¦·‘³†åş¥ùBDž^{ÌC´­¦ŷJ£^[†‹—ª¿‡ğ|‹ƅ…•N… skóā‡¹¿€ï]ă~÷O§­@—Vm¡‹Qđ¦¢Ĥ{ºjԏŽŒª¥nf´•~ÕoŸž×Ûą‹Gû¥cÑ[Zœ‰¶˜ŨβSÊǔƐ˜ƀƒ’AÚŌ¦QؼrŭŽ­«}NÏürʬŒmjr€@ĘrTW ­SsdHzƓ^ÇÂyUi¯DÅYlŹu{hTœ}mĉ–¹¥ě‰Dÿë©ıÓ[Oº£ž“¥ót€ł¹MՄžƪƒ`Pš…Di–ÛUоÅ‌ìˆU’ñB“È£ýhe‰dy¡oċ€`pfmjP~‚kZa…ZsÐd°wj§ƒ@€Ĵ®w~^‚kÀÅKvNmX\\¨a“”сqvíó¿F„¤¡@ũÑVw}S@j}¾«pĂr–ªg àÀ²NJ¶¶Dô…K‚|^ª†Ž°LX¾ŴäPᜣEXd›”^¶›IJÞܓ~‘u¸ǔ˜Ž›MRhsR…e†`ÄofIÔ\\Ø  i”ćymnú¨cj ¢»–GČìƊÿШXeĈ¾Oð Fi ¢|[jVxrIQŒ„_E”zAN¦zLU`œcªx”OTu RLĪpUžĪ‚ȴ^ŎµªÉžFx…Ü€f¤ºgIJèy°Áb[¦Zb¦–z½xBĖ@ªpº›˜jS´rVźOd©ʪiĎă’JP‡ž`"]],"encodeOffsets":[[[115640,30489],[112577,27316],[114113,30649]]]},"properties":{"cp":[112.982279,28.19409],"name":"湖南","childNum":3}},{"id":"440000","geometry":{"type":"MultiPolygon","coordinates":[["@@QdˆAsa","@@ƒlxDRm","@@sbhNLo","@@Ă ý","@@WltOY[","@@Krœ]‰S","@@e„~AS}","@@I|„Mym","@@ƒÛ³LSŒž²Q","@@nvºB–ë¥cÕº","@@zdšÛ›JmŠ","@@†°³","@@a yAª¸ËJIx،@€ĀHÉÕZ™o•fo…o","@@šs‰ŗÃÔėAƁ›ZšÄ ~°ČP‚‹ºb","@@‹¶Ý’Ì‚vmĞh¹Ĺ","@@HœŠdSjĒ¢D}war…“u«ZqadY{K","@@elŒ\\LqqO","@@~rMmX","@@f„^E","@@øPªoj÷ÍÝħXČx”°Q¨ıXJp","@@gÇƳˆˆ–m’Žxa†tfu","@@E–ÆC½‘","@@¸B_¶ekWvSi‡vc•}p}Ăº¾NĎyj¦Èm thœ†_®žÄ}ˆ»âUzL™Ë‹²‘Aƒā¡ßH©Ùñ}wkNÕ¹ÇO½¿£ēUlƒaUìIžÇª`ŠuTÅxYĒÖ¼k֞’µ‚MžjJÚwn\\h‘œĒv]îh|’È›Ƅøègž¸Ķß ĉĈWb¹ƀdéƌNTtP[ŠöSvrCZžžaGuœbo´ŖÒÇА~¡zCI…özx¢„Pn‹•‰Èñ @ŒĥÒ¦†]ƜŽX³ăĔñiiÄÓVépKG½Ä‘ÓávYo–C·sit‹iaÀy„ŧΡÈYDÑům}‰ý|m[węõĉZÅxUO}÷N¹³ĉo_qtă“qwµŁYلǝŕ¹tïÛUïmRCº…ˆĭ|µ›ÕÊK™½R‘ē ó]‘–GªęAx–ŸNqSF•|ām‡¡diď×YïYWªʼnOeÚtĐ«zđ¹T…ā‡úE™áÎÁWw헟HcòßÎſ¿Çdğ·ùT×Çūʄ¡XgWÀLJğ·¿ÃˆOj YÇ÷Sğ³kzőõm‰™ĝ—[³‹¡VÙæÅöM̳¹pÁaËýý©D©Ü“JŹƕģGą¤{Ùū…ǘO²«BƱéA—Ò‰ĥ‡¡«BhlmtÃPµyU¯uc“d·w_bŝcīímGOŽ€GBȅ‰ŹãĻFŷŽŕ@Óoo¿ē‹±ß}Ž}ÓF÷tIJWÈCőâUâǙI›ğʼn©I›ijEׅÁ”³AĥDĈ±ÌŒÜӔĨ£L]ĈÙƺZǾĆĖMĸĤfŒÎĵl•ŨnȈ‘ĐtF”Š–FĤ–‚êk¶œ^k°f¶gŠŽœ}®Fa˜f`vXŲxl˜„¦–ÔÁ²¬ÐŸ¦pqÊ̲ˆi€XŸØRDÎ}†Ä@ZĠ’s„x®AR~®ETtĄZ†–ƈfŠŠHâÒÐA†µ\\S¸„^wĖkRzŠalŽŜ|E¨ÈNĀňZTŒ’pBh£\\ŒĎƀuXĖtKL–¶G|Ž»ĺEļĞ~ÜĢÛĊrˆO˜Ùîvd]nˆ¬VœÊĜ°R֟pM††–€ƀ¬HbwžEÀˆ˜©Œž\\…¤]ŸI®¥D³|ˎ]CúAЦ…æ’´¥¸Lv¼€•¢ĽBaô–F~—š®²GÌҐEY„„œzk¤’°ahlV՞I^‹šCxĈPŽsB‰ƒºV‰ÀB¶¨R²´D","@@OŽR"]],"encodeOffsets":[[[117381,22988],[116552,22934],[116790,22617],[116973,22545],[116444,22536],[116931,22515],[116496,22490],[116453,22449],[113301,21439],[118726,21604],[118709,21486],[113210,20816],[115482,22082],[113171,21585],[113199,21590],[115232,22102],[115739,22373],[115134,22184],[113056,21175],[119573,21271],[119957,24020],[115859,22356],[116680,26053],[116561,22649]]]},"properties":{"cp":[113.280637,23.125178],"name":"广东","childNum":24}},{"id":"450000","geometry":{"type":"MultiPolygon","coordinates":[["@@H– TI¡U","@@Ɣ_LÊFZg…čP­kini«‹qǀcz͔Y®¬Ů»qR×ō©DՄ‘§ƙǃŵTÉĩ±ŸıdÑnYY›IJvNĆÌØÜ Öp–}e³¦m‹©iÓ|¹Ÿħņ›|ª¦QF¢Â¬ʖovg¿em‡^ucäāmÇÖåB¡Õçĝ}FϼĹ{µHK•sLSđƃr‹č¤[Ag‘oS‹ŇYMÿ§Ç{Fśbky‰lQxĕƒ]T·¶[B…ÑÏGáşşƇe€…•ăYSs­FQ}­Bƒw‘tYğÃ@~…C̀Q ×W‡j˱rÉ¥oÏ ±«ÓÂ¥•ƒ€k—ŽwWűŽue_b—­E›~‰µh¯ecl¯›Ïr¯‡E쉕Jƒğƒ}žw³–Ƈē`ãògK_ÛsUʝ“ćğ¶hŒöŒO¤Ǜn³Žc‘`¡yi–ę–‘[ďĵűMę§]X˜Î_‚훘Û]é’ÛUćİÕBƣ±…dƒy¹T^džûÅÑŦ·‡PĻþÙ`K€¦˜…¢ÍeœĥR¿Œ³£[~Œäu¼dl‰t‚†W¸oRM¢ď\\zœ}Æzdvň–{ÎXF¶°Â_„ÒÂÏL©Ö•TmuŸ¼ãl‰›īkiqéfA„·Êµ\\őDc¥ÝF“y›Ôć˜c€űH_hL܋êĺШc}rn`½„Ì@¸¶ªVLŒŠhŒ‹\\•Ţĺk~ŽĠið°|gŒtTĭĸ^x‘vK˜VGréAé‘bUu›MJ‰VÃO¡…qĂXËS‰ģãlýàŸ_ju‡YÛÒB†œG^˜é֊¶§ŽƒEG”ÅzěƒƯ¤Ek‡N[kdåucé¬dnYpAyČ{`]þ±X’\\’ÞÈk‚¡Ĭj†àh„ÂƄ¢H茠Ŕ⪃LƒĒ^Öm¶ħĊAǦė¸zÚGn£¾›rªŀÜt¬@֛ڈSx~øOŒ˜ŶÐÂæȠ\\„ÈÜObĖw^oބLf¬°bI lTØB̈F£Ć¹gñĤaY“t¿¤VSñœK¸¤nM†¼‚JE±„½¸šŠño‹ÜCƆæĪ^ŠĚQÖ¦^‡ˆˆf´Q†üÜʝz¯šlzUĺš@쇀p¶n]sxtx¶@„~ÒĂJb©gk‚{°‚~c°`ԙ¬rV\\“la¼¤ôá`¯¹LC†ÆbŒxEræO‚v[H­˜„[~|aB£ÖsºdAĐzNÂðsŽÞƔ…Ĥªbƒ–ab`ho¡³F«èVZs„\\\\Œ™ÔRzpp®SŽĪº¨ÖƒºN…ij„d`’a”¦¤F³¢@„`¢ĨĀìhYvlŠĆº¦Ċ•~nS›|gźv^kGƄÀè·"]],"encodeOffsets":[[[111707,21520],[113706,26955]]]},"properties":{"cp":[108.320004,22.82402],"name":"广西","childNum":2}},{"id":"460000","geometry":{"type":"Polygon","coordinates":["@@š¦Ŝil¢”XƦ‘ƞò–ïè§ŞCêɕrŧůÇąĻõ™·ĉ³œ̅kÇm@ċȧƒŧĥ‰Ľʉ­ƅſ“ȓÒ˦ŝE}ºƑ[ÍĜȋ gÎfǐÏĤ¨êƺ\\Ɔ¸ĠĎvʄȀœÐ¾jNðĀÒRŒšZdž™zМŒĊ†¢DÀɘZ"],"encodeOffsets":[[112750,20508]]},"properties":{"cp":[110.33119,20.031971],"name":"海南","childNum":1}},{"id":"510000","geometry":{"type":"MultiPolygon","coordinates":[["@@LqSn","@@ĆOìÛÐ@Ğ™ǔNY{¤Á§d…i“´ezÝúØãwŒƒIŸþËQǦÃqɞSJ»ĂéʔõÔƁİlƞ¹„§Ĭqt‘ÀƄmÀêErĒtD®ċæcQƒ”E®³^ĭ¥©l}äQto˜ŖÜqƎkµ–„ªÔĻĴ¡@Ċ°B²Èw^^RsºT£ڿœQP‘JvÄz„^Đ¹Æ¯fLà´GC²‘dt˜­ĀRt¼¤ĦOðğfÔðDŨŁĞƘïžPȆ®âbMüÀXZ ¸£@Ś›»»QÉ­™]d“sÖ×_͖_ÌêŮPrĔĐÕGĂeZÜîĘqBhtO ¤tE[h|Y‹Ô‚ZśÎs´xº±UŒ’ñˆt|O’ĩĠºNbgþŠJy^dÂY Į„]Řz¦gC‚³€R`Šz’¢AjŒ¸CL„¤RÆ»@­Ŏk\\Ç´£YW}z@Z}‰Ã¶“oû¶]´^N‡Ò}èN‚ª–P˜Íy¹`S°´†ATe€VamdUĐwʄvĮÕ\\ƒu‹Æŗ¨Yp¹àZÂm™Wh{á„}WØǍ•Éüw™ga§ßAYŸrÅÂQĀÕ¬LŐý®X˜øxª½Ű¦¦[€—þ„`ÜUÖ´òrÙŠ°²Äk„ijnDX{Uƒ~ET{ļº¦PZc”jF²Ė@Žp˜g€ˆ¨“B{ƒu¨ŦyhoÚD®¯¢˜ WòàFΤ¨GDäz¦kŮPœġq˚¥À]€Ÿ˜eŽâÚ´ªKxī„Pˆ—Ö|æ[xäJÞĥ‚s’NÖ½ž€I†¬nĨY´®Ð—ƐŠ€mD™ŝuäđđEb…e’e_™v¡}ìęNJē}q”É埁T¯µRs¡M@}ůa†a­¯wvƉåZwž\\Z{åû`Ÿ†[±oi•‘JDŦ]‘‰ĕãïrG •réÏ·~ąSfy×͂·ºſƽĵȁŗūmHQ¡Y¡®ÁÃ×t«ƒ­Tƒ¤J–JJŒyJ•ÈŠ`Ohߦ¡uËhIyCjmÿw…ZG……Ti‹SˆsO‰žB²ŸfNmsPaˆ{M{ŠõE‘^Hj}gYpaeuž¯‘oáwHjÁ½M¡pM“–uå‡mni{fk”\\oƒÎqCw†EZ¼K›ĝŠƒAy{m÷L‡wO×SimRI¯rK™õBS«sFe‡]fµ¢óY_ÆPRcue°Cbo׌bd£ŌIHgtrnyPt¦foaXďx›lBowz‹_{ÊéWiêE„GhܸºuFĈIxf®Ž•Y½ĀǙ]¤EyŸF²ċ’w¸¿@g¢§RGv»–áŸW`ÃĵJwi]t¥wO­½a[׈]`Ãi­üL€¦LabbTÀå’c}Íh™Æhˆ‹®BH€î|Ék­¤S†y£„ia©taį·Ɖ`ō¥Uh“O…ƒĝLk}©Fos‰´›Jm„µlŁu—…ø–nÑJWΪ–YÀïAetTžŅ‚ӍG™Ë«bo‰{ıwodƟ½ƒžOġܑµxàNÖ¾P²§HKv¾–]|•B‡ÆåoZ`¡Ø`ÀmºĠ~ÌЧnDž¿¤]wğ@sƒ‰rğu‰~‘Io”[é±¹ ¿žſđӉ@q‹gˆ¹zƱřaí°KtǤV»Ã[ĩǭƑ^ÇÓ@ỗs›Zϕ‹œÅĭ€Ƌ•ěpwDóÖሯneQˌq·•GCœýS]xŸ·ý‹q³•O՜Œ¶Qzßti{ř‰áÍÇWŝŭñzÇW‹pç¿JŒ™‚Xœĩè½cŒF–ÂLiVjx}\\N†ŇĖ¥Ge–“JA¼ÄHfÈu~¸Æ«dE³ÉMA|b˜Ò…˜ćhG¬CM‚õŠ„ƤąAvƒüV€éŀ‰_V̳ĐwQj´·ZeÈÁ¨X´Æ¡Qu·»Ÿ“˜ÕZ³ġqDo‰y`L¬gdp°şŠp¦ėìÅĮZްIä”h‚‘ˆzŠĵœf²å ›ĚрKp‹IN|‹„Ñz]ń……·FU×é»R³™MƒÉ»GM«€ki€™ér™}Ã`¹ăÞmȝnÁîRǀ³ĜoİzŔwǶVÚ£À]ɜ»ĆlƂ²Ġ…þTº·àUȞÏʦ¶†I’«dĽĢdĬ¿–»Ĕ׊h\\c¬†ä²GêëĤł¥ÀǿżÃÆMº}BÕĢyFVvw–ˆxBèĻĒ©Ĉ“t@Ğû¸£B¯¨ˋäߜkŽķŒ½ª“ôNԓ~t¼Ŵ„u„œ^s¼{TA¼ø°¢İªDè¾Ň¶ÝJ‘®Z´ğ~Sn|ªWÚ©òzPOȸ‚bð¢|‹øĞŠŒœŠA"]],"encodeOffsets":[[[108815,30935],[100197,35028]]]},"properties":{"cp":[104.065735,30.659462],"name":"四川","childNum":2}},{"id":"520000","geometry":{"type":"MultiPolygon","coordinates":[["@@†G\\†lY£‘cj","@@q‚|ˆ‚mc¯vωV","@@hÑ£Is‡NgßH†›HªķÃh_¹ƒ¡ĝħń¦uيùŽgS¯JHŸ|sÝÅtÁïyMDč»eÕtA¤{b\\}—ƒG®u\\åPFq‹wÅaD…žK°ºâ_£ùbµ”mÁ‹ÛœĹM[q|hlaªāI}тƒµ@swtwm^oµˆD鼊yV™ky°ÉžûÛR…³‚‡eˆ‡¥]RՋěħ[ƅåÛDpŒ”J„iV™™‰ÂF²I…»mN·£›LbÒYb—WsÀbŽ™pki™TZĄă¶HŒq`……ĥ_JŸ¯ae«ƒKpÝx]aĕÛPƒÇȟ[ÁåŵÏő—÷Pw}‡TœÙ@Õs«ĿÛq©½œm¤ÙH·yǥĘĉBµĨÕnđ]K„©„œá‹ŸG纍§Õßg‡ǗĦTèƤƺ{¶ÉHÎd¾ŚÊ·OÐjXWrãLyzÉAL¾ę¢bĶėy_qMĔąro¼hĊžw¶øV¤w”²Ĉ]ʚKx|`ź¦ÂÈdr„cȁbe¸›`I¼čTF´¼Óýȃr¹ÍJ©k_șl³´_pН`oÒh޶pa‚^ÓĔ}D»^Xyœ`d˜[Kv…JPhèhCrĂĚÂ^Êƌ wˆZL­Ġ£šÁbrzOIl’MM”ĪŐžËr×ÎeŦŽtw|Œ¢mKjSǘňĂStÎŦEtqFT†¾†E쬬ôxÌO¢Ÿ KгŀºäY†„”PVgŎ¦Ŋm޼VZwVlŒ„z¤…ž£Tl®ctĽÚó{G­A‡ŒÇgeš~Αd¿æaSba¥KKûj®_ć^\\ؾbP®¦x^sxjĶI_Ä X‚⼕Hu¨Qh¡À@Ëô}ޱžGNìĎlT¸ˆ…`V~R°tbÕĊ`¸úÛtπFDu€[ƒMfqGH·¥yA‰ztMFe|R‚_Gk†ChZeÚ°to˜v`x‹b„ŒDnÐ{E}šZ˜è€x—†NEފREn˜[Pv@{~rĆAB§‚EO¿|UZ~ì„Uf¨J²ĂÝÆ€‚sª–B`„s¶œfvö¦ŠÕ~dÔq¨¸º»uù[[§´sb¤¢zþFœ¢Æ…Àhˆ™ÂˆW\\ıŽËI݊o±ĭŠ£þˆÊs}¡R]ŒěƒD‚g´VG¢‚j±®è†ºÃmpU[Á›‘Œëº°r›ÜbNu¸}Žº¼‡`ni”ºÔXĄ¤¼Ôdaµ€Á_À…†ftQQgœR—‘·Ǔ’v”}Ýלĵ]µœ“Wc¤F²›OĩųãW½¯K‚©…]€{†LóµCIµ±Mß¿hŸ•©āq¬o‚½ž~@i~TUxð´Đhw­ÀEîô‚uĶ‚’“‚b[§nWuMÆJl½]vuıµb"]],"encodeOffsets":[[[112158,27383],[112105,27474],[112095,27476]]]},"properties":{"cp":[106.713478,26.578343],"name":"贵州","childNum":3}},{"id":"530000","geometry":{"type":"Polygon","coordinates":["@@[„ùx½}ÑRH‘YīĺûsÍn‘iEoã½Ya²ė{c¬ĝg•ĂsA•ØÅwď‚õzFjw}—«Dx¿}UũlŸê™@•HÅ­F‰¨ÇoJ´Ónũuą¡Ã¢pÒŌ“Ø TF²‚xa²ËX€‚cʋlHîAßËŁkŻƑŷÉ©h™W­æßU‡“Ës¡¦}•teèÆ¶StǀÇ}Fd£j‹ĈZĆÆ‹¤T‚č\\Dƒ}O÷š£Uˆ§~ŃG™‚åŃDĝ¸œTsd¶¶Bªš¤u¢ŌĎo~t¾ÍŶÒtD¦Ú„iôö‰€z›ØX²ghįh½Û±¯€ÿm·zR¦Ɵ`ªŊÃh¢rOԍ´£Ym¼èêf¯ŪĽn„†cÚbŒw\\zlvWžªâˆ ¦g–mĿBş£¢ƹřbĥkǫßeeZkÙIKueT»sVesb‘aĕ  ¶®dNœĄÄpªyސ¼—„³BE˜®l‡ŽGœŭCœǶwêżĔÂe„pÍÀQƞpC„–¼ŲÈ­AÎô¶R„ä’Q^Øu¬°š_Èôc´¹ò¨P΢hlϦ´Ħ“Æ´sâDŽŲPnÊD^¯°’Upv†}®BP̪–jǬx–Söwlfòªv€qĸ|`H€­viļ€ndĜ­Ćhň•‚em·FyށqóžSᝑ³X_ĞçêtryvL¤§z„¦c¦¥jnŞk˜ˆlD¤øz½ĜàžĂŧMÅ|áƆàÊcðÂF܎‚áŢ¥\\\\º™İøÒÐJĴ‡„îD¦zK²ǏÎEh~’CD­hMn^ÌöÄ©ČZÀžaü„fɭyœpį´ěFűk]Ôě¢qlÅĆÙa¶~Äqššê€ljN¬¼H„ÊšNQ´ê¼VظE††^ŃÒyŒƒM{ŒJLoÒœęæŸe±Ķ›y‰’‡gã“¯JYÆĭĘëo¥Š‰o¯hcK«z_pŠrC´ĢÖY”—¼ v¸¢RŽÅW³Â§fǸYi³xR´ďUˊ`êĿU„û€uĆBƒƣö‰N€DH«Ĉg†——Ñ‚aB{ÊNF´¬c·Åv}eÇÃGB»”If•¦HňĕM…~[iwjUÁKE•Ž‹¾dĪçW›šI‹èÀŒoÈXòyŞŮÈXâÎŚŠj|àsRy‹µÖ›–Pr´þŒ ¸^wþTDŔ–Hr¸‹žRÌmf‡żÕâCôox–ĜƌÆĮŒ›Ð–œY˜tâŦÔ@]ÈǮƒ\\μģUsȯLbîƲŚºyh‡rŒŠ@ĒԝƀŸÀ²º\\êp“’JŠ}ĠvŠqt„Ġ@^xÀ£È†¨mËÏğ}n¹_¿¢×Y_æpˆÅ–A^{½•Lu¨GO±Õ½ßM¶w’ÁĢۂP‚›Ƣ¼pcIJxŠ|ap̬HšÐŒŊSfsðBZ¿©“XÏÒK•k†÷Eû¿‰S…rEFsÕūk”óVǥʼniTL‚¡n{‹uxţÏh™ôŝ¬ğōN“‘NJkyPaq™Âğ¤K®‡YŸxÉƋÁ]āęDqçgOg†ILu—\\_gz—]W¼ž~CÔē]bµogpў_oď`´³Țkl`IªºÎȄqÔþž»E³ĎSJ»œ_f·‚adÇqƒÇc¥Á_Źw{™L^ɱćx“U£µ÷xgĉp»ĆqNē`rĘzaĵĚ¡K½ÊBzyäKXqiWPÏɸ½řÍcÊG|µƕƣG˛÷Ÿk°_^ý|_zċBZocmø¯hhcæ\\lˆMFlư£Ĝ„ÆyH“„F¨‰µêÕ]—›HA…àӄ^it `þßäkŠĤÎT~Wlÿ¨„ÔPzUC–NVv [jâôDôď[}ž‰z¿–msSh‹¯{jïğl}šĹ[–őŒ‰gK‹©U·µË@¾ƒm_~q¡f¹…ÅË^»‘f³ø}Q•„¡Ö˳gͱ^ǁ…\\ëÃA_—¿bW›Ï[¶ƛ鏝£F{īZgm@|kHǭƁć¦UĔťƒ×ëǟ…eċ¼ȡȘÏíBə£āĘPªij¶“ʼnÿ‡y©n‰ď£G¹¡I›Š±LÉĺÑdĉ܇W¥˜‰}g˜Á†{aqÃ¥aŠıęÏZ—Á`"],"encodeOffsets":[[104636,22969]]},"properties":{"cp":[102.712251,25.040609],"name":"云南","childNum":1}},{"id":"540000","geometry":{"type":"Polygon","coordinates":["@@hžľxŽŖ‰xƒÒVކºÅâAĪÝȆµę¯Ňa±r_w~uSÕň‘qOj]ɄQ…£Z……UDûoY’»©M[‹L¼qãË{V͕çWViŽ]ë©Ä÷àyƛh›ÚU°ŒŒa”d„cQƒ~Mx¥™caŸÛcSyF—ցk­ŒuRýq¿Ôµ•QĽ³aG{¿FµëªéĜÿª@¬·–K‰·àariĕĀ«V»Ŷ™Ĵū˜gèLǴŇƶaf‹tŒèBŚ£^Šâ†ǐÝ®–šM¦ÁǞÿ¬LhŸŽJ¾óƾƺcxw‹f]Y…´ƒ¦|œQLn°aœdĊ…œ\\¨o’œǀÍŎœ´ĩĀd`tÊQŞŕ|‚¨C^©œĈ¦„¦ÎJĊ{ŽëĎjª²rЉšl`¼Ą[t|¦St辉PŒÜK¸€d˜Ƅı]s¤—î_v¹ÎVòŦj˜£Əsc—¬_Ğ´|٘¦Avަw`ăaÝaa­¢e¤ı²©ªSªšÈMĄwžÉØŔì@T‘¤—Ę™\\õª@”þo´­xA s”ÂtŎKzó²Çȵ¢rž^nĊ­Æ¬×üGž¢‚³ {âĊ]š™G‚~bÀgVjzlhǶf€žOšfdЉªB]pj„•TO–tĊ‚n¤}®¦ƒČ¥d¢¼»ddš”Y¼Žt—¢eȤJ¤}Ǿ¡°§¤AГlc@ĝ”sªćļđAç‡wx•UuzEÖġ~AN¹ÄÅȀݦ¿ģŁéì±H…ãd«g[؉¼ēÀ•cīľġ¬cJ‘µ…ÐʥVȝ¸ßS¹†ý±ğkƁ¼ą^ɛ¤Ûÿ‰b[}¬ōõÃ]ËNm®g@•Bg}ÍF±ǐyL¥íCˆƒIij€Ï÷њį[¹¦[⚍EÛïÁÉdƅß{âNÆāŨߝ¾ě÷yC£‡k­´ÓH@¹†TZ¥¢įƒ·ÌAЧ®—Zc…v½ŸZ­¹|ŕWZqgW“|ieZÅYVӁqdq•bc²R@†c‡¥Rã»Ge†ŸeƃīQ•}J[ғK…¬Ə|o’ėjġĠÑN¡ð¯EBčnwôɍėªƒ²•CλŹġǝʅįĭạ̃ūȹ]ΓͧgšsgȽóϧµǛ†ęgſ¶ҍć`ĘąŌJޚä¤rÅň¥ÖÁUětęuůÞiĊÄÀ\\Æs¦ÓRb|Â^řÌkÄŷ¶½÷‡f±iMݑ›‰@ĥ°G¬ÃM¥n£Øą‚ğ¯ß”§aëbéüÑOčœk£{\\‘eµª×M‘šÉfm«Ƒ{Å׃Gŏǩãy³©WÑăû‚··‘Q—òı}¯ã‰I•éÕÂZ¨īès¶ZÈsŽæĔTŘvŽgÌsN@îá¾ó@‰˜ÙwU±ÉT廣TđŸWxq¹Zo‘b‹s[׌¯cĩv‡Œėŧ³BM|¹k‰ªħ—¥TzNYnݍßpęrñĠĉRS~½ŠěVVе‚õ‡«ŒM££µB•ĉ¥áºae~³AuĐh`Ü³ç@BۘïĿa©|z²Ý¼D”£à貋ŸƒIƒû›I ā€óK¥}rÝ_Á´éMaň¨€~ªSĈ½Ž½KÙóĿeƃÆBŽ·¬ën×W|Uº}LJrƳ˜lŒµ`bÔ`QˆˆÐÓ@s¬ñIŒÍ@ûws¡åQÑßÁ`ŋĴ{Ī“T•ÚÅTSij‚‹Yo|Ç[ǾµMW¢ĭiÕØ¿@˜šMh…pÕ]j†éò¿OƇĆƇp€êĉâlØw–ěsˆǩ‚ĵ¸c…bU¹ř¨WavquSMzeo_^gsÏ·¥Ó@~¯¿RiīB™Š\\”qTGªÇĜçPoŠÿfñòą¦óQīÈáP•œābß{ƒZŗĸIæÅ„hnszÁCËìñšÏ·ąĚÝUm®ó­L·ăU›Èíoù´Êj°ŁŤ_uµ^‘°Œìǖ@tĶĒ¡Æ‡M³Ģ«˜İĨÅ®ğ†RŽāð“ggheÆ¢z‚Ê©Ô\\°ÝĎz~ź¤Pn–MĪÖB£Ÿk™n鄧żćŠ˜ĆK„ǰ¼L¶è‰âz¨u¦¥LDĘz¬ýÎmĘd¾ß”Fz“hg²™Fy¦ĝ¤ċņbΛ@y‚Ąæm°NĮZRÖíŽJ²öLĸÒ¨Y®ƌÐV‰à˜tt_ڀÂyĠzž]Ţh€zĎ{†ĢX”ˆc|šÐqŽšfO¢¤ög‚ÌHNŽ„PKŖœŽ˜Uú´xx[xˆvĐCûŠìÖT¬¸^}Ìsòd´_އKgžLĴ…ÀBon|H@–Êx˜—¦BpŰˆŌ¿fµƌA¾zLjRxжF”œkĄźRzŀˆ~¶[”´Hnª–VƞuĒ­È¨ƎcƽÌm¸ÁÈM¦x͊ëÀxdžB’šú^´W†£–d„kɾĬpœw‚˂ØɦļĬIŚœÊ•n›Ŕa¸™~J°î”lɌxĤÊÈðhÌ®‚g˜T´øŽàCˆŽÀ^ªerrƘdž¢İP|Ė ŸWœªĦ^¶´ÂL„aT±üWƜ˜ǀRšŶUńšĖ[QhlLüA†‹Ü\\†qR›Ą©"],"encodeOffsets":[[90849,37210]]},"properties":{"cp":[91.132212,29.660361],"name":"西藏","childNum":1}},{"id":"610000","geometry":{"type":"Polygon","coordinates":["@@¸œÂW¢xR­—ƒFq§uF—Œ@NŸ¢XLƒŠRMº[ğȣſï|¥J™kc`sʼnǷ’£Y³‹WN«ùM‘ëï³ÛIg÷±mTșڍÒķø©—þ¥ƒy‚ÓŸğęmWµÎumZyOŅƟĥÓ~sÑL¤µaŅY¦ocyZ{‰y c]{ŒTa©ƒ`U_Ěē£ωÊƍKù’K¶ȱÝƷ§{û»ÅÁȹÍéuij|¹cÑd‘ŠìUYƒŽO‘uF–ÕÈYvÁCqӃT•Ǣí§·S¹NgŠV¬ë÷Át‡°Dد’C´ʼnƒópģ}„ąiE˅FŸŸéGU¥×K…§­¶³B‹Č}C¿åċ`wġB·¤őcƭ²ő[Å^axwQO…ñJÙïŚ•ĤNĔŸwƇˆÄŠńwĪ­Šo[„_KÓª³“ÙnK‰Çƒěœÿ]ď€ă_d©·©Ýŏ°Ù®g]±„Ÿ‡ßš×¥¬÷m\\›iaǑkěX{¢|ZKlçhLt€Ňîŵ€œè[€É@ƉĄEœ‡tƇÏ˜³­ħZ«mJ…›×¾‘MtÝĦ£IwÄå\\Õ{‡˜ƒOwĬ©LÙ³ÙT“ª¿^™¦r̛ĢŭO¥lãyC§HÍ£ßEñŸX¡—­°ÙCgpťz‘ˆb`wI„vA|¥”‡—hoĕ@E±“iYd¥OÿµÇvPŒW|mCƒĴŜǂ҈W¶¸AĜh^Wx{@„¬‚­F¸¡„ķn£P|ŸªĴ@^ĠĈæb–Ôc¶l˜Yi…–^Mi˜cϰÂ[ä€vï¶gv@À“Ĭ·lJ¸sn|¼u~a]’ÆÈtŌºJp’ƒþ£KKf~ЦUbyäIšĺãn‡Ô¿^­žŵMT–hĠܤko¼Ŏìąǜh`[tŒRd²IJ_œXPrɲ‰l‘‚XžiL§àƒ–¹ŽH˜°Ȧqº®QC—bA†„ŌJ¸ĕÚ³ĺ§ `d¨YjžiZvRĺ±öVKkjGȊĐePОZmļKÀ€‚[ŠŽ`ösìh†ïÎoĬdtKÞ{¬èÒÒBŒÔpIJÇĬJŊ¦±J«ˆ[©ārH€µàåVKe§|P²ÇÓ·vUz‰gnN¾yI@oŸHĆۄķhx“e‘n¡QQ’±”ƝJ‹ǖRbzy€¸ËАl›¼EºpĤ¼Œx¼½~Ğ’”à@†ÚüdK^ˆmÌSjˆp²—ȮµšûG™Ħ}Ħšðǚ¶òƄ€jɂz°{ºØkÈęâ¦jª‚Bg‚\\œċ°s¬Ž’]jžú ‚E”Ȍdž¬s„t‡”RˆÆdĠݎwܔ¸ôW¾ƮłÒ_{’Ìšû¼„jº¹¢GǪÒ¯ĘƒZ`ºŊƒecņąš~BÂgzpâēòYƲȐπ"],"encodeOffsets":[[113634,40474]]},"properties":{"cp":[108.948024,34.263161],"name":"陕西","childNum":1}},{"id":"620000","geometry":{"type":"MultiPolygon","coordinates":[["@@Vu_^","@@ų‹EĠtt~nkh`Q‰¦ÅÄÜdw˜Ab×ĠąJˆ¤DüègĺqBqœj°lI¡Ĩ¶šĖIHdš‰ŠjΑBаaZˆ¢KJŽ’O[|A£žDx}Nì•HUnrk„ kp€¼Y kMJn[aG‚áÚÏ[½rc†}aQxOgsPMnUs‡nc‹Z…ž–sKúvA›t„Þġ’£®ĀYKdnFwš¢JE°”Latf`¼h¬we|€Æ‡šbj}GA€·~WŽ”—`†¢MC¤tL©IJ°qdf”O‚“bÞĬ¹ttu`^ZúE`Œ[@„Æsîz®¡’C„ƳƜG²“R‘¢R’m”fŽwĸg܃‚ą G@pzJM½mŠhVy¸uÈÔO±¨{LfæU¶ßGĂq\\ª¬‡²I‚¥IʼnÈīoı‹ÓÑAçÑ|«LÝcspīðÍg…të_õ‰\\ĉñLYnĝg’ŸRǡÁiHLlõUĹ²uQjYi§Z_c¨Ÿ´ĹĖÙ·ŋI…ƒaBD˜­R¹ȥr—¯G•ºß„K¨jWk’ɱŠOq›Wij\\a­‹Q\\sg_ĆǛōëp»£lğۀgS•ŶN®À]ˆÓäm™ĹãJaz¥V}‰Le¤L„ýo‘¹IsŋÅÇ^‘Žbz…³tmEÁ´aйcčecÇN•ĊãÁ\\蝗dNj•]j†—ZµkÓda•ćå]ğij@ ©O{¤ĸm¢ƒE·®ƒ«|@Xwg]A챝‡XǁÑdzªc›wQÚŝñsÕ³ÛV_ýƒ˜¥\\ů¥©¾÷w—Ž©WÕÊĩhÿÖÁRo¸V¬âDb¨šhûx–Ê×nj~Zâƒg|šXÁnßYoº§ZÅŘvŒ[„ĭÖʃuďxcVbnUSf…B¯³_Tzº—ΕO©çMÑ~Mˆ³]µ^püµ”ŠÄY~y@X~¤Z³€[Èōl@®Å¼£QKƒ·Di‹¡By‘ÿ‰Q_´D¥hŗyƒ^ŸĭÁZ]cIzý‰ah¹MĪğP‘s{ò‡‹‘²Vw¹t³Ŝˁ[ŽÑ}X\\gsFŸ£sPAgěp×ëfYHāďÖqēŭOÏë“dLü•\\iŒ”t^c®šRʺ¶—¢H°mˆ‘rYŸ£BŸ¹čIoľu¶uI]vģSQ{ƒUŻ”Å}QÂ|̋°ƅ¤ĩŪU ęĄžÌZҞ\\v˜²PĔ»ƢNHƒĂyAmƂwVmž`”]ȏb•”H`‰Ì¢²ILvĜ—H®¤Dlt_„¢JJÄämèÔDëþgºƫ™”aʎÌrêYi~ ÎݤNpÀA¾Ĕ¼b…ð÷’Žˆ‡®‚”üs”zMzÖĖQdȨý†v§Tè|ªH’þa¸|šÐ ƒwKĢx¦ivr^ÿ ¸l öæfƟĴ·PJv}n\\h¹¶v†·À|\\ƁĚN´Ĝ€çèÁz]ġ¤²¨QÒŨTIl‡ªťØ}¼˗ƦvÄùØE‹’«Fï˛Iq”ōŒTvāÜŏ‚íÛߜÛV—j³âwGăÂíNOŠˆŠPìyV³ʼnĖýZso§HіiYw[߆\\X¦¥c]ÔƩÜ·«j‡ÐqvÁ¦m^ċ±R™¦΋ƈťĚgÀ»IïĨʗƮްƝ˜ĻþÍAƉſ±tÍEÕÞāNU͗¡\\ſčåÒʻĘm ƭÌŹöʥ’ëQ¤µ­ÇcƕªoIýˆ‰Iɐ_mkl³ă‰Ɠ¦j—¡Yz•Ňi–}Msßõ–īʋ —}ƒÁVmŸ_[n}eı­Uĥ¼‘ª•I{ΧDӜƻėoj‘qYhĹT©oūĶ£]ďxĩ‹ǑMĝ‰q`B´ƃ˺Ч—ç~™²ņj@”¥@đ´ί}ĥtPńǾV¬ufӃÉC‹tÓ̻‰…¹£G³€]ƖƾŎĪŪĘ̖¨ʈĢƂlɘ۪üºňUðǜȢƢż̌ȦǼ‚ĤŊɲĖ­KqĘʼn¼ĔDzņɾªǀÞĈĂD†½ĄĎÌŗĞrôñnŽœN¼â¾ʄľԆ|DŽŽ֦ज़ȗlj̘̭ɺƅêgV̍ʆĠ·ÌĊv|ýĖÕWĊǎÞ´õ¼cÒÒBĢ͢UĜð͒s¨ňƃLĉÕÝ@ɛƯ÷¿Ľ­ĹeȏijëCȚDŲyê×Ŗyò¯ļcÂßY…tÁƤyAã˾J@ǝrý‹‰@¤…rz¸oP¹ɐÚyᐇHŸĀ[Jw…cVeȴϜ»ÈŽĖ}ƒŰŐèȭǢόĀƪÈŶë;Ñ̆ȤМľĮEŔ—ĹŊũ~ËUă{ŸĻƹɁύȩþĽvĽƓÉ@ē„ĽɲßǐƫʾǗĒpäWÐxnsÀ^ƆwW©¦cÅ¡Ji§vúF¶Ž¨c~c¼īŒeXǚ‹\\đ¾JŽwÀďksãA‹fÕ¦L}wa‚o”Z’‹D½†Ml«]eÒÅaɲáo½FõÛ]ĻÒ¡wYR£¢rvÓ®y®LF‹LzĈ„ôe]gx}•|KK}xklL]c¦£fRtív¦†PŨ£","@@Mš T‡¥"]],"encodeOffsets":[[[108619,36299],[108594,36341],[108600,36306]]]},"properties":{"cp":[103.823557,36.058039],"name":"甘肃","childNum":3}},{"id":"630000","geometry":{"type":"MultiPolygon","coordinates":[["@@InJo","@@CƒÆ½OŃĦsΰ~dz¦@@“Ņiš±è}ؘƄ˹A³r_ĞŠǒNΌĐw¤^ŬĵªpĺSZg’rpiƼĘԛ¨C|͖J’©Ħ»®VIJ~f\\m `Un„˜~ʌŸ•ĬàöNt•~ňjy–¢Zi˜Ɣ¥ĄŠk´nl`JʇŠJþ©pdƖ®È£¶ìRʦ‘źõƮËnŸʼėæÑƀĎ[‚˜¢VÎĂMÖÝÎF²sƊƀÎBļýƞ—¯ʘƭðħ¼Jh¿ŦęΌƇš¥²Q]Č¥nuÂÏriˆ¸¬ƪÛ^Ó¦d€¥[Wà…x\\ZŽjҕ¨GtpþYŊĕ´€zUO뇉P‰îMĄÁxH´á˜iÜUà›îÜՁĂÛSuŎ‹r“œJð̬EŒ‘FÁú×uÃÎkr“Ē{V}İ«O_ÌËĬ©ŽÓŧSRѱ§Ģ£^ÂyèçěM³Ƃę{[¸¿u…ºµ[gt£¸OƤĿéYŸõ·kŸq]juw¥Dĩƍ€õÇPéĽG‘ž©ã‡¤G…uȧþRcÕĕNy“yût“ˆ­‡ø‘†ï»a½ē¿BMoᣟÍj}éZËqbʍš“Ƭh¹ìÿÓAçãnIáI`ƒks£CG­ě˜Uy×Cy•…’Ÿ@¶ʡÊBnāzG„ơMē¼±O÷õJËĚăVŸĪũƆ£Œ¯{ËL½Ìzż“„VR|ĠTbuvJvµhĻĖH”Aëáa…­OÇðñęNw‡…œľ·L›mI±íĠĩPÉ×®ÿs—’cB³±JKßĊ«`…ađ»·QAmO’‘Vţéÿ¤¹SQt]]Çx€±¯A@ĉij¢Ó祖•ƒl¶ÅÛr—ŕspãRk~¦ª]Į­´“FR„åd­ČsCqđéFn¿Åƃm’Éx{W©ºƝºįkÕƂƑ¸wWūЩÈFž£\\tÈ¥ÄRÈýÌJ ƒlGr^×äùyÞ³fj”c†€¨£ÂZ|ǓMĝšÏ@ëÜőR‹›ĝ‰Œ÷¡{aïȷPu°ËXÙ{©TmĠ}Y³’­ÞIňµç½©C¡į÷¯B»|St»›]vƒųƒs»”}MÓ ÿʪƟǭA¡fs˜»PY¼c¡»¦c„ċ­¥£~msĉP•–Siƒ^o©A‰Šec‚™PeǵŽkg‚yUi¿h}aH™šĉ^|ᴟ¡HØûÅ«ĉ®]m€¡qċ¶±ÈyôōLÁst“BŸ®wn±ă¥HSò뚣˜S’ë@לÊăxÇN©™©T±ª£IJ¡fb®ÞbŽb_Ą¥xu¥B—ž{łĝ³«`d˜Ɛt—¤ťiñžÍUuºí`£˜^tƃIJc—·ÛLO‹½Šsç¥Ts{ă\\_»™kϊ±q©čiìĉ|ÍIƒ¥ć¥›€]ª§D{ŝŖÉR_sÿc³Īō›ƿΑ›§p›[ĉ†›c¯bKm›R¥{³„Z†e^ŽŒwx¹dƽŽôIg §Mĕ ƹĴ¿—ǣÜ̓]‹Ý–]snåA{‹eŒƭ`ǻŊĿ\\ijŬű”YÂÿ¬jĖqŽßbЏ•L«¸©@ěĀ©ê¶ìÀEH|´bRľž–Ó¶rÀQþ‹vl®Õ‚E˜TzÜdb ˜hw¤{LR„ƒd“c‹b¯‹ÙVgœ‚ƜßzÃô쮍^jUèXΖ|UäÌ»rKŽ\\ŒªN‘¼pZCü†VY††¤ɃRi^rPҒTÖ}|br°qňb̰ªiƶGQ¾²„x¦PœmlŜ‘[Ĥ¡ΞsĦŸÔÏâ\\ªÚŒU\\f…¢N²§x|¤§„xĔsZPòʛ²SÐqF`ª„VƒÞŜĶƨVZŒÌL`ˆ¢dŐIqr\\oäõ–F礻Ŷ×h¹]Clـ\\¦ďÌį¬řtTӺƙgQÇÓHţĒ”´ÃbEÄlbʔC”|CˆŮˆk„Ʈ[ʼ¬ňœ´KŮÈΰÌζƶlð”ļA†TUvdTŠG†º̼ŠÔ€ŒsÊDԄveMg"]],"encodeOffsets":[[[105308,37219],[95370,40081]]]},"properties":{"cp":[101.778916,36.623178],"name":"青海","childNum":2}},{"id":"640000","geometry":{"type":"Polygon","coordinates":["@@KëÀęĞ«OęȿȕŸı]ʼn¡åįÕÔ«Ǵõƪ™ĚQÐZhv K°›öqÀѐS[ÃÖHƖčË‡nL]ûc…Ùß@‚“ĝ‘¾}w»»‹oģF¹œ»kÌÏ·{zPƒ§B­¢íyÅt@ƒ@áš]Yv_ssģ¼i߁”ĻL¾ġsKD£¡N_…“˜X¸}B~Haiˆ™Åf{«x»ge_bs“KF¯¡Ix™mELcÿZ¤­Ģ‘ƒÝœsuBLù•t†ŒYdˆmVtNmtOPhRw~bd…¾qÐ\\âÙH\\bImlNZŸ»loƒŸqlVm–Gā§~QCw¤™{A\\‘PKŸNY‡¯bF‡kC¥’sk‹Šs_Ã\\ă«¢ħkJi¯r›rAhĹûç£CU‡ĕĊ_ԗBixÅُĄnªÑaM~ħpOu¥sîeQ¥¤^dkKwlL~{L~–hw^‚ófćƒKyEŒ­K­zuÔ¡qQ¤xZÑ¢^ļöܾEpž±âbÊÑÆ^fk¬…NC¾‘Œ“YpxbK~¥Že֎ŒäBlt¿Đx½I[ĒǙŒWž‹f»Ĭ}d§dµùEuj¨‚IÆ¢¥dXªƅx¿]mtÏwßR͌X¢͎vÆzƂZò®ǢÌʆCrâºMÞzžÆMҔÊÓŊZľ–r°Î®Ȉmª²ĈUªĚøºˆĮ¦ÌĘk„^FłĬhĚiĀ˾iİbjË"],"encodeOffsets":[[109366,40242]]},"properties":{"cp":[106.278179,38.46637],"name":"宁夏","childNum":1}},{"id":"650000","geometry":{"type":"Polygon","coordinates":["@@QØĔ²X¨”~ǘBºjʐߨvK”ƔX¨vĊOžÃƒ·¢i@~c—‡ĝe_«”Eš“}QxgɪëÏÃ@sÅyXoŖ{ô«ŸuX…ê•Îf`œC‚¹ÂÿÐGĮÕĞXŪōŸMźÈƺQèĽôe|¿ƸJR¤ĘEjcUóº¯Ĩ_ŘÁMª÷Ð¥Oéȇ¿ÖğǤǷÂF҇zÉx[]­Ĥĝ‰œ¦EP}ûƥé¿İƷTėƫœŕƅ™ƱB»Đ±’ēO…¦E–•}‘`cȺrĦáŖuҞª«IJ‡πdƺÏØZƴwʄ¤ĖGЙǂZ̓èH¶}ÚZצʥĪï|ÇĦMŔ»İĝLj‹ì¥Βœba­¯¥ǕǚkĆŵĦɑĺƯxūД̵nơʃĽá½M»›òmqóŘĝč˾ăC…ćāƿÝɽ©DZŅ»ēėŊLrÁ®ɱĕģʼnǻ̋ȥơŻǛȡVï¹Ň۩ûkɗġƁ§ʇė̕ĩũƽō^ƕŠUv£ƁQï“Ƶkŏ½ΉÃŭdzLқʻ«ƭ\\lƒ‡ŭD‡“{ʓDkaFÃÄa“³ŤđÔGRÈƚhSӹŚsİ«ĐË[¥ÚDkº^Øg¼ŵ¸£EÍö•€ůʼnT¡c_‡ËKY‹ƧUśĵ„݃U_©rETÏʜ±OñtYw獃{£¨uM³x½şL©Ùá[ÓÐĥ Νtģ¢\\‚ś’nkO›w¥±ƒT»ƷFɯàĩÞáB¹Æ…ÑUw„੍žĽw]•kE½Èå~‡Æ÷QyŠěCFmĭZī—ŵVÁ™ƿQƛ—ûXS²‰b½KϽĉS›©ŷXĕŸ{ŽĕK·¥Ɨcqq©f¿]‡ßDõU³h—­gËÇïģÉɋw“k¯í}I·šœbmœÉ–ř›īJɥĻˁ×xo›ɹī‡l•c…¤³Xù]‘™DžA¿w͉ì¥wÇN·ÂËnƾƍdǧđ®Ɲv•Um©³G\\“}µĿ‡QyŹl㓛µEw‰LJQ½yƋBe¶ŋÀů‡ož¥A—˜Éw@•{Gpm¿Aij†ŽKLhˆ³`ñcËtW‚±»ÕS‰ëüÿďD‡u\\wwwù³—V›LŕƒOMËGh£õP¡™er™Ïd{“‡ġWÁ…č|yšg^ğyÁzÙs`—s|ÉåªÇ}m¢Ń¨`x¥’ù^•}ƒÌ¥H«‰Yªƅ”Aйn~Ꝛf¤áÀz„gŠÇDIԝ´AňĀ҄¶ûEYospõD[{ù°]u›Jq•U•|Soċxţ[õÔĥkŋÞŭZ˺óYËüċrw €ÞkrťË¿XGÉbřaDü·Ē÷Aê[Ää€I®BÕИÞ_¢āĠpŠÛÄȉĖġDKwbm‡ÄNô‡ŠfœƫVÉvi†dz—H‘‹QµâFšù­Âœ³¦{YGžƒd¢ĚÜO „€{Ö¦ÞÍÀPŒ^b–ƾŠl[‚vt×ĈÍE˨¡Đ~´î¸ùÎh€uè`¸ŸHÕŔVºwĠââWò‡@{œÙNÝ´ə²ȕn{¿¥{l—÷eé^e’ďˆXj©î\\ªÑò˜Üìc\\üqˆÕ[Č¡xoÂċªbØ­Œø|€¶ȴZdÆÂšońéŒGš\\”¼C°ÌƁn´nxšÊOĨ’ہƴĸ¢¸òTxÊǪMīИÖŲÃɎOvˆʦƢ~FއRěò—¿ġ~åŊœú‰Nšžš¸qŽ’Ę[Ĕ¶ÂćnÒPĒÜvúĀÊbÖ{Äî¸~Ŕünp¤ÂH¾œĄYÒ©ÊfºmԈĘcDoĬMŬ’˜S¤„s²‚”ʘچžȂVŦ –ŽèW°ªB|IJXŔþÈJĦÆæFĚêŠYĂªĂ]øªŖNÞüA€’fɨJ€˜¯ÎrDDšĤ€`€mz\\„§~D¬{vJÂ˜«lµĂb–¤p€ŌŰNĄ¨ĊXW|ų ¿¾ɄĦƐMT”‡òP˜÷fØĶK¢ȝ˔Sô¹òEð­”`Ɩ½ǒÂň×äı–§ĤƝ§C~¡‚hlå‚ǺŦŞkâ’~}ŽFøàIJaĞ‚fƠ¥Ž„Ŕdž˜®U¸ˆźXœv¢aƆúŪtŠųƠjd•ƺŠƺÅìnrh\\ĺ¯äɝĦ]èpĄ¦´LƞĬŠ´ƤǬ˼Ēɸ¤rºǼ²¨zÌPðŀbþ¹ļD¢¹œ\\ĜÑŚŸ¶ZƄ³âjĦoâŠȴLʉȮŒĐ­ĚăŽÀêZǚŐ¤qȂ\\L¢ŌİfÆs|zºeªÙæ§΢{Ā´ƐÚ¬¨Ĵà²łhʺKÞºÖTŠiƢ¾ªì°`öøu®Ê¾ãÖ"],"encodeOffsets":[[88824,50096]]},"properties":{"cp":[87.617733,43.792818],"name":"新疆","childNum":1}},{"id":"110000","geometry":{"type":"Polygon","coordinates":["@@R„ºaY՜™QaúÍÔiþĩȨWĢ‹ü|Ėu[qb[swP@ÅğP¿{\\‡¯Y²·‘Ѩj¯ŠX\\¯œMSvU¯YIŕY{[fk­VÁ›ûtŷmiÍt_H»Ĩ±d`й­{bw…Yr“³S]§§o¹€qGtm_Sŧ€“oa›‹FLg‘QN_•dV€@Zom_ć\\ߚW´—€ÕiœRcfi…Ÿ’o§ËgToÛJíĔóu…|wP¤™XnO¢ÉŠŦ¯pNÄā¤zâŖÈRpŢZŠœÚ{GŠrFt¦Òx§ø¹RóäV¤XdˆżâºWbwڍUd®bêņ¾‘jnŎGŃŶŠnzÚScîĚZŠen¬"],"encodeOffsets":[[119421,42013]]},"properties":{"cp":[116.405285,39.904989],"name":"北京","childNum":1}},{"id":"120000","geometry":{"type":"Polygon","coordinates":["@@ŬgX§Ü«E…¶Ḟ“¬O_™ïlÁg“z±AXe™µÄĵ{¶]gitgšIj·›¥ì_iU€‰¨ÐƎk}ĕ{gB—qGf{¿a†U^fI“ư‹³õ{YƒıëNĿžk©ïËZukāA‘īlĕĥs¡bġ«@dekąI[nlPqCnp{ˆō³°`{PNdƗqSÄĻNNâyj]äžÒD ĬH°Æ]~¡HO¾ŒX}ÐxŒgp“gWˆrDGˆŒpù‚Š^L‚ˆrzWxˆZ^¨´T\\|~@I‰zƒ–bĤ‹œjeĊªz£®Ĕvě€L†mV¾Ô_ȔNW~zbĬvG†²ZmDM~”~"],"encodeOffsets":[[120237,41215]]},"properties":{"cp":[117.190182,39.125596],"name":"天津","childNum":1}},{"id":"310000","geometry":{"type":"MultiPolygon","coordinates":[["@@ɧư¬EpƸÁx]‡","@@©„²ƒ","@@”MA‹“˜","@@QpªK†WT…‰‰§¨","@@bŝՕÕEȣÚƥêImɇǦèÜĠŒÚÄÓŴ·ʌÇ","@@S‚ô¤r]ì†ƬįǜûȬɋŠŭ™×^‰sYŒɍDŋ‘ŽąñCG²«ªč@h–_p¯A{‡oloY€¬j@IJ`•gQڙpptǀ^MIJvtbe´Rh@–oj¨ž","@@ÆLH{a}Eo¦"]],"encodeOffsets":[[[124702,32062],[124547,32200],[124808,31991],[124726,32110],[124903,32376],[124065,32166],[124870,31965]]]},"properties":{"cp":[121.472644,31.231706],"name":"上海","childNum":7}},{"id":"500000","geometry":{"type":"Polygon","coordinates":["@@TÂÛ`Ùƅően½S‹êqDu[R‹å͹ˆ÷eXÍy‘¸_ĺę}÷`M¯ċfCVµqʼn÷Z•gg‘Œ^d½pDO‡ÎCnœ^uf²ènh¼WtƏxRGg¦…pV„†FI±ŽG^ŒIc´ec‡’G•ĹÞ½sëÆNä̤“Kӈe¯|‚R¸§L‘ÜkPoïƭNï¶}Gy“wdiù©nkĈzjŸ•@™Óc£»Wă¹Óf§c[µŠo·Ó|MvÛaqœ½«‡èœ’\\ÂoVnŽÓØÍ™²«‹bq¿eƒhCž„€‹Ĝ^Qž~ Évý‡ş¤²Į‰pEĶyhsŊwH‹½‡š¿gņ›¡ýE¡ya£³t\\¨\\vú¹¼©·Ñr_oÒý¥‚‘et³]—Et©uÖ¥±ă©KVeëƒ]}wVPÀFA¨ąB}qTjgRemfFm‰QF݅My˜ù•nцAmыCaƒwŒu_p—¯sfۍ_g†“I_pNysBЦzG¸rHe‚„N\\CvEsÐñÚkcD‘ÖĉsaQ¯€}_U‡†zÁēˆ}Ÿ^R •Äd^ÍĸZ¾·¶ƒ`wećJEž¹vÛ·Hgƒ‚éFXjÉê`|yŒpxkAwœWĐpb¥eOsmzwqChóUQl¥F^laf‹anòsr›EvfQdÁUVf—ÎvÜ^efˆtET¬ôA\\œ¢sJŽnQTjP؈xøK|nBz‰„œĞ»LY‚…FDxӄvr“[ehľš•vN”¢o¾NiÂxGp⬐z›bfZo~hGi’]öF|‰|Nb‡tOMn eA±ŠtPT‡LjpYQ|†SH††YĀxinzDJ€Ìg¢và¥Pg‰_–ÇzII‹€II•„£®S¬„Øsμ–¥¨^LšnGIJļIJƤjÎƀƾ¹¸ØÎezĆT¸}êЖqHŸðqĖ䒊¥^CƒIj–²p…\\_ æüY|[YxƊæuž°xb®…Űb@~¢NQt°¶‚S栓Ê~rljĔëĚ¢~šuf`‘‚†fa‚ĔJåĊ†nÔ]„jƎćÊ@Š£¾a®£Ű{ŶĕF‹ègLk{Y|¡ĜWƔtƬJÑxq‹±ĢN´‰òK‰™–LÈüD|s`ŋ’ć]ƒÃ‰`đŒMùƱ¿~Y°ħ`ƏíW‰½eI‹½{aŸ‘OIrÏ¡ĕŇa†p†µÜƃġ‰²"],"encodeOffsets":[[111728,31311]]},"properties":{"cp":[106.504962,29.533155],"name":"重庆","childNum":1}},{"id":"810000","geometry":{"type":"MultiPolygon","coordinates":[["@@AlFi","@@mŽp","@@EpHo","@@rMUw‡AS¬€]","@@ea¢pl¸Eõ¹‡hj[ƒ]ÔCΖ@lj˜¡uBXŸ…•´‹AI¹…[‹yDUˆ]W`çwZkmc–…M›žp€Åv›}I‹oJlcaƒfёKްä¬XJmРđhI®æÔtSHn€Eˆ„ÒrÄc"]],"encodeOffsets":[[[117111,23002],[117072,22876],[117045,22887],[116882,22747],[116975,23082]]]},"properties":{"cp":[114.173355,22.320048],"name":"香港","childNum":5}},{"id":"820000","geometry":{"type":"Polygon","coordinates":["@@œá—w{ÎrŽ"],"encodeOffsets":[[116285,22746]]},"properties":{"cp":[113.54909,22.198951],"name":"澳门","childNum":1}}],"UTF8Encoding":true}

export default chinaMap;

================================================
FILE: src/utils/index.ts
================================================
export const setProperty = (prop: string, val: any, dom = document.documentElement) => {
    dom.style.setProperty(prop, val);
};

export const mix = (color1: string, color2: string, weight: number = 0.5): string => {
    let color = '#';
    for (let i = 0; i <= 2; i++) {
        const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16);
        const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16);
        const c = Math.round(c1 * weight + c2 * (1 - weight));
        color += c.toString(16).padStart(2, '0');
    }
    return color;
};


================================================
FILE: src/utils/request.ts
================================================
import axios, { AxiosInstance, AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';

const service: AxiosInstance = axios.create({
    timeout: 5000
});

service.interceptors.request.use(
    (config: InternalAxiosRequestConfig) => {
        return config;
    },
    (error: AxiosError) => {
        console.log(error);
        return Promise.reject();
    }
);

service.interceptors.response.use(
    (response: AxiosResponse) => {
        if (response.status === 200) {
            return response;
        } else {
            Promise.reject();
        }
    },
    (error: AxiosError) => {
        console.log(error);
        return Promise.reject();
    }
);

export default service;


================================================
FILE: src/views/chart/echarts.vue
================================================
<template>
    <div class="container">
        <div class="plugins-tips">
            vue-echarts:Apache ECharts™ 的 Vue.js 组件。 访问地址:
            <a href="https://github.com/ecomfe/vue-echarts" target="_blank">vue-echarts</a>
        </div>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">柱状图</div>
            </template>
            <v-chart class="schart" :option="barOptions" />
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">折线图</div>
            </template>
            <v-chart class="schart" :option="lineOptions" />
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">饼状图</div>
            </template>
            <v-chart class="schart" :option="pieOptions" />
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">环形图</div>
            </template>
            <v-chart class="schart" :option="ringOptions" />
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">词云图</div>
            </template>
            <v-chart class="schart" :option="wordOptions" />
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">地图</div>
            </template>
            <v-chart class="schart" :option="mapOptions" />
        </el-card>
    </div>
</template>

<script setup lang="ts" name="echarts">
import { registerMap, use } from 'echarts/core';
import { BarChart, LineChart, PieChart, MapChart } from 'echarts/charts';
import {
    GridComponent,
    TooltipComponent,
    LegendComponent,
    TitleComponent,
    VisualMapComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import VChart from 'vue-echarts';
import 'echarts-wordcloud';
import { barOptions, lineOptions, pieOptions, ringOptions, wordOptions, mapOptions } from './options';
import chinaMap from '@/utils/china';
use([
    CanvasRenderer,
    BarChart,
    GridComponent,
    LineChart,
    PieChart,
    MapChart,
    TooltipComponent,
    LegendComponent,
    TitleComponent,
    VisualMapComponent,
]);
registerMap('china', chinaMap);
</script>

<style scoped>
.schart {
    width: 100%;
    height: 400px;
}

.content-title {
    font-weight: 400;
    font-size: 22px;
    color: #1f2f3d;
}
</style>


================================================
FILE: src/views/chart/options.ts
================================================
import { graphic } from 'echarts/core';
export const barOptions = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    },
    yAxis: {
        type: 'value',
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'shadow',
        },
    },
    color: ['#009688', '#f44336'],
    series: [
        {
            data: [120, 200, 150, 80, 70, 110, 130],
            type: 'bar',
        },
        {
            data: [180, 230, 190, 120, 110, 230, 235],
            type: 'bar',
        },
    ],
};

export const lineOptions = {
    tooltip: {
        trigger: 'axis',
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true,
    },
    xAxis: {
        type: 'category',
        boundaryGap: false,
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    },
    yAxis: {
        type: 'value',
    },
    color: ['#009688', '#f44336'],
    series: [
        {
            name: 'Email',
            type: 'line',
            stack: 'Total',
            areaStyle: {},
            smooth: true,
            data: [120, 132, 101, 134, 90, 230, 210],
        },
        {
            name: 'Union Ads',
            type: 'line',
            stack: 'Total',
            areaStyle: {},
            smooth: true,
            data: [220, 182, 191, 234, 290, 330, 310],
        },
    ],
};

export const pieOptions = {
    title: {
        text: 'Referer of a Website',
        subtext: 'Fake Data',
        left: 'center',
    },
    tooltip: {
        trigger: 'item',
    },
    legend: {
        orient: 'vertical',
        left: 'left',
    },
    series: [
        {
            name: 'Access From',
            type: 'pie',
            radius: '50%',
            data: [
                { value: 1048, name: 'Search Engine' },
                { value: 735, name: 'Direct' },
                { value: 580, name: 'Email' },
                { value: 484, name: 'Union Ads' },
                { value: 300, name: 'Video Ads' },
            ],
            emphasis: {
                itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(0, 0, 0, 0.5)',
                },
            },
        },
    ],
};

export const wordOptions = {
    series: [
        {
            type: 'wordCloud',
            rotationRange: [0, 0],
            autoSize: {
                enable: true,
                minSize: 14,
            },
            textStyle: {
                fontFamily: '微软雅黑,sans-serif',
                color: function () {
                    return (
                        'rgb(' +
                        [
                            Math.round(Math.random() * 160),
                            Math.round(Math.random() * 160),
                            Math.round(Math.random() * 160),
                        ].join(',') +
                        ')'
                    );
                },
            },
            data: [
                {
                    name: 'Vue',
                    value: 10000,
                },
                {
                    name: 'React',
                    value: 9000,
                },
                {
                    name: '图表',
                    value: 4000,
                },
                {
                    name: '产品',
                    value: 7000,
                },
                {
                    name: 'vue-manage-system',
                    value: 2000,
                },
                {
                    name: 'element-plus',
                    value: 6000,
                },
                {
                    name: '管理系统',
                    value: 5000,
                },
                {
                    name: '前端',
                    value: 4000,
                },
                {
                    name: '测试',
                    value: 3000,
                },
                {
                    name: '后端',
                    value: 8000,
                },
                {
                    name: '软件开发',
                    value: 6000,
                },
                {
                    name: '程序员',
                    value: 4000,
                },
            ],
        },
    ],
};

export const ringOptions = {
    tooltip: {
        trigger: 'item',
    },
    legend: {
        top: '5%',
        left: 'center',
    },

    series: [
        {
            name: 'Access From',
            type: 'pie',
            radius: ['40%', '70%'],
            avoidLabelOverlap: false,
            itemStyle: {
                borderRadius: 10,
                borderColor: '#fff',
                borderWidth: 2,
            },
            label: {
                show: false,
                position: 'center',
            },
            emphasis: {
                label: {
                    show: true,
                    fontSize: 40,
                    fontWeight: 'bold',
                },
            },
            labelLine: {
                show: false,
            },
            data: [
                { value: 1048, name: 'Search Engine' },
                { value: 735, name: 'Direct' },
                { value: 580, name: 'Email' },
                { value: 484, name: 'Union Ads' },
                { value: 300, name: 'Video Ads' },
            ],
        },
    ],
};

export const dashOpt1 = {
    xAxis: {
        type: 'category',
        boundaryGap: false,
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    },
    yAxis: {
        type: 'value',
    },
    grid: {
        top: '2%',
        left: '2%',
        right: '3%',
        bottom: '2%',
        containLabel: true,
    },
    color: ['#009688', '#f44336'],
    series: [
        {
            type: 'line',
            areaStyle: {
                color: new graphic.LinearGradient(0, 0, 0, 1, [
                    {
                        offset: 0,
                        color: 'rgba(0, 150, 136,0.8)',
                    },
                    {
                        offset: 1,
                        color: 'rgba(0, 150, 136,0.2)',
                    },
                ]),
            },
            smooth: true,
            data: [120, 132, 301, 134, 90, 230, 210],
        },
        {
            type: 'line',
            smooth: true,
            data: [220, 122, 191, 234, 190, 130, 310],
        },
    ],
};

export const dashOpt2 = {
    legend: {
        bottom: '1%',
        left: 'center',
    },
    color: ['#3f51b5', '#009688', '#f44336', '#00bcd4', '#1ABC9C'],
    series: [
        {
            type: 'pie',
            radius: ['40%', '70%'],
            avoidLabelOverlap: false,
            itemStyle: {
                borderRadius: 10,
                borderColor: '#fff',
                borderWidth: 2,
            },
            data: [
                { value: 1048, name: '数码' },
                { value: 735, name: '食品' },
                { value: 580, name: '母婴' },
                { value: 484, name: '家电' },
                { value: 300, name: '运动' },
            ],
        },
    ],
};

export const mapOptions = {
    tooltip: {
        trigger: 'item',
    },
    geo: {
        map: 'china',
        roam: false,
        emphasis: {
            label: {
                show: false,
            },
        },
    },
    visualMap: {
        show: false,
        min: 0,
        max: 100,
        realtime: false,
        calculable: false,
        inRange: {
            color: ['#d2e0f5', '#71A9FF'],
        },
    },
    series: [
        {
            geoIndex: 0,
            name: '地域分布',
            type: 'map',
            coordinateSystem: 'geo',
            map: 'china',
            data: [
                { name: '北京', value: 100 },
                { name: '上海', value: 100 },
                { name: '广东', value: 100 },
                { name: '浙江', value: 90 },
                { name: '江西', value: 80 },
                { name: '山东', value: 70 },
                { name: '广西', value: 60 },
                { name: '河南', value: 50 },
                { name: '河南', value: 40 },
                { name: '青海', value: 70 },
                { name: '河南', value: 30 },
                { name: '黑龙江', value: 20 },
                { name: '新疆', value: 20 },
                { name: '云南', value: 20 },
                { name: '甘肃', value: 20 },
            ],
        },
    ],
};


================================================
FILE: src/views/chart/schart.vue
================================================
<template>
	<div class="container">
		<div class="plugins-tips">
			vue-schart:vue.js封装sChart.js的图表组件。 访问地址:
			<a href="https://github.com/lin-xin/vue-schart" target="_blank">vue-schart</a>
		</div>
		<el-card class="mgb20" shadow="hover">
			<template #header>
				<div class="content-title">柱状图</div>
			</template>
			<schart class="schart" canvasId="bar" :options="options1"></schart>
		</el-card>
		<el-card class="mgb20" shadow="hover">
			<template #header>
				<div class="content-title">折线图</div>
			</template>
			<schart class="schart" canvasId="line" :options="options2"></schart>
		</el-card>
		<el-card class="mgb20" shadow="hover">
			<template #header>
				<div class="content-title">饼状图</div>
			</template>
			<schart class="schart" canvasId="pie" :options="options3"></schart>
		</el-card>
		<el-card class="mgb20" shadow="hover">
			<template #header>
				<div class="content-title">环形图</div>
			</template>
			<schart class="schart" canvasId="ring" :options="options4"></schart>
		</el-card>
	</div>
</template>

<script setup lang="ts" name="schart">
import Schart from 'vue-schart';

const options1 = {
	type: 'bar',
	title: {
		text: '最近一周各品类销售图'
	},
	colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
	labels: ['周一', '周二', '周三', '周四', '周五'],
	datasets: [
		{
			label: '家电',
			// fillColor: 'rgba(241, 49, 74, 0.5)',
			data: [234, 278, 270, 190, 230]
		},
		{
			label: '百货',
			data: [164, 178, 190, 135, 160]
		},
		{
			label: '食品',
			data: [144, 198, 150, 235, 120]
		}
	]
};
const options2 = {
	type: 'line',
	title: {
		text: '最近几个月各品类销售趋势图'
	},
	colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
	labels: ['6月', '7月', '8月', '9月', '10月'],
	datasets: [
		{
			label: '家电',
			data: [234, 278, 270, 190, 230]
		},
		{
			label: '百货',
			data: [164, 178, 150, 135, 160]
		},
		{
			label: '食品',
			data: [114, 138, 200, 235, 190]
		}
	]
};
const options3 = {
	type: 'pie',
	title: {
		text: '服装品类销售饼状图'
	},
	legend: {
		position: 'left'
	},
	colorList: ["#2196f3", '#673ab7', "#009688", "#1ABC9C", "#3f51b5", "#f44336", "#00bcd4"],
	labels: ['T恤', '牛仔裤', '连衣裙', '毛衣', '七分裤', '短裙', '羽绒服'],
	datasets: [
		{
			data: [334, 278, 190, 235, 260, 200, 141]
		}
	]
};
const options4 = {
	type: 'ring',
	title: {
		text: '环形三等分'
	},
	showValue: false,
	legend: {
		position: 'bottom',
		bottom: 40
	},
	colorList: ["#3f51b5", "#009688", "#f44336", "#00bcd4", "#1ABC9C"],
	labels: ['vue', 'react', 'angular'],
	datasets: [
		{
			data: [500, 500, 500]
		}
	]
};
</script>

<style scoped>
.schart {
	width: 100%;
	height: 400px;
}

.content-title {
	font-weight: 400;
	font-size: 22px;
	color: #1f2f3d;
}
</style>


================================================
FILE: src/views/dashboard.vue
================================================
<template>
    <div>
        <el-row :gutter="20" class="mgb20">
            <el-col :span="6">
                <el-card shadow="hover" body-class="card-body">
                    <el-icon class="card-icon bg1">
                        <User />
                    </el-icon>
                    <div class="card-content">
                        <countup class="card-num color1" :end="6666" />
                        <div>用户访问量</div>
                    </div>
                </el-card>
            </el-col>
            <el-col :span="6">
                <el-card shadow="hover" body-class="card-body">
                    <el-icon class="card-icon bg2">
                        <ChatDotRound />
                    </el-icon>
                    <div class="card-content">
                        <countup class="card-num color2" :end="168" />
                        <div>系统消息</div>
                    </div>
                </el-card>
            </el-col>
            <el-col :span="6">
                <el-card shadow="hover" body-class="card-body">
                    <el-icon class="card-icon bg3">
                        <Goods />
                    </el-icon>
                    <div class="card-content">
                        <countup class="card-num color3" :end="8888" />
                        <div>商品数量</div>
                    </div>
                </el-card>
            </el-col>
            <el-col :span="6">
                <el-card shadow="hover" body-class="card-body">
                    <el-icon class="card-icon bg4">
                        <ShoppingCartFull />
                    </el-icon>
                    <div class="card-content">
                        <countup class="card-num color4" :end="568" />
                        <div>今日订单量</div>
                    </div>
                </el-card>
            </el-col>
        </el-row>

        <el-row :gutter="20" class="mgb20">
            <el-col :span="18">
                <el-card shadow="hover">
                    <div class="card-header">
                        <p class="card-header-title">订单动态</p>
                        <p class="card-header-desc">最近一周订单状态,包括订单成交量和订单退货量</p>
                    </div>
                    <v-chart class="chart" :option="dashOpt1" />
                </el-card>
            </el-col>
            <el-col :span="6">
                <el-card shadow="hover">
                    <div class="card-header">
                        <p class="card-header-title">品类分布</p>
                        <p class="card-header-desc">最近一个月销售商品的品类情况</p>
                    </div>
                    <v-chart class="chart" :option="dashOpt2" />
                </el-card>
            </el-col>
        </el-row>
        <el-row :gutter="20">
            <el-col :span="7">
                <el-card shadow="hover" :body-style="{ height: '400px' }">
                    <div class="card-header">
                        <p class="card-header-title">时间线</p>
                        <p class="card-header-desc">最新的销售动态和活动信息</p>
                    </div>
                    <el-timeline>
                        <el-timeline-item v-for="(activity, index) in activities" :key="index" :color="activity.color">
                            <div class="timeline-item">
                                <div>
                                    <p>{{ activity.content }}</p>
                                    <p class="timeline-desc">{{ activity.description }}</p>
                                </div>
                                <div class="timeline-time">{{ activity.timestamp }}</div>
                            </div>
                        </el-timeline-item>
                    </el-timeline>
                </el-card>
            </el-col>
            <el-col :span="10">
                <el-card shadow="hover" :body-style="{ height: '400px' }">
                    <div class="card-header">
                        <p class="card-header-title">渠道统计</p>
                        <p class="card-header-desc">最近一个月的订单来源统计</p>
                    </div>
                    <v-chart class="map-chart" :option="mapOptions" />
                </el-card>
            </el-col>
            <el-col :span="7">
                <el-card shadow="hover" :body-style="{ height: '400px' }">
                    <div class="card-header">
                        <p class="card-header-title">排行榜</p>
                        <p class="card-header-desc">销售商品的热门榜单Top5</p>
                    </div>
                    <div>
                        <div class="rank-item" v-for="(rank, index) in ranks">
                            <div class="rank-item-avatar">{{ index + 1 }}</div>
                            <div class="rank-item-content">
                                <div class="rank-item-top">
                                    <div class="rank-item-title">{{ rank.title }}</div>
                                    <div class="rank-item-desc">销量:{{ rank.value }}</div>
                                </div>
                                <el-progress
                                    :show-text="false"
                                    striped
                                    :stroke-width="10"
                                    :percentage="rank.percent"
                                    :color="rank.color"
                                />
                            </div>
                        </div>
                    </div>
                </el-card>
            </el-col>
        </el-row>
    </div>
</template>

<script setup lang="ts" name="dashboard">
import countup from '@/components/countup.vue';
import { use, registerMap } from 'echarts/core';
import { BarChart, LineChart, PieChart, MapChart } from 'echarts/charts';
import {
    GridComponent,
    TooltipComponent,
    LegendComponent,
    TitleComponent,
    VisualMapComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import VChart from 'vue-echarts';
import { dashOpt1, dashOpt2, mapOptions } from './chart/options';
import chinaMap from '@/utils/china';
use([
    CanvasRenderer,
    BarChart,
    GridComponent,
    LineChart,
    PieChart,
    TooltipComponent,
    LegendComponent,
    TitleComponent,
    VisualMapComponent,
    MapChart,
]);
registerMap('china', chinaMap);
const activities = [
    {
        content: '收藏商品',
        description: 'xxx收藏了你的商品,就是不买',
        timestamp: '30分钟前',
        color: '#00bcd4',
    },
    {
        content: '用户评价',
        description: 'xxx给了某某商品一个差评,吐血啊',
        timestamp: '55分钟前',
        color: '#1ABC9C',
    },
    {
        content: '订单提交',
        description: 'xxx提交了订单,快去收钱吧',
        timestamp: '1小时前',
        color: '#3f51b5',
    },
    {
        content: '退款申请',
        description: 'xxx申请了仅退款,又要亏钱了',
        timestamp: '15小时前',
        color: '#f44336',
    },
    {
        content: '商品上架',
        description: '运营专员瞒着你上架了一辆飞机',
        timestamp: '1天前',
        color: '#009688',
    },
];

const ranks = [
    {
        title: '手机',
        value: 10000,
        percent: 80,
        color: '#f25e43',
    },
    {
        title: '电脑',
        value: 8000,
        percent: 70,
        color: '#00bcd4',
    },
    {
        title: '相机',
        value: 6000,
        percent: 60,
        color: '#64d572',
    },
    {
        title: '衣服',
        value: 5000,
        percent: 55,
        color: '#e9a745',
    },
    {
        title: '书籍',
        value: 4000,
        percent: 50,
        color: '#009688',
    },
];
</script>

<style>
.card-body {
    display: flex;
    align-items: center;
    height: 100px;
    padding: 0;
}
</style>
<style scoped>
.card-content {
    flex: 1;
    text-align: center;
    font-size: 14px;
    color: #999;
    padding: 0 20px;
}

.card-num {
    font-size: 30px;
}

.card-icon {
    font-size: 50px;
    width: 100px;
    height: 100px;
    text-align: center;
    line-height: 100px;
    color: #fff;
}

.bg1 {
    background: #2d8cf0;
}

.bg2 {
    background: #64d572;
}

.bg3 {
    background: #f25e43;
}

.bg4 {
    background: #e9a745;
}

.color1 {
    color: #2d8cf0;
}

.color2 {
    color: #64d572;
}

.color3 {
    color: #f25e43;
}

.color4 {
    color: #e9a745;
}

.chart {
    width: 100%;
    height: 400px;
}

.card-header {
    padding-left: 10px;
    margin-bottom: 20px;
}

.card-header-title {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 5px;
}

.card-header-desc {
    font-size: 14px;
    color: #999;
}

.timeline-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 16px;
    color: #000;
}

.timeline-time,
.timeline-desc {
    font-size: 12px;
    color: #787878;
}

.rank-item {
    display: flex;
    align-items: center;
    margin-bottom: 20px;
}

.rank-item-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #f2f2f2;
    text-align: center;
    line-height: 40px;
    margin-right: 10px;
}

.rank-item-content {
    flex: 1;
}

.rank-item-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: #343434;
    margin-bottom: 10px;
}

.rank-item-desc {
    font-size: 14px;
    color: #999;
}
.map-chart {
    width: 100%;
    height: 350px;
}
</style>


================================================
FILE: src/views/element/calendar.vue
================================================
<template>
    <div class="container">
        <el-calendar v-model="value">
            <template #date-cell="{ data }">
                <div>{{ data.date.getDate() }}</div>
                <div class="notes-container" v-if="notes[data.day.toString()]">
                    <div class="notes" v-for="note in notes[data.day.toString()]">
                        <span :class="note.status === 1 ? 'text-success' : 'text-danger'"></span>
                        <div class="note-title">{{ note.title }}</div>
                    </div>
                </div>
            </template>
        </el-calendar>
    </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const today = new Date();
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
const value = ref(today);

const todayDate = today.toISOString().slice(0, 10);
const yesterdayDate = yesterday.toISOString().slice(0, 10);

const notes: any = {
    [todayDate]: [
        { title: '吃饭', status: 1 },
        { title: '睡觉', status: 0 },
        { title: '吃饭', status: 1 },
        { title: '睡觉', status: 0 },
        { title: '吃饭', status: 1 },
        { title: '睡觉', status: 0 },
    ],
    [yesterdayDate]: [{ title: '参加会议', status: 0 }],
};
</script>

<style scoped>
.notes-container {
    height: 60px;
    overflow-y: auto;
}

.notes-container::-webkit-scrollbar {
    width: 0;
}

.notes {
    display: flex;
    align-items: center;
    width: 100%;
    font-size: 12px;
}

.notes:hover {
    background-color: #eee;
}

.note-title {
    flex: 1;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
}

.notes span {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: 5px;
}

.text-success {
    background-color: #5cb85c;
}

.text-danger {
    background-color: #d9534f;
}
</style>


================================================
FILE: src/views/element/carousel.vue
================================================
<template>
    <div>
        <el-card class="mgb20">
            <template #header>基础用法</template>
            <el-carousel height="400px">
                <el-carousel-item v-for="item in 4" :key="item">
                    <h3>{{ item }}</h3>
                </el-carousel-item>
            </el-carousel>
        </el-card>

        <el-row :gutter="20">
            <el-col :span="12">
                <el-card class="mgb20">
                    <template #header>轮播图</template>
                    <el-carousel height="300px">
                        <el-carousel-item v-for="item in imgs" :key="item">
                            <el-image class="carousel-img" :src="item" fit="cover" />
                        </el-carousel-item>
                    </el-carousel>
                </el-card>
            </el-col>
            <el-col :span="12">
                <el-card class="mgb20">
                    <template #header>卡片模式</template>
                    <el-carousel height="300px" type="card">
                        <el-carousel-item v-for="item in imgs" :key="item">
                            <el-image class="carousel-img" :src="item" fit="cover" />
                        </el-carousel-item>
                    </el-carousel>
                </el-card>
            </el-col>
        </el-row>
    </div>
</template>

<script lang="ts" setup>
const imgs = [
    'https://cdn.pixabay.com/photo/2017/08/07/08/23/sea-2601374_640.jpg',
    'https://cdn.pixabay.com/photo/2020/02/11/10/24/lake-4839058_640.jpg',
    'https://cdn.pixabay.com/photo/2024/02/21/08/06/coast-8587004_640.jpg',
    'https://cdn.pixabay.com/photo/2023/07/29/10/21/grasshopper-8156626_640.jpg',
];
</script>

<style scoped>
.el-carousel__item h3 {
    color: #475669;
    line-height: 400px;
    margin: 0;
    text-align: center;
}

.el-carousel__item:nth-child(2n) {
    background-color: #99a9bf;
}

.el-carousel__item:nth-child(2n + 1) {
    background-color: #d3dce6;
}

.carousel-img {
    width: 100%;
    height: 100%;
}
</style>


================================================
FILE: src/views/element/form.vue
================================================
<template>
    <div class="container">
        <el-radio-group class="mgb20" v-model="labelPosition">
            <el-radio-button value="left">Left</el-radio-button>
            <el-radio-button value="right">Right</el-radio-button>
            <el-radio-button value="top">Top</el-radio-button>
        </el-radio-group>
        <el-form ref="formRef" :rules="rules" :model="form" label-width="120px" :label-position="labelPosition">
            <el-row :gutter="50">
                <el-col :span="10">
                    <el-form-item label="文本框" prop="name">
                        <el-input v-model="form.name"></el-input>
                    </el-form-item>
                    <el-form-item label="数字框" prop="num">
                        <el-input-number v-model="form.num" :min="1" :max="10" />
                    </el-form-item>
                    <el-form-item label="日期选择" prop="date">
                        <el-date-picker type="date" placeholder="选择日期" v-model="form.date"></el-date-picker>
                    </el-form-item>
                    <el-form-item label="时间选择" prop="time">
                        <el-time-picker placeholder="选择时间" v-model="form.time">
                        </el-time-picker>
                    </el-form-item>
                    <el-form-item label="选择器" prop="region">
                        <el-select v-model="form.region" placeholder="请选择">
                            <el-option key="小明" label="小明" value="小明"></el-option>
                            <el-option key="小红" label="小红" value="小红"></el-option>
                            <el-option key="小白" label="小白" value="小白"></el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="城市级联" prop="options">
                        <el-cascader :options="options" v-model="form.options"></el-cascader>
                    </el-form-item>
                    <el-form-item label="文本框" prop="desc">
                        <el-input type="textarea" rows="5" v-model="form.desc"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="评分" prop="rate">
                        <el-rate v-model="form.rate" allow-half />
                    </el-form-item>
                    <el-form-item label="滑块" prop="num">
                        <el-slider v-model="form.num" :step="1" show-stops :max="10" />
                    </el-form-item>
                    <el-form-item label="开关" prop="delivery">
                        <el-switch v-model="form.delivery"></el-switch>
                    </el-form-item>
                    <el-form-item label="颜色选择" prop="color">
                        <el-color-picker v-model="form.color" />
                    </el-form-item>
                    <el-form-item label="多选框" prop="type">
                        <el-checkbox-group v-model="form.type">
                            <el-checkbox label="小明" value="小明" name="type"></el-checkbox>
                            <el-checkbox label="小红" value="小红" name="type"></el-checkbox>
                            <el-checkbox label="小白" value="小白" name="type"></el-checkbox>
                        </el-checkbox-group>
                    </el-form-item>
                    <el-form-item label="单选框" prop="resource">
                        <el-radio-group v-model="form.resource">
                            <el-radio label="小明" value="小明"></el-radio>
                            <el-radio label="小红" value="小红"></el-radio>
                            <el-radio label="小白" value="小白"></el-radio>
                        </el-radio-group>
                    </el-form-item>
                    <el-form-item label="穿梭框" prop="transfer">
                        <el-transfer v-model="form.transfer" :data="transferData" />
                    </el-form-item>
                </el-col>

                <el-col :span="24">
                    <el-form-item>
                        <el-button type="primary" @click="onSubmit(formRef)">表单提交</el-button>
                        <el-button @click="onReset(formRef)">重置表单</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
    </div>
</template>

<script setup lang="ts" name="forms">
import { reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import type { FormInstance, FormProps, FormRules } from 'element-plus';
const labelPosition = ref<FormProps['labelPosition']>('right')
const options = [
    {
        value: 'guangdong',
        label: '广东省',
        children: [
            {
                value: 'guangzhou',
                label: '广州市',
                children: [
                    {
                        value: 'tianhe',
                        label: '天河区',
                    },
                    {
                        value: 'haizhu',
                        label: '海珠区',
                    },
                ],
            },
            {
                value: 'dongguan',
                label: '东莞市',
                children: [
                    {
                        value: 'changan',
                        label: '长安镇',
                    },
                    {
                        value: 'humen',
                        label: '虎门镇',
                    },
                ],
            },
        ],
    },
    {
        value: 'hunan',
        label: '湖南省',
        children: [
            {
                value: 'changsha',
                label: '长沙市',
                children: [
                    {
                        value: 'yuelu',
                        label: '岳麓区',
                    },
                ],
            },
        ],
    },
];
const rules: FormRules = {
    name: [{ required: true, message: '请输入表单名称', trigger: 'blur' }],
};
const formRef = ref<FormInstance>();
const form = reactive({
    name: '',
    region: '',
    date: '',
    time: '',
    delivery: true,
    type: ['小明'],
    resource: '小红',
    desc: '',
    options: [],
    color: '',
    num: 1,
    rate: 0,
    transfer: [],

});
const generateData = () => {
    const data = []
    for (let i = 1; i <= 15; i++) {
        data.push({
            key: i,
            label: `Option ${i}`,
            disabled: i % 4 === 0,
        })
    }
    return data
}

const transferData = ref(generateData())
// 提交
const onSubmit = (formEl: FormInstance | undefined) => {
    // 表单校验
    if (!formEl) return;
    formEl.validate((valid) => {
        if (valid) {
            console.log(form);
            ElMessage.success('提交成功!');
        } else {
            return false;
        }
    });
};
// 重置
const onReset = (formEl: FormInstance | undefined) => {
    if (!formEl) return;
    formEl.resetFields();
};
</script>

================================================
FILE: src/views/element/statistic.vue
================================================
<template>
    <div>


        <el-card class="mgb20" shadow="hover">
            <template #header>基础用法</template>
            <el-row>
                <el-col :span="6" style="text-align: center">
                    <el-statistic title="Daily active users" :value="268500" />
                </el-col>
                <el-col :span="6" style="text-align: center">
                    <el-statistic :value="138">
                        <template #title>
                            <div style="display: inline-flex; align-items: center">
                                Ratio of men to women
                            </div>
                        </template>
                        <template #suffix>/100</template>
                    </el-statistic>
                </el-col>
                <el-col :span="6" style="text-align: center">
                    <el-statistic title="数字滚动" :value="outputValue" />
                </el-col>
                <el-col :span="6" style="text-align: center">
                    <el-countdown title="倒计时" :value="value" />
                </el-col>
            </el-row>
        </el-card>

        <el-card class="mgb20" shadow="hover">
            <template #header>CountUp.js</template>
            <div class="plugins-tips">
                countup.js:用于快速创建以更有趣的方式显示数字数据的动画。 访问地址:
                <a href="https://github.com/inorganik/countUp.js" target="_blank">countUp.js</a>
            </div>
            <el-row>
                <el-col :span="8" style="text-align: center">
                    <p>基础用法</p>
                    <countup class="countup" :end="6666" />
                </el-col>
                <el-col :span="8" style="text-align: center">
                    <p>具体配置</p>
                    <countup class="countup" :end="8888.5" :options="options" />
                </el-col>
                <el-col :span="8" style="text-align: center">
                    <p>更新数值</p>
                    <countup class="countup" :end="value1" />
                </el-col>
            </el-row>
        </el-card>
        <el-card class="mgb20" shadow="never">
            <template #header>统计卡片</template>

            <el-row :gutter="20" class="mgb20">
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon color1">
                            <User />
                        </el-icon>
                        <div class="card-content text-right">
                            <el-statistic title="日活跃用户量" :value="268500" />
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon color2">
                            <ChatDotRound />
                        </el-icon>
                        <div class="card-content text-right">
                            <el-statistic title="系统消息" :value="16800" />
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon color3">
                            <Goods />
                        </el-icon>
                        <div class="card-content text-right">
                            <el-statistic title="商品数量" :value="8888" />
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon color4">
                            <ShoppingCartFull />
                        </el-icon>
                        <div class="card-content text-right">
                            <el-statistic title="今日订单量" :value="56888" />
                        </div>
                    </el-card>
                </el-col>
            </el-row>
            <el-row :gutter="20" class="mgb20">
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <div class="card-content text-left">
                            <el-statistic :value-style="{ color: '#2d8cf0' }" title="日活跃用户量" :value="268500" />
                        </div>
                        <el-icon class="card-icon color1">
                            <User />
                        </el-icon>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <div class="card-content text-left">
                            <el-statistic :value-style="{ color: '#64d572' }" title="系统消息" :value="16800" />
                        </div>
                        <el-icon class="card-icon color2">
                            <ChatDotRound />
                        </el-icon>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <div class="card-content text-left">
                            <el-statistic :value-style="{ color: '#f25e43' }" title="商品数量" :value="8888" />
                        </div>
                        <el-icon class="card-icon color3">
                            <Goods />
                        </el-icon>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <div class="card-content text-left">
                            <el-statistic :value-style="{ color: '#e9a745' }" title="今日订单量" :value="56888" />
                        </div>
                        <el-icon class="card-icon color4">
                            <ShoppingCartFull />
                        </el-icon>
                    </el-card>
                </el-col>
            </el-row>
            <el-row :gutter="20" class="mgb20">
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon bg1">
                            <User />
                        </el-icon>
                        <div class="card-content">
                            <countup class="card-num color1" :end="6666" />
                            <div>用户访问量</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon bg2">
                            <ChatDotRound />
                        </el-icon>
                        <div class="card-content">
                            <countup class="card-num color2" :end="168" />
                            <div>系统消息</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon bg3">
                            <Goods />
                        </el-icon>
                        <div class="card-content">
                            <countup class="card-num color3" :end="8888" />
                            <div>商品数量</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body">
                        <el-icon class="card-icon bg4">
                            <ShoppingCartFull />
                        </el-icon>
                        <div class="card-content">
                            <countup class="card-num color4" :end="568" />
                            <div>今日订单量</div>
                        </div>
                    </el-card>
                </el-col>
            </el-row>
            <el-row :gutter="20" class="mgb20">
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body bg1">
                        <el-icon class="card-icon ">
                            <User />
                        </el-icon>
                        <div class="card-content color0">
                            <countup class="card-num" :end="6666" />
                            <div>用户访问量</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body bg2">
                        <el-icon class="card-icon">
                            <ChatDotRound />
                        </el-icon>
                        <div class="card-content color0">
                            <countup class="card-num" :end="168" />
                            <div>系统消息</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body bg3">
                        <el-icon class="card-icon">
                            <Goods />
                        </el-icon>
                        <div class="card-content color0">
                            <countup class="card-num " :end="8888" />
                            <div>商品数量</div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card shadow="hover" body-class="card-body bg4">
                        <el-icon class="card-icon">
                            <ShoppingCartFull />
                        </el-icon>
                        <div class="card-content color0">
                            <countup class="card-num " :end="568" />
                            <div>今日订单量</div>
                        </div>
                    </el-card>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { useTransition } from '@vueuse/core'
import countup from '@/components/countup.vue';

const source = ref(0)
const outputValue = useTransition(source, {
    duration: 1500,
})
source.value = 172000

const value = ref(Date.now() + 1000 * 60 * 60 * 7)
const value1 = ref(1000);
setTimeout(() => {
    value1.value = 8000;
}, 5000);
const options = {
    startVal: 1000,
    decimalPlaces: 2,
    duration: 5,
    useGrouping: false,
    prefix: '$',
    separator: ',',
    decimal: '.',
    suffix: '',
}
</script>

<style>
.card-body {
    display: flex;
    align-items: center;
    height: 100px;
    padding: 0;
}

.bg1 {
    background: #2d8cf0;
}

.bg2 {
    background: #64d572;
}

.bg3 {
    background: #f25e43;
}

.bg4 {
    background: #e9a745;
}
</style>
<style scoped>
.countup {
    font-size: 24px;
}

.card-content {
    flex: 1;
    text-align: center;
    font-size: 14px;
    color: #999;
    padding: 0 20px;
}

.card-num {
    font-size: 30px;
}

.card-icon {
    font-size: 50px;
    width: 100px;
    height: 100px;
    text-align: center;
    line-height: 100px;
    color: #fff;
}


.color0 {
    color: #fff;
}

.color1 {
    color: #2d8cf0;
}

.color2 {
    color: #64d572;
}

.color3 {
    color: #f25e43;
}

.color4 {
    color: #e9a745;
}

.text-right {
    text-align: right;
}

.text-left {
    text-align: left;
}
</style>

================================================
FILE: src/views/element/steps.vue
================================================
<template>
    <div class="container">
        <div class="step-div" v-if="step === 0">
            <p>输入注册时的邮箱,我们会发送验证码到您的邮箱</p>
            <el-input placeholder="请输入邮箱"></el-input>
            <el-button class="step-btn" type="primary" @click="step++">下一步</el-button>
        </div>
        <div class="step-div" v-else-if="step === 1">
            <p>验证码已发送至您的邮箱,请输入验证码</p>
            <el-input placeholder="请输入验证码"></el-input>
            <el-button class="step-btn" type="primary" @click="step++">下一步</el-button>
        </div>

        <div class="step-div" v-else-if="step === 2">
            <p>请输入6位以上密码</p>
            <el-input placeholder="请输入新密码"></el-input>
            <el-button class="step-btn" type="primary" @click="step++">保存</el-button>
        </div>
        <div v-else>
            <el-result icon="success" title="保存成功" sub-title="请退出后重新登录"></el-result>
        </div>
        <el-steps class="step-style" :active="step" align-center finish-status="success">
            <el-step title="Step 1" description="填写邮箱" />
            <el-step title="Step 2" description="填写验证码" />
            <el-step title="Step 3" description="修改密码" />
        </el-steps>
        <el-steps class="step-style" :active="step" finish-status="success" simple>
            <el-step title="填写邮箱" />
            <el-step title="填写验证码" />
            <el-step title="修改密码" />
        </el-steps>
    </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
const step = ref(0)
</script>

<style scoped>
.step-div {
    max-width: 500px;
    margin: 0 auto;
}

.step-div p {
    margin-bottom: 20px;
    color: #787878;
}

.step-btn {
    display: block;
    width: 100%;
    margin: 20px 0;
}

.step-style {
    max-width: 800px;
    margin: 40px auto;
}
</style>

================================================
FILE: src/views/element/tabs.vue
================================================
<template>
	<el-tabs v-model="message" type="card">
		<el-tab-pane :label="`未读消息(${state.unread.length})`" name="first">
			<el-table :data="state.unread" :show-header="false" style="width: 100%">
				<el-table-column>
					<template #default="scope">
						<span class="message-title">{{ scope.row.title }}</span>
					</template>
				</el-table-column>
				<el-table-column prop="date" width="180"></el-table-column>
				<el-table-column width="120">
					<template #default="scope">
						<el-button size="small" @click="handleRead(scope.$index)">标为已读</el-button>
					</template>
				</el-table-column>
			</el-table>
			<div class="handle-row">
				<el-button type="primary">全部标为已读</el-button>
			</div>
		</el-tab-pane>
		<el-tab-pane :label="`已读消息(${state.read.length})`" name="second">
			<template v-if="message === 'second'">
				<el-table :data="state.read" :show-header="false" style="width: 100%">
					<el-table-column>
						<template #default="scope">
							<span class="message-title">{{ scope.row.title }}</span>
						</template>
					</el-table-column>
					<el-table-column prop="date" width="180"></el-table-column>
					<el-table-column width="120">
						<template #default="scope">
							<el-button type="danger" size="small" @click="handleDel(scope.$index)">删除</el-button>
						</template>
					</el-table-column>
				</el-table>
				<div class="handle-row">
					<el-button type="danger">删除全部</el-button>
				</div>
			</template>
		</el-tab-pane>
		<el-tab-pane :label="`回收站(${state.recycle.length})`" name="third">
			<template v-if="message === 'third'">
				<el-table :data="state.recycle" :show-header="false" style="width: 100%">
					<el-table-column>
						<template #default="scope">
							<span class="message-title">{{ scope.row.title }}</span>
						</template>
					</el-table-column>
					<el-table-column prop="date" width="180"></el-table-column>
					<el-table-column width="120">
						<template #default="scope">
							<el-button size="small" @click="handleRestore(scope.$index)">还原</el-button>
						</template>
					</el-table-column>
				</el-table>
				<div class="handle-row">
					<el-button type="danger">清空回收站</el-button>
				</div>
			</template>
		</el-tab-pane>
	</el-tabs>
</template>

<script setup lang="ts" name="tabs">
import { ref, reactive } from 'vue';

const message = ref('first');
const state = reactive({
	unread: [
		{
			date: '2018-04-19 20:00:00',
			title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
		},
		{
			date: '2018-04-19 21:00:00',
			title: '今晚12点整发大红包,先到先得'
		}
	],
	read: [
		{
			date: '2018-04-19 20:00:00',
			title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
		}
	],
	recycle: [
		{
			date: '2018-04-19 20:00:00',
			title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
		}
	]
});

const handleRead = (index: number) => {
	const item = state.unread.splice(index, 1);
	state.read = item.concat(state.read);
};
const handleDel = (index: number) => {
	const item = state.read.splice(index, 1);
	state.recycle = item.concat(state.recycle);
};
const handleRestore = (index: number) => {
	const item = state.recycle.splice(index, 1);
	state.read = item.concat(state.read);
};
</script>

<style>
.message-title {
	cursor: pointer;
	color: var(--el-color-primary);
}

.handle-row {
	margin-top: 30px;
}
</style>


================================================
FILE: src/views/element/tour.vue
================================================
<template>
    <div class="container">
        <el-button type="primary" @click="open = true">开始引导</el-button>

        <el-divider />

        <el-space>
            <el-button ref="ref1">上传</el-button>
            <el-button ref="ref2" type="primary">保存</el-button>
            <el-button ref="ref3" :icon="MoreFilled" />
        </el-space>

        <el-tour v-model="open">
            <el-tour-step :target="ref1?.$el" title="上传文件">
                <img style="width: 120px" src="../../assets/img/img.jpg" alt="tour.png" />
                <div>点击这里选择文件</div>
            </el-tour-step>
            <el-tour-step :target="ref2?.$el" title="保存" description="点击进行上传" />
            <el-tour-step :target="ref3?.$el" title="更多操作" description="点击查看更多操作" />
        </el-tour>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { MoreFilled } from '@element-plus/icons-vue'

const ref1 = ref()
const ref2 = ref()
const ref3 = ref()

const open = ref(false)
</script>

================================================
FILE: src/views/element/upload.vue
================================================
<template>
    <div class="container">
        <div class="content-title">支持拖拽</div>
        <div class="plugins-tips">
            Element Plus自带上传组件。 访问地址:
            <a href="https://element-plus.org/zh-CN/component/upload.html" target="_blank">Element Plus Upload</a>
        </div>
        <el-upload class="upload-demo" drag action="http://jsonplaceholder.typicode.com/api/posts/" multiple
            :on-change="handle">
            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
            <div class="el-upload__text">
                将文件拖到此处,或
                <em>点击上传</em>
            </div>
        </el-upload>

        <div class="content-title">支持裁剪</div>
        <div class="plugins-tips">
            vue-cropper:一个简单的vue图片裁剪插件。 访问地址:
            <a href="https://github.com/xyxiao001/vue-cropper" target="_blank">vue-cropper</a>。 示例请查看
            <router-link to="/ucenter">个人中心-我的头像</router-link>
        </div>
    </div>
</template>

<script setup lang="ts">
const handle = (rawFile: any) => {
    console.log(rawFile);
};
</script>

<style scoped>
.content-title {
    font-weight: 400;
    line-height: 50px;
    margin: 10px 0;
    font-size: 22px;
    color: #1f2f3d;
}

.upload-demo {
    width: 360px;
}
</style>


================================================
FILE: src/views/element/watermark.vue
================================================
<template>
    <div class="container">
        <el-row :gutter="20">
            <el-col :span="18">
                <el-watermark :content="config.content" :font="config.font" :z-index="config.zIndex"
                    :rotate="config.rotate" :gap="config.gap" :offset="config.offset">
                    <div style="height: 600px" />
                </el-watermark>
            </el-col>
            <el-col :span="6">
                <el-form class="form" :model="config" label-position="top" label-width="50px">
                    <el-form-item label="Content">
                        <el-input v-model="config.content" />
                    </el-form-item>
                    <el-form-item label="Color">
                        <el-color-picker v-model="config.font.color" show-alpha />
                    </el-form-item>
                    <el-form-item label="FontSize">
                        <el-slider v-model="config.font.fontSize" />
                    </el-form-item>
                    <el-form-item label="zIndex">
                        <el-slider v-model="config.zIndex" />
                    </el-form-item>
                    <el-form-item label="Rotate">
                        <el-slider v-model="config.rotate" :min="-180" :max="180" />
                    </el-form-item>
                    <el-form-item label="Gap">
                        <el-space>
                            <el-input-number v-model="config.gap[0]" controls-position="right" />
                            <el-input-number v-model="config.gap[1]" controls-position="right" />
                        </el-space>
                    </el-form-item>
                    <el-form-item label="Offset">
                        <el-space>
                            <el-input-number v-model="config.offset[0]" placeholder="offsetLeft"
                                controls-position="right" />
                            <el-input-number v-model="config.offset[1]" placeholder="offsetTop"
                                controls-position="right" />
                        </el-space>
                    </el-form-item>
                </el-form>
            </el-col>
        </el-row>

    </div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'

const config = reactive({
    content: 'vue-manage-system',
    font: {
        fontSize: 16,
        color: 'rgba(0, 0, 0, 0.15)',
    },
    zIndex: -1,
    rotate: -22,
    gap: [100, 100] as [number, number],
    offset: [] as unknown as [number, number],
})
</script>

================================================
FILE: src/views/home.vue
================================================
<template>
    <div class="wrapper">
        <v-header />
        <v-sidebar />
        <div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
            <v-tabs></v-tabs>
            <div class="content">
                <router-view v-slot="{ Component }">
                    <transition name="move" mode="out-in">
                        <keep-alive :include="tabs.nameList">
                            <component :is="Component"></component>
                        </keep-alive>
                    </transition>
                </router-view>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { useSidebarStore } from '@/store/sidebar';
import { useTabsStore } from '@/store/tabs';
import vHeader from '@/components/header.vue';
import vSidebar from '@/components/sidebar.vue';
import vTabs from '@/components/tabs.vue';

const sidebar = useSidebarStore();
const tabs = useTabsStore();
</script>

<style>
.wrapper {
    height: 100vh;
    overflow: hidden;
}
.content-box {
    position: absolute;
    left: 250px;
    right: 0;
    top: 70px;
    bottom: 0;
    padding-bottom: 30px;
    -webkit-transition: left 0.3s ease-in-out;
    transition: left 0.3s ease-in-out;
    background: #eef0fc;
    overflow: hidden;
}

.content {
    width: auto;
    height: 100%;
    padding: 20px;
    overflow-y: scroll;
    box-sizing: border-box;
}

.content::-webkit-scrollbar {
    width: 0;
}

.content-collapse {
    left: 65px;
}
</style>


================================================
FILE: src/views/pages/403.vue
================================================
<template>
    <div class="error-page">
        <div class="error-box">
            <div class="error-code">403</div>
            <div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
            <div class="error-handle">
                <router-link to="/">
                    <el-button type="primary" size="large">返回首页</el-button>
                </router-link>
                <el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts" name="403">
import { useRouter } from 'vue-router';

const router = useRouter();
const goBack = () => {
    router.go(-2);
};
</script>

<style scoped>
.error-page {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    width: 100%;
    height: 100vh;
    background: #eef0fc;
    box-sizing: border-box;
}

.error-box {
    width: 400px;
    background-color: #fff;
    padding: 80px 50px;
    border-radius: 5px;
}

.error-code {
    line-height: 1;
    font-size: 100px;
    font-weight: bold;
    color: var(--el-color-primary);
    margin-bottom: 20px;
    text-align: center;
}

.error-desc {
    font-size: 20px;
    color: #777;
    text-align: center;
}

.error-handle {
    margin-top: 50px;
    text-align: center;
}

.error-btn {
    margin-left: 100px;
}
</style>


================================================
FILE: src/views/pages/404.vue
================================================
<template>
    <div class="error-page">
        <div class="error-box">
            <div class="error-code">404</div>
            <div class="error-desc">啊哦~ 你所访问的页面不存在</div>
            <div class="error-handle">
                <router-link to="/">
                    <el-button type="primary" size="large">返回首页</el-button>
                </router-link>
                <el-button class="error-btn" size="large" @click="goBack">返回上一页</el-button>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts" name="404">
import { useRouter } from 'vue-router';

const router = useRouter();
const goBack = () => {
    router.go(-1);
};
</script>

<style scoped>
.error-page {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    width: 100%;
    height: 100vh;
    background: #eef0fc;
    box-sizing: border-box;
}

.error-box {
    width: 400px;
    background-color: #fff;
    padding: 80px 50px;
    border-radius: 5px;
}

.error-code {
    line-height: 1;
    font-size: 100px;
    font-weight: bold;
    color: var(--el-color-primary);
    margin-bottom: 20px;
    text-align: center;
}

.error-desc {
    font-size: 20px;
    color: #777;
    text-align: center;
}

.error-handle {
    margin-top: 50px;
    text-align: center;
}

.error-btn {
    margin-left: 100px;
}
</style>


================================================
FILE: src/views/pages/editor.vue
================================================
<template>
    <div class="container">
        <div class="plugins-tips">
            wangEditor:轻量级 web 富文本编辑器,配置方便,使用简单。 访问地址:
            <a href="https://www.wangeditor.com/doc/" target="_blank">wangEditor</a>
        </div>
        <div style="border: 1px solid #ccc; margin-bottom: 10px">
            <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" />
            <Editor
                style="height: 500px; overflow-y: hidden"
                v-model="valueHtml"
                :defaultConfig="editorConfig"
                @onCreated="handleCreated"
            />
        </div>
        <el-button type="primary" @click="syncHTML">提交</el-button>
    </div>
</template>

<script setup lang="ts" name="editor">
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import { onBeforeUnmount, ref, reactive, shallowRef, onMounted } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();

// 内容 HTML
const valueHtml = ref('<p>hello</p>');

// 模拟 ajax 异步获取内容
onMounted(() => {
    setTimeout(() => {
        valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>';
    }, 1500);
});

const toolbarConfig = {};
const editorConfig = { placeholder: '请输入内容...' };

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
    const editor = editorRef.value;
    if (editor == null) return;
    editor.destroy();
});

const handleCreated = (editor: any) => {
    editorRef.value = editor; // 记录 editor 实例,重要!
};
const syncHTML = () => {
    console.log(valueHtml.value);
};
</script>

<style></style>


================================================
FILE: src/views/pages/icon.vue
================================================
<template>

	<el-tabs type="border-card">
		<el-tab-pane label="自定义图标">
			<h2>使用方法</h2>
			<p style="line-height: 50px">
				直接通过设置类名为 el-icon-lx-iconName 来使用即可。例如:(共{{ iconList.length }}个图标)
			</p>
			<p class="example-p">
				<i class="el-icon-lx-redpacket_fill" style="font-size: 30px; color: #ff5900"></i>
				<span>&lt;i class=&quot;el-icon-lx-redpacket_fill&quot;&gt;&lt;/i&gt;</span>
			</p>
			<p class="example-p">
				<i class="el-icon-lx-weibo" style="font-size: 30px; color: #fd5656"></i>
				<span>&lt;i class=&quot;el-icon-lx-weibo&quot;&gt;&lt;/i&gt;</span>
			</p>
			<p class="example-p">
				<i class="el-icon-lx-emojifill" style="font-size: 30px; color: #ffc300"></i>
				<span>&lt;i class=&quot;el-icon-lx-emojifill&quot;&gt;&lt;/i&gt;</span>
			</p>
			<br />
			<h2>图标</h2>
			<div class="search-box">
				<el-input class="search" size="large" v-model="keyword" clearable placeholder="请输入图标名称"></el-input>
			</div>
			<ul>
				<li class="icon-li" v-for="(item, index) in list" :key="index">
					<div class="icon-li-content">
						<i :class="`el-icon-lx-${item}`"></i>
						<span>{{ item }}</span>
					</div>
				</li>
			</ul>
		</el-tab-pane>
		<el-tab-pane label="Element图标">
			<el-link type="primary" href="https://element-plus.org/zh-CN/component/icon.html#icon-collection"
				target="_blank">前往官方文档查看</el-link>
		</el-tab-pane>
	</el-tabs>
</template>

<script setup lang="ts" name="icon">
import { computed, ref } from 'vue';

const iconList: Array<string> = [
	'attentionforbid',
	'attentionforbidfill',
	'attention',
	'attentionfill',
	'tag',
	'tagfill',
	'people',
	'peoplefill',
	'notice',
	'noticefill',
	'mobile',
	'mobilefill',
	'voice',
	'voicefill',
	'unlock',
	'lock',
	'home',
	'homefill',
	'delete',
	'deletefill',
	'notification',
	'notificationfill',
	'notificationforbidfill',
	'like',
	'likefill',
	'comment',
	'commentfill',
	'camera',
	'camerafill',
	'warn',
	'warnfill',
	'time',
	'timefill',
	'location',
	'locationfill',
	'favor',
	'favorfill',
	'skin',
	'skinfill',
	'news',
	'newsfill',
	'record',
	'recordfill',
	'emoji',
	'emojifill',
	'message',
	'messagefill',
	'goods',
	'goodsfill',
	'crown',
	'crownfill',
	'move',
	'add',
	'hot',
	'hotfill',
	'service',
	'servicefill',
	'present',
	'presentfill',
	'pic',
	'picfill',
	'rank',
	'rankfill',
	'male',
	'female',
	'down',
	'top',
	'recharge',
	'rechargefill',
	'forward',
	'forwardfill',
	'info',
	'infofill',
	'redpacket',
	'redpacket_fill',
	'roundadd',
	'roundaddfill',
	'friendadd',
	'friendaddfill',
	'cart',
	'cartfill',
	'more',
	'moreandroid',
	'back',
	'right',
	'shop',
	'shopfill',
	'question',
	'questionfill',
	'roundclose',
	'roundclosefill',
	'roundcheck',
	'roundcheckfill',
	'global',
	'mail',
	'punch',
	'exit',
	'upload',
	'read',
	'file',
	'link',
	'full',
	'group',
	'friend',
	'profile',
	'addressbook',
	'calendar',
	'text',
	'copy',
	'share',
	'wifi',
	'vipcard',
	'weibo',
	'remind',
	'refresh',
	'filter',
	'settings',
	'scan',
	'qrcode',
	'cascades',
	'apps',
	'sort',
	'searchlist',
	'search',
	'edit',
	'apple-line',
	'baidu-fill',
	'amazon-fill',
	'netease-cloud-music-fill',
	'qq-line',
	'wechat-fill',
	'alipay-fill',
	'android-fill',
	'android-line',
	'whatsapp-line',
	'whatsapp-fill',
	'bilibili-fill',
	'chrome-fill',
	'dingding-fill',
	'dingding-line',
	'apple-fill',
	'github-fill',
	'qq-fill',
	'wechat-pay-fill',
	'windows-line',
	'windows-fill',
	'youtube-line',
	'youtube-fill',
	'wechat-pay-line',
	'zhihu-line'
];

const keyword = ref('');
const list = computed(() => {
	return iconList.filter(item => {
		return item.indexOf(keyword.value) !== -1;
	});
});
</script>

<style scoped>
.example-p {
	height: 45px;
	display: flex;
	align-items: center;
}

.search-box {
	text-align: center;
	margin-top: 10px;
}

.search {
	width: 300px;
}

ul,
li {
	list-style: none;
}

.icon-li {
	display: inline-block;
	padding: 10px;
	width: 120px;
	height: 120px;
}

.icon-li-content {
	display: flex;
	height: 100%;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}

.icon-li-content i {
	font-size: 36px;
	color: #606266;
}

.icon-li-content span {
	margin-top: 10px;
	color: #787878;
}

.iframe {
	width: 100%;
	height: 700px;
}
</style>


================================================
FILE: src/views/pages/login.vue
================================================
<template>
    <div class="login-bg">
        <div class="login-container">
            <div class="login-header">
                <img class="logo mr10" src="../../assets/img/logo.svg" alt="" />
                <div class="login-title">后台管理系统</div>
            </div>
            <el-form :model="param" :rules="rules" ref="login" size="large">
                <el-form-item prop="username">
                    <el-input v-model="param.username" placeholder="用户名">
                        <template #prepend>
                            <el-icon>
                                <User />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <el-form-item prop="password">
                    <el-input
                        type="password"
                        placeholder="密码"
                        v-model="param.password"
                        @keyup.enter="submitForm(login)"
                    >
                        <template #prepend>
                            <el-icon>
                                <Lock />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <div class="pwd-tips">
                    <el-checkbox class="pwd-checkbox" v-model="checked" label="记住密码" />
                    <el-link type="primary" @click="$router.push('/reset-pwd')">忘记密码</el-link>
                </div>
                <el-button class="login-btn" type="primary" size="large" @click="submitForm(login)">登录</el-button>
                <p class="login-tips">Tips : 用户名和密码随便填。</p>
                <p class="login-text">
                    没有账号?<el-link type="primary" @click="$router.push('/register')">立即注册</el-link>
                </p>
            </el-form>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue';
import { useTabsStore } from '@/store/tabs';
import { usePermissStore } from '@/store/permiss';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import type { FormInstance, FormRules } from 'element-plus';

interface LoginInfo {
    username: string;
    password: string;
}

const lgStr = localStorage.getItem('login-param');
const defParam = lgStr ? JSON.parse(lgStr) : null;
const checked = ref(lgStr ? true : false);

const router = useRouter();
const param = reactive<LoginInfo>({
    username: defParam ? defParam.username : '',
    password: defParam ? defParam.password : '',
});

const rules: FormRules = {
    username: [
        {
            required: true,
            message: '请输入用户名',
            trigger: 'blur',
        },
    ],
    password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
};
const permiss = usePermissStore();
const login = ref<FormInstance>();
const submitForm = (formEl: FormInstance | undefined) => {
    if (!formEl) return;
    formEl.validate((valid: boolean) => {
        if (valid) {
            ElMessage.success('登录成功');
            localStorage.setItem('vuems_name', param.username);
            const keys = permiss.defaultList[param.username == 'admin' ? 'admin' : 'user'];
            permiss.handleSet(keys);
            router.push('/');
            if (checked.value) {
                localStorage.setItem('login-param', JSON.stringify(param));
            } else {
                localStorage.removeItem('login-param');
            }
        } else {
            ElMessage.error('登录失败');
            return false;
        }
    });
};

const tabs = useTabsStore();
tabs.clearTabs();
</script>

<style scoped>
.login-bg {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100vh;
    background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
}

.login-header {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 40px;
}

.logo {
    width: 35px;
}

.login-title {
    font-size: 22px;
    color: #333;
    font-weight: bold;
}

.login-container {
    width: 450px;
    border-radius: 5px;
    background: #fff;
    padding: 40px 50px 50px;
    box-sizing: border-box;
}

.pwd-tips {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 14px;
    margin: -10px 0 10px;
    color: #787878;
}

.pwd-checkbox {
    height: auto;
}

.login-btn {
    display: block;
    width: 100%;
}

.login-tips {
    font-size: 12px;
    color: #999;
}

.login-text {
    display: flex;
    align-items: center;
    margin-top: 20px;
    font-size: 14px;
    color: #787878;
}
</style>


================================================
FILE: src/views/pages/markdown.vue
================================================
<template>
	<div class="container">
		<div class="plugins-tips">
			md-editor-v3:vue3版本的 markdown 编辑器,配置丰富,请详看文档。 访问地址:
			<a href="https://imzbf.github.io/md-editor-v3/index" target="_blank">md-editor-v3</a>
		</div>
		<md-editor class="mgb20" v-model="text" @on-upload-img="onUploadImg" />
		<el-button type="primary">提交</el-button>
	</div>
</template>

<script setup lang="ts" name="md">
import { ref } from 'vue';
import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

const text = ref('Hello Editor!');
const onUploadImg = (files: any) => {
	console.log(files);
};
</script>


================================================
FILE: src/views/pages/register.vue
================================================
<template>
    <div class="login-bg">
        <div class="login-container">
            <div class="login-header">
                <img class="logo mr10" src="../../assets/img/logo.svg" alt="" />
                <div class="login-title">后台管理系统</div>
            </div>
            <el-form :model="param" :rules="rules" ref="register" size="large">
                <el-form-item prop="username">
                    <el-input v-model="param.username" placeholder="用户名">
                        <template #prepend>
                            <el-icon>
                                <User />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <el-form-item prop="email">
                    <el-input v-model="param.email" placeholder="邮箱">
                        <template #prepend>
                            <el-icon>
                                <Message />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <el-form-item prop="password">
                    <el-input
                        type="password"
                        placeholder="密码"
                        v-model="param.password"
                        @keyup.enter="submitForm(register)"
                    >
                        <template #prepend>
                            <el-icon>
                                <Lock />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <el-button class="login-btn" type="primary" size="large" @click="submitForm(register)">注册</el-button>
                <p class="login-text">
                    已有账号,<el-link type="primary" @click="$router.push('/login')">立即登录</el-link>
                </p>
            </el-form>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { Register } from '@/types/user';

const router = useRouter();
const param = reactive<Register>({
    username: '',
    password: '',
    email: '',
});

const rules: FormRules = {
    username: [
        {
            required: true,
            message: '请输入用户名',
            trigger: 'blur',
        },
    ],
    password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
    email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
};
const register = ref<FormInstance>();
const submitForm = (formEl: FormInstance | undefined) => {
    if (!formEl) return;
    formEl.validate((valid: boolean) => {
        if (valid) {
            ElMessage.success('注册成功,请登录');
            router.push('/login');
        } else {
            return false;
        }
    });
};
</script>

<style scoped>
.login-bg {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100vh;
    background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
}

.login-header {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 40px;
}

.logo {
    width: 35px;
}

.login-title {
    font-size: 22px;
    color: #333;
    font-weight: bold;
}

.login-container {
    width: 450px;
    border-radius: 5px;
    background: #fff;
    padding: 40px 50px 50px;
    box-sizing: border-box;
}

.login-btn {
    display: block;
    width: 100%;
}

.login-text {
    display: flex;
    align-items: center;
    margin-top: 20px;
    font-size: 14px;
    color: #787878;
}
</style>


================================================
FILE: src/views/pages/reset-pwd.vue
================================================
<template>
    <div class="login-bg">
        <div class="login-container">
            <div class="reset-title">重置密码</div>
            <p class="reset-text">输入你的邮箱,我们将发送重置密码邮件</p>
            <el-form :model="param" :rules="rules" ref="register" size="large">
                <el-form-item prop="email">
                    <el-input v-model="param.email" placeholder="邮箱">
                        <template #prepend>
                            <el-icon>
                                <Message />
                            </el-icon>
                        </template>
                    </el-input>
                </el-form-item>
                <el-button class="login-btn" type="primary" size="large" @click="submitForm(register)"
                    >发送邮件</el-button
                >
                <p class="login-text"><el-link type="primary" @click="$router.push('/login')">返回登录</el-link></p>
            </el-form>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';

const param = ref({
    email: '',
});

const rules: FormRules = {
    email: [
        { required: true, message: '请输入邮箱', trigger: 'blur' },
        {
            pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
            message: '请输入正确的邮箱格式',
            trigger: 'blur',
        },
    ],
};
const register = ref<FormInstance>();
const submitForm = (formEl: FormInstance | undefined) => {
    if (!formEl) return;
    formEl.validate((valid: boolean) => {
        if (valid) {
            ElMessage.success('邮件已发送,请注意查收');
        } else {
            return false;
        }
    });
};
</script>

<style scoped>
.login-bg {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100vh;
    background: url(../../assets/img/login-bg.jpg) center/cover no-repeat;
}

.reset-title {
    text-align: center;
    font-size: 22px;
    color: #333;
    font-weight: bold;
    margin-bottom: 10px;
}

.reset-text {
    text-align: center;
    font-size: 14px;
    color: #787878;
    margin-bottom: 40px;
}

.login-container {
    width: 450px;
    border-radius: 5px;
    background: #fff;
    padding: 40px 50px 50px;
    box-sizing: border-box;
}

.login-btn {
    display: block;
    width: 100%;
}

.login-text {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 20px;
    font-size: 14px;
    color: #333;
}
</style>


================================================
FILE: src/views/pages/theme.vue
================================================
<template>
    <div>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">系统主题</div>
            </template>
            <div class="theme-list mgb20">
                <div class="theme-item" @click="setSystemTheme(item)" v-for="item in system"
                    :style="{ backgroundColor: item.color, color: '#fff' }">{{ item.name }}
                </div>
            </div>
            <div class="flex-center">
                <el-button @click="resetSystemTheme">重置主题</el-button>
            </div>
        </el-card>
        <el-card class="mgb20" shadow="hover">
            <template #header>
                <div class="content-title">Element-Plus主题</div>
            </template>
            <div class="theme-list mgb20">
                <div class="theme-item" v-for="theme in themes">
                    <el-button :type="theme.name">{{ theme.name }}</el-button>
                    <div class="theme-color">{{ theme.color }}</div>
                    <el-color-picker v-model="color[theme.name]" @change="changeColor(theme.name)" />
                </div>
            </div>
            <div class="flex-center">
                <el-button @click="resetTheme">重置主题</el-button>
            </div>
        </el-card>

        <el-row :gutter="50">
            <el-col :span="12">
                <el-card class="mgb20" shadow="hover">
                    <template #header>
                        <div class="content-title">头部主题</div>
                    </template>
                    <div class="theme-list mgb20">
                        <div class="theme-item">
                            <el-button :color="color.headerBgColor">背景颜色</el-button>
                            <div class="theme-color">{{ color.headerBgColor }}</div>
                            <el-color-picker v-model="color.headerBgColor"
                                @change="themeStore.setHeaderBgColor(color.headerBgColor)" />
                        </div>
                        <div class="theme-item">
                            <el-button :color="color.headerTextColor">文字颜色</el-button>
                            <div class="theme-color">{{ color.headerTextColor }}</div>
                            <el-color-picker v-model="color.headerTextColor"
                                @change="themeStore.setHeaderTextColor(color.headerTextColor)" />
                        </div>
                    </div>
                    <div class="flex-center">
                        <el-button @click="resetHeader">重置主题</el-button>
                    </div>
                </el-card>
            </el-col>

            <el-col :span="12">
                <el-card class="mgb20" shadow="hover">
                    <template #header>
                        <div class="content-title">菜单主题</div>
                    </template>
                    <div class="them
Download .txt
gitextract_omct5e3d/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── auto-imports.d.ts
├── components.d.ts
├── index.html
├── package.json
├── public/
│   ├── mock/
│   │   ├── role.json
│   │   ├── table.json
│   │   └── user.json
│   └── template.xlsx
├── src/
│   ├── App.vue
│   ├── api/
│   │   └── index.ts
│   ├── assets/
│   │   └── css/
│   │       ├── icon.css
│   │       └── main.css
│   ├── components/
│   │   ├── countup.vue
│   │   ├── header.vue
│   │   ├── menu.ts
│   │   ├── sidebar.vue
│   │   ├── table-custom.vue
│   │   ├── table-detail.vue
│   │   ├── table-edit.vue
│   │   ├── table-search.vue
│   │   └── tabs.vue
│   ├── main.ts
│   ├── router/
│   │   └── index.ts
│   ├── store/
│   │   ├── permiss.ts
│   │   ├── sidebar.ts
│   │   ├── tabs.ts
│   │   └── theme.ts
│   ├── types/
│   │   ├── form-option.ts
│   │   ├── menu.ts
│   │   ├── role.ts
│   │   ├── table.ts
│   │   └── user.ts
│   ├── utils/
│   │   ├── china.ts
│   │   ├── index.ts
│   │   └── request.ts
│   ├── views/
│   │   ├── chart/
│   │   │   ├── echarts.vue
│   │   │   ├── options.ts
│   │   │   └── schart.vue
│   │   ├── dashboard.vue
│   │   ├── element/
│   │   │   ├── calendar.vue
│   │   │   ├── carousel.vue
│   │   │   ├── form.vue
│   │   │   ├── statistic.vue
│   │   │   ├── steps.vue
│   │   │   ├── tabs.vue
│   │   │   ├── tour.vue
│   │   │   ├── upload.vue
│   │   │   └── watermark.vue
│   │   ├── home.vue
│   │   ├── pages/
│   │   │   ├── 403.vue
│   │   │   ├── 404.vue
│   │   │   ├── editor.vue
│   │   │   ├── icon.vue
│   │   │   ├── login.vue
│   │   │   ├── markdown.vue
│   │   │   ├── register.vue
│   │   │   ├── reset-pwd.vue
│   │   │   ├── theme.vue
│   │   │   └── ucenter.vue
│   │   ├── system/
│   │   │   ├── menu.vue
│   │   │   ├── role-permission.vue
│   │   │   ├── role.vue
│   │   │   └── user.vue
│   │   └── table/
│   │       ├── basetable.vue
│   │       ├── export.vue
│   │       ├── import.vue
│   │       └── table-editor.vue
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (26 symbols across 11 files)

FILE: components.d.ts
  type GlobalComponents (line 9) | interface GlobalComponents {

FILE: src/main.ts
  method mounted (line 21) | mounted(el, binding) {

FILE: src/store/permiss.ts
  type ObjectList (line 3) | interface ObjectList {
  method handleSet (line 56) | handleSet(val: string[]) {

FILE: src/store/sidebar.ts
  method handleCollapse (line 13) | handleCollapse() {
  method setBgColor (line 16) | setBgColor(color: string) {
  method setTextColor (line 20) | setTextColor(color: string) {

FILE: src/store/tabs.ts
  type ListItem (line 3) | interface ListItem {
  method delTabsItem (line 24) | delTabsItem(index: number) {
  method setTabsItem (line 27) | setTabsItem(data: ListItem) {
  method clearTabs (line 30) | clearTabs() {
  method closeTabsOther (line 33) | closeTabsOther(data: ListItem[]) {
  method closeCurrentTag (line 36) | closeCurrentTag(data: any) {

FILE: src/store/theme.ts
  method initTheme (line 18) | initTheme() {
  method resetTheme (line 30) | resetTheme() {
  method setPropertyColor (line 35) | setPropertyColor(color: string, type: string = 'primary') {
  method setThemeLight (line 41) | setThemeLight(type: string = 'primary') {
  method setHeaderBgColor (line 47) | setHeaderBgColor(color: string) {
  method setHeaderTextColor (line 52) | setHeaderTextColor(color: string) {

FILE: src/types/form-option.ts
  type FormOption (line 1) | interface FormOption {
  type FormOptionList (line 8) | interface FormOptionList {

FILE: src/types/menu.ts
  type Menus (line 1) | interface Menus {

FILE: src/types/role.ts
  type Role (line 2) | interface Role {

FILE: src/types/table.ts
  type TableItem (line 1) | interface TableItem {

FILE: src/types/user.ts
  type User (line 2) | interface User {
  type Register (line 12) | interface Register {
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (258K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 683,
    "preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
  },
  {
    "path": ".gitignore",
    "chars": 254,
    "preview": ".DS_Store\r\nnode_modules\r\n/dist\r\n\r\n\r\n# local env files\r\n.env.local\r\n.env.*.local\r\n\r\n# Log files\r\nnpm-debug.log*\r\nyarn-deb"
  },
  {
    "path": "LICENSE",
    "chars": 1078,
    "preview": "MIT License\n\nCopyright (c) 2016-2023 vue-manage-system\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 2046,
    "preview": "# vue-manage-system\r\n\r\n  <a href=\"https://github.com/lin-xin/vue-manage-system/releases\">\r\n    <img src=\"https://img.shi"
  },
  {
    "path": "README_EN.md",
    "chars": 3379,
    "preview": "# vue-manage-system\r\n\r\n<a href=\"https://github.com/vuejs/vue\">\r\n    <img src=\"https://img.shields.io/badge/vue-2.6.10-br"
  },
  {
    "path": "auto-imports.d.ts",
    "chars": 69,
    "preview": "// Generated by 'unplugin-auto-import'\nexport {}\ndeclare global {\n\n}\n"
  },
  {
    "path": "components.d.ts",
    "chars": 4682,
    "preview": "// generated by unplugin-vue-components\n// We suggest you to commit this file into source control\n// Read more: https://"
  },
  {
    "path": "index.html",
    "chars": 679,
    "preview": "<!DOCTYPE html>\r\n<html lang=\"\">\r\n\r\n<head>\r\n  <meta charset=\"utf-8\">\r\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=ed"
  },
  {
    "path": "package.json",
    "chars": 1057,
    "preview": "{\r\n\t\"name\": \"vue-manage-system\",\r\n\t\"version\": \"5.5.0\",\r\n\t\"private\": true,\r\n\t\"scripts\": {\r\n\t\t\"dev\": \"vite\",\r\n\t\t\"build\": \""
  },
  {
    "path": "public/mock/role.json",
    "chars": 957,
    "preview": "{\r\n    \"list\": [\r\n        {\r\n            \"id\": 1,\r\n            \"name\": \"管理员\",\r\n            \"key\": \"admin\",\r\n            "
  },
  {
    "path": "public/mock/table.json",
    "chars": 1124,
    "preview": "{\r\n    \"list\": [\r\n        {\r\n            \"id\": 1,\r\n            \"name\": \"张三\",\r\n            \"money\": 123,\r\n            \"ad"
  },
  {
    "path": "public/mock/user.json",
    "chars": 526,
    "preview": "{\r\n    \"list\": [\r\n        {\r\n            \"id\": 1,\r\n            \"name\": \"张三\",\r\n            \"password\": \"123\",\r\n          "
  },
  {
    "path": "src/App.vue",
    "chars": 405,
    "preview": "<template>\r\n\t<el-config-provider :locale=\"zhCn\">\r\n\t\t<router-view />\r\n\t</el-config-provider>\r\n</template>\r\n\r\n<script setu"
  },
  {
    "path": "src/api/index.ts",
    "chars": 434,
    "preview": "import request from '../utils/request';\r\n\r\nexport const fetchData = () => {\r\n    return request({\r\n        url: './mock/"
  },
  {
    "path": "src/assets/css/icon.css",
    "chars": 91,
    "preview": "[class*=\" el-icon-lx\"],\r\n[class^=el-icon-lx] {\r\n    font-family: lx-iconfont !important;\r\n}"
  },
  {
    "path": "src/assets/css/main.css",
    "chars": 1316,
    "preview": "* {\r\n\tmargin: 0;\r\n\tpadding: 0;\r\n\toutline: 0 !important;\r\n}\r\n\r\n\r\nbody {\r\n\tfont-family: 'PingFang SC', 'Helvetica Neue', H"
  },
  {
    "path": "src/components/countup.vue",
    "chars": 757,
    "preview": "<template>\r\n    <span ref=\"countRef\"></span>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { onMounted, ref, watch } "
  },
  {
    "path": "src/components/header.vue",
    "chars": 5686,
    "preview": "<template>\r\n    <div class=\"header\">\r\n        <!-- 折叠按钮 -->\r\n        <div class=\"header-left\">\r\n            <img class=\""
  },
  {
    "path": "src/components/menu.ts",
    "chars": 5248,
    "preview": "import { Menus } from '@/types/menu';\r\n\r\nexport const menuData: Menus[] = [\r\n    {\r\n        id: '0',\r\n        title: '系统"
  },
  {
    "path": "src/components/sidebar.vue",
    "chars": 3087,
    "preview": "<template>\r\n    <div class=\"sidebar\">\r\n        <el-menu\r\n            class=\"sidebar-el-menu\"\r\n            :default-activ"
  },
  {
    "path": "src/components/table-custom.vue",
    "chars": 6557,
    "preview": "<template>\r\n    <div>\r\n        <div class=\"table-toolbar\" v-if=\"hasToolbar\">\r\n            <div class=\"table-toolbar-left"
  },
  {
    "path": "src/components/table-detail.vue",
    "chars": 520,
    "preview": "<template>\r\n\t<el-descriptions :title=\"title\" :column=\"column\" border>\r\n\t\t<el-descriptions-item v-for=\"item in list\" :spa"
  },
  {
    "path": "src/components/table-edit.vue",
    "chars": 3428,
    "preview": "<template>\r\n\t<el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" :label-width=\"options.labelWidth\">\r\n\t\t<el-row>\r\n\t\t\t<el-"
  },
  {
    "path": "src/components/table-search.vue",
    "chars": 1834,
    "preview": "<template>\r\n\t<div class=\"search-container\">\r\n\t\t<el-form ref=\"searchRef\" :model=\"query\" :inline=\"true\">\r\n\t\t\t<el-form-item"
  },
  {
    "path": "src/components/tabs.vue",
    "chars": 3786,
    "preview": "<template>\r\n    <div class=\"tabs-container\">\r\n        <el-tabs v-model=\"activePath\" class=\"tabs\" type=\"card\" closable @t"
  },
  {
    "path": "src/main.ts",
    "chars": 791,
    "preview": "import { createApp } from 'vue';\r\nimport { createPinia } from 'pinia';\r\nimport * as ElementPlusIconsVue from '@element-p"
  },
  {
    "path": "src/router/index.ts",
    "chars": 9637,
    "preview": "import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';\r\nimport { usePermissStore } from '../st"
  },
  {
    "path": "src/store/permiss.ts",
    "chars": 1450,
    "preview": "import { defineStore } from 'pinia';\r\n\r\ninterface ObjectList {\r\n    [key: string]: string[];\r\n}\r\n\r\nexport const usePermi"
  },
  {
    "path": "src/store/sidebar.ts",
    "chars": 635,
    "preview": "import { defineStore } from 'pinia';\r\n\r\nexport const useSidebarStore = defineStore('sidebar', {\r\n\tstate: () => {\r\n\t\tretu"
  },
  {
    "path": "src/store/tabs.ts",
    "chars": 1086,
    "preview": "import { defineStore } from 'pinia';\r\n\r\ninterface ListItem {\r\n\tname: string;\r\n\tpath: string;\r\n\ttitle: string;\r\n}\r\n\r\nexpo"
  },
  {
    "path": "src/store/theme.ts",
    "chars": 2275,
    "preview": "import { mix, setProperty } from '@/utils';\r\nimport { defineStore } from 'pinia';\r\n\r\nexport const useThemeStore = define"
  },
  {
    "path": "src/types/form-option.ts",
    "chars": 438,
    "preview": "export interface FormOption {\r\n    list: FormOptionList[];\r\n    labelWidth?: number | string;\r\n    span?: number;\r\n\r\n}\r\n"
  },
  {
    "path": "src/types/menu.ts",
    "chars": 171,
    "preview": "export interface Menus {\r\n    id: string;\r\n    pid?: string;\r\n    icon?: string;\r\n    index: string;\r\n    title: string;"
  },
  {
    "path": "src/types/role.ts",
    "chars": 127,
    "preview": "\r\nexport interface Role {\r\n    id: number;\r\n    name: string;\r\n    key: string;\r\n    status: boolean;\r\n    permiss: stri"
  },
  {
    "path": "src/types/table.ts",
    "chars": 168,
    "preview": "export interface TableItem {\r\n    id: number;\r\n    name: string;\r\n    thumb: string;\r\n    money: number;\r\n    state: str"
  },
  {
    "path": "src/types/user.ts",
    "chars": 265,
    "preview": "\r\nexport interface User {\r\n    id: number;\r\n    name: string;\r\n    password: string;\r\n    email: string;\r\n    phone: str"
  },
  {
    "path": "src/utils/china.ts",
    "chars": 41098,
    "preview": "let chinaMap:any = {\"type\":\"FeatureCollection\",\"features\":[{\"id\":\"710000\",\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates"
  },
  {
    "path": "src/utils/index.ts",
    "chars": 574,
    "preview": "export const setProperty = (prop: string, val: any, dom = document.documentElement) => {\r\n    dom.style.setProperty(prop"
  },
  {
    "path": "src/utils/request.ts",
    "chars": 738,
    "preview": "import axios, { AxiosInstance, AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';\r\n\r\nconst service: A"
  },
  {
    "path": "src/views/chart/echarts.vue",
    "chars": 2710,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <div class=\"plugins-tips\">\r\n            vue-echarts:Apache ECharts™ 的 V"
  },
  {
    "path": "src/views/chart/options.ts",
    "chars": 8882,
    "preview": "import { graphic } from 'echarts/core';\r\nexport const barOptions = {\r\n    xAxis: {\r\n        type: 'category',\r\n        d"
  },
  {
    "path": "src/views/chart/schart.vue",
    "chars": 2801,
    "preview": "<template>\r\n\t<div class=\"container\">\r\n\t\t<div class=\"plugins-tips\">\r\n\t\t\tvue-schart:vue.js封装sChart.js的图表组件。 访问地址:\r\n\t\t\t<a h"
  },
  {
    "path": "src/views/dashboard.vue",
    "chars": 9667,
    "preview": "<template>\r\n    <div>\r\n        <el-row :gutter=\"20\" class=\"mgb20\">\r\n            <el-col :span=\"6\">\r\n                <el-"
  },
  {
    "path": "src/views/element/calendar.vue",
    "chars": 1908,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <el-calendar v-model=\"value\">\r\n            <template #date-cell=\"{ data"
  },
  {
    "path": "src/views/element/carousel.vue",
    "chars": 2097,
    "preview": "<template>\r\n    <div>\r\n        <el-card class=\"mgb20\">\r\n            <template #header>基础用法</template>\r\n            <el-c"
  },
  {
    "path": "src/views/element/form.vue",
    "chars": 7060,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <el-radio-group class=\"mgb20\" v-model=\"labelPosition\">\r\n            <el"
  },
  {
    "path": "src/views/element/statistic.vue",
    "chars": 12052,
    "preview": "<template>\r\n    <div>\r\n\r\n\r\n        <el-card class=\"mgb20\" shadow=\"hover\">\r\n            <template #header>基础用法</template>"
  },
  {
    "path": "src/views/element/steps.vue",
    "chars": 1839,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <div class=\"step-div\" v-if=\"step === 0\">\r\n            <p>输入注册时的邮箱,我们会发送"
  },
  {
    "path": "src/views/element/tabs.vue",
    "chars": 3371,
    "preview": "<template>\r\n\t<el-tabs v-model=\"message\" type=\"card\">\r\n\t\t<el-tab-pane :label=\"`未读消息(${state.unread.length})`\" name=\"first"
  },
  {
    "path": "src/views/element/tour.vue",
    "chars": 1030,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <el-button type=\"primary\" @click=\"open = true\">开始引导</el-button>\r\n\r\n    "
  },
  {
    "path": "src/views/element/upload.vue",
    "chars": 1302,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <div class=\"content-title\">支持拖拽</div>\r\n        <div class=\"plugins-tips"
  },
  {
    "path": "src/views/element/watermark.vue",
    "chars": 2616,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <el-row :gutter=\"20\">\r\n            <el-col :span=\"18\">\r\n               "
  },
  {
    "path": "src/views/home.vue",
    "chars": 1567,
    "preview": "<template>\r\n    <div class=\"wrapper\">\r\n        <v-header />\r\n        <v-sidebar />\r\n        <div class=\"content-box\" :cl"
  },
  {
    "path": "src/views/pages/403.vue",
    "chars": 1428,
    "preview": "<template>\r\n    <div class=\"error-page\">\r\n        <div class=\"error-box\">\r\n            <div class=\"error-code\">403</div>"
  },
  {
    "path": "src/views/pages/404.vue",
    "chars": 1427,
    "preview": "<template>\r\n    <div class=\"error-page\">\r\n        <div class=\"error-box\">\r\n            <div class=\"error-code\">404</div>"
  },
  {
    "path": "src/views/pages/editor.vue",
    "chars": 1657,
    "preview": "<template>\r\n    <div class=\"container\">\r\n        <div class=\"plugins-tips\">\r\n            wangEditor:轻量级 web 富文本编辑器,配置方便,"
  },
  {
    "path": "src/views/pages/icon.vue",
    "chars": 4487,
    "preview": "<template>\r\n\r\n\t<el-tabs type=\"border-card\">\r\n\t\t<el-tab-pane label=\"自定义图标\">\r\n\t\t\t<h2>使用方法</h2>\r\n\t\t\t<p style=\"line-height: "
  },
  {
    "path": "src/views/pages/login.vue",
    "chars": 4865,
    "preview": "<template>\r\n    <div class=\"login-bg\">\r\n        <div class=\"login-container\">\r\n            <div class=\"login-header\">\r\n "
  },
  {
    "path": "src/views/pages/markdown.vue",
    "chars": 621,
    "preview": "<template>\r\n\t<div class=\"container\">\r\n\t\t<div class=\"plugins-tips\">\r\n\t\t\tmd-editor-v3:vue3版本的 markdown 编辑器,配置丰富,请详看文档。 访问地"
  },
  {
    "path": "src/views/pages/register.vue",
    "chars": 3878,
    "preview": "<template>\r\n    <div class=\"login-bg\">\r\n        <div class=\"login-container\">\r\n            <div class=\"login-header\">\r\n "
  },
  {
    "path": "src/views/pages/reset-pwd.vue",
    "chars": 2625,
    "preview": "<template>\r\n    <div class=\"login-bg\">\r\n        <div class=\"login-container\">\r\n            <div class=\"reset-title\">重置密码"
  },
  {
    "path": "src/views/pages/theme.vue",
    "chars": 6939,
    "preview": "<template>\r\n    <div>\r\n        <el-card class=\"mgb20\" shadow=\"hover\">\r\n            <template #header>\r\n                <"
  },
  {
    "path": "src/views/pages/ucenter.vue",
    "chars": 8136,
    "preview": "<template>\r\n    <div>\r\n        <div class=\"user-container\">\r\n            <el-card class=\"user-profile\" shadow=\"hover\" :b"
  },
  {
    "path": "src/views/system/menu.vue",
    "chars": 4279,
    "preview": "<template>\r\n    <div>\r\n        <div class=\"container\">\r\n            <TableCustom :columns=\"columns\" :tableData=\"menuData"
  },
  {
    "path": "src/views/system/role-permission.vue",
    "chars": 2039,
    "preview": "<template>\r\n    <div>\r\n        <el-tree\r\n            class=\"mgb10\"\r\n            ref=\"tree\"\r\n            :data=\"data\"\r\n  "
  },
  {
    "path": "src/views/system/role.vue",
    "chars": 4928,
    "preview": "<template>\r\n    <div>\r\n        <TableSearch :query=\"query\" :options=\"searchOpt\" :search=\"handleSearch\" />\r\n        <div "
  },
  {
    "path": "src/views/system/user.vue",
    "chars": 4134,
    "preview": "<template>\r\n    <div>\r\n        <TableSearch :query=\"query\" :options=\"searchOpt\" :search=\"handleSearch\" />\r\n        <div "
  },
  {
    "path": "src/views/table/basetable.vue",
    "chars": 4388,
    "preview": "<template>\r\n\t<div>\r\n\t\t<TableSearch :query=\"query\" :options=\"searchOpt\" :search=\"handleSearch\" />\r\n\t\t<div class=\"containe"
  },
  {
    "path": "src/views/table/export.vue",
    "chars": 2399,
    "preview": "<template>\r\n    <div>\r\n        <div class=\"container\">\r\n            <div class=\"handle-box\">\r\n                <el-button"
  },
  {
    "path": "src/views/table/import.vue",
    "chars": 3170,
    "preview": "<template>\r\n    <div>\r\n        <div class=\"container\">\r\n            <div class=\"handle-box\">\r\n                <el-upload"
  },
  {
    "path": "src/views/table/table-editor.vue",
    "chars": 2501,
    "preview": "<template>\r\n\t<div class=\"container\">\r\n\t\t<TableCustom :columns=\"columns\" :tableData=\"tableData\" :hasToolbar=\"false\" :hasP"
  },
  {
    "path": "src/vite-env.d.ts",
    "chars": 251,
    "preview": "/// <reference types=\"vite/client\" />\r\n\r\ndeclare module '*.vue' {\r\n  import type { DefineComponent } from 'vue'\r\n  const"
  },
  {
    "path": "tsconfig.json",
    "chars": 563,
    "preview": "{\r\n  \"compilerOptions\": {\r\n    \"target\": \"ESNext\",\r\n    \"useDefineForClassFields\": true,\r\n    \"module\": \"ESNext\",\r\n    \""
  },
  {
    "path": "tsconfig.node.json",
    "chars": 193,
    "preview": "{\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"module\": \"ESNext\",\r\n    \"moduleResolution\": \"Node\",\r\n    \"allowS"
  },
  {
    "path": "vite.config.ts",
    "chars": 735,
    "preview": "import { defineConfig } from 'vite';\r\nimport vue from '@vitejs/plugin-vue';\r\nimport VueSetupExtend from 'vite-plugin-vue"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the lin-xin/vue-manage-system GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (228.2 KB), approximately 88.9k tokens, and a symbol index with 26 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!