commit 364064e5ce4d437ba6f4ee442645a2fe93bf6bc8 Author: ljw <84855512@qq.com> Date: Fri Sep 13 16:34:15 2024 +0800 first diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..fa3a809 --- /dev/null +++ b/.env.development @@ -0,0 +1,6 @@ +ENV = 'development' + +VITE_DEV_PORT = 8888 +VITE_SERVER_API = /api/admin +VITE_SERVER_PATH = http://127.0.0.1:21114 + diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..c8396e7 --- /dev/null +++ b/.env.production @@ -0,0 +1,5 @@ +ENV = 'production' + +VITE_DEV_PORT = 5000 +VITE_SERVER_API =/api/admin +VITE_SERVER_PATH = http://127.0.0.1:5000 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..300482a --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e81e3be --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2021 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..159c6e3 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Gwen-Admin +# 基于 Vue3 + Element Plus 的后台管理系统 + + + vue3 + + + element-plus + + + license + + +# 安装步骤 + +~~~shell script +git clone https://github.com/lejianwen/Gwen-admin.git +cd Gwen-admin +npm install + +// 本地开发 +npm run dev + +// 打包 +npm run build + +// 本地预览 +npm run server +~~~ + +## 功能 + +- [x] Element Plus +- [x] 登录/注销 +- [x] 路由权限 +- [x] Dashboard +- [x] 表格 +- [x] 表单 +- [x] 图片本地/oss上传 +- [x] 404 +- [x] 多级菜单 diff --git a/index.html b/index.html new file mode 100644 index 0000000..25559fa --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Gwen-Admin + + +
+ + + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..16cdc15 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@/*": [ + "src/*" + ] + } + }, + "exclude": [ + "node_modules" + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..49b5c53 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "hello-vue3", + "version": "0.0.0", + "scripts": { + "dev": "vite --host", + "build": "vite build", + "serve": "vite preview" + }, + "dependencies": { + "axios": "1.6.0", + "element-plus": "^2.8.2", + "js-cookie": "^3.0.1", + "normalize.css": "^8.0.1", + "nprogress": "^0.2.0", + "pinia": "2.0.3", + "vue": "3.2.37", + "vue-router": "^4.0.12" + }, + "devDependencies": { + "@element-plus/icons": "0.0.11", + "@vitejs/plugin-vue": "^1.9.3", + "dotenv": "^10.0.0", + "qs": "^6.10.2", + "sass-loader": "^12.3.0", + "sass": "^1.43.4", + "vite": "^2.9.18" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..ee0d9c3 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/api/address_book.js b/src/api/address_book.js new file mode 100644 index 0000000..648945d --- /dev/null +++ b/src/api/address_book.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function list (params) { + return request({ + url: '/address_book/list', + params, + }) +} + +export function detail (id) { + return request({ + url: `/address_book/detail/${id}`, + }) +} + +export function create (data) { + return request({ + url: '/address_book/create', + method: 'post', + data, + }) +} + +export function update (data) { + return request({ + url: '/address_book/update', + method: 'post', + data, + }) +} + +export function remove (data) { + return request({ + url: '/address_book/delete', + method: 'post', + data, + }) +} + +export function changePwd (data) { + return request({ + url: '/address_book/changePwd', + method: 'post', + data, + }) +} diff --git a/src/api/file.js b/src/api/file.js new file mode 100644 index 0000000..19359d7 --- /dev/null +++ b/src/api/file.js @@ -0,0 +1,7 @@ +import request from '@/utils/request' + +export function ossToken () { + return request({ + url: '/file/oss_token', + }) +} diff --git a/src/api/group.js b/src/api/group.js new file mode 100644 index 0000000..ea48bc0 --- /dev/null +++ b/src/api/group.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function list (params) { + return request({ + url: '/group/list', + params, + }) +} + +export function detail (id) { + return request({ + url: `/group/detail/${id}`, + }) +} + +export function create (data) { + return request({ + url: '/group/create', + method: 'post', + data, + }) +} + +export function update (data) { + return request({ + url: '/group/update', + method: 'post', + data, + }) +} + +export function remove (data) { + return request({ + url: '/group/delete', + method: 'post', + data, + }) +} + +export function changePwd (data) { + return request({ + url: '/group/changePwd', + method: 'post', + data, + }) +} diff --git a/src/api/peer.js b/src/api/peer.js new file mode 100644 index 0000000..e7a24ec --- /dev/null +++ b/src/api/peer.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function list (params) { + return request({ + url: '/peer/list', + params, + }) +} + +export function detail (id) { + return request({ + url: `/peer/detail/${id}`, + }) +} + +export function create (data) { + return request({ + url: '/peer/create', + method: 'post', + data, + }) +} + +export function update (data) { + return request({ + url: '/peer/update', + method: 'post', + data, + }) +} + +export function remove (data) { + return request({ + url: '/peer/delete', + method: 'post', + data, + }) +} + +export function changePwd (data) { + return request({ + url: '/peer/changePwd', + method: 'post', + data, + }) +} diff --git a/src/api/rustdesk.js b/src/api/rustdesk.js new file mode 100644 index 0000000..ffc1ecc --- /dev/null +++ b/src/api/rustdesk.js @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export function config () { + return request({ + url: '/server-config', + method: 'get', + }) +} diff --git a/src/api/tag.js b/src/api/tag.js new file mode 100644 index 0000000..eade62f --- /dev/null +++ b/src/api/tag.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function list (params) { + return request({ + url: '/tag/list', + params, + }) +} + +export function detail (id) { + return request({ + url: `/tag/detail/${id}`, + }) +} + +export function create (data) { + return request({ + url: '/tag/create', + method: 'post', + data, + }) +} + +export function update (data) { + return request({ + url: '/tag/update', + method: 'post', + data, + }) +} + +export function remove (data) { + return request({ + url: '/tag/delete', + method: 'post', + data, + }) +} + +export function changePwd (data) { + return request({ + url: '/tag/changePwd', + method: 'post', + data, + }) +} diff --git a/src/api/user.js b/src/api/user.js new file mode 100644 index 0000000..8b83815 --- /dev/null +++ b/src/api/user.js @@ -0,0 +1,69 @@ +import request from '@/utils/request' + +export function login (data) { + return request({ + url: '/login', + method: 'post', + data, + }) +} + +export function current () { + return request({ + url: '/user/current', + method: 'get', + }) +} + +export function list (params) { + return request({ + url: '/user/list', + params, + }) +} + +export function detail (id) { + return request({ + url: `/user/detail/${id}`, + }) +} + +export function create (data) { + return request({ + url: '/user/create', + method: 'post', + data, + }) +} + +export function update (data) { + return request({ + url: '/user/update', + method: 'post', + data, + }) +} + +export function remove (data) { + return request({ + url: '/user/delete', + method: 'post', + data, + }) +} + +export function changePwd (data) { + return request({ + url: '/user/changePwd', + method: 'post', + data, + }) +} + +export function changeCurPwd (data) { + return request({ + url: '/user/changeCurPwd', + method: 'post', + data, + }) +} diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/form/address.vue b/src/components/form/address.vue new file mode 100644 index 0000000..923ad3e --- /dev/null +++ b/src/components/form/address.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/src/components/form/upload/imageUpload.vue b/src/components/form/upload/imageUpload.vue new file mode 100644 index 0000000..24712ac --- /dev/null +++ b/src/components/form/upload/imageUpload.vue @@ -0,0 +1,198 @@ + + + + diff --git a/src/components/form/upload/imagesUpload.vue b/src/components/form/upload/imagesUpload.vue new file mode 100644 index 0000000..61c71e9 --- /dev/null +++ b/src/components/form/upload/imagesUpload.vue @@ -0,0 +1,272 @@ + + + + diff --git a/src/components/form/upload/local.js b/src/components/form/upload/local.js new file mode 100644 index 0000000..3493fc3 --- /dev/null +++ b/src/components/form/upload/local.js @@ -0,0 +1,21 @@ +import { getToken } from '@/utils/auth' + +export function useLocal (beforeUp, host) { + const fileUploadData = {} + const fileUploadHost = host + const headers = { 'api-token': getToken() } + const beforeFileUpload = async (file) => { + if (beforeUp) { + const br = await beforeUp(file) + if (!br) { return Promise.reject() } + } + return Promise.resolve() + } + + return { + fileUploadData, + fileUploadHost, + beforeFileUpload, + headers, + } +} diff --git a/src/components/form/upload/oss.js b/src/components/form/upload/oss.js new file mode 100644 index 0000000..33d901c --- /dev/null +++ b/src/components/form/upload/oss.js @@ -0,0 +1,52 @@ +import { ossToken } from '@/api/file' +import { random_filename } from '@/utils/file' +import { reactive, ref } from 'vue' + +export function useOss (beforeUp, multiple) { + let fileUploadData = reactive({ + policy: '', + OSSAccessKeyId: '', + success_action_status: '200', // 让服务端返回200,不然,默认会返回204 + callback: '', + signature: '', + 'x:dir': '', + }) + const fileExpire = ref(0) + const fileUploadHost = ref('') + + const beforeFileUpload = async (file) => { + if (beforeUp) { + const br = await beforeUp(file) + if (!br) { return Promise.reject() } + } + + const now = Date.parse(new Date()) / 1000 + if (fileExpire.value < now) { + const res = await ossToken() + const obj = JSON.parse(res.data) + fileExpire.value = parseInt(obj['expire']) + fileUploadData.policy = obj['policy'] + fileUploadData.OSSAccessKeyId = obj['accessid'] + fileUploadData.callback = obj['callback'] + fileUploadData.signature = obj['signature'] + fileUploadData['x:dir'] = obj['dir'] + fileUploadHost.value = obj['host'] + } + //多选文件时需要这个,不然每个文件上传的都是一样的data + if (multiple) { + await new Promise(resolve => { + setTimeout(() => { resolve() }, 50) + }) + } + fileUploadData['x:origin_filename'] = file.name + fileUploadData.key = fileUploadData['x:dir'] + random_filename(file.name) + return Promise.resolve() + } + + return { + fileUploadHost, + fileUploadData, + beforeFileUpload, + headers: {}, + } +} diff --git a/src/global.js b/src/global.js new file mode 100644 index 0000000..f9a70e5 --- /dev/null +++ b/src/global.js @@ -0,0 +1,19 @@ +import { ref, reactive, watch } from 'vue' +import { list as fetchUsers } from '@/api/user' + +export function loadAllUsers () { + const allUsers = ref([]) + const getAllUsers = async () => { + const res = await fetchUsers({ page_size: 9999 }).catch(_ => false) + if (res) { + allUsers.value = res.data.list + } + } + + return { + allUsers, + getAllUsers, + } + +} + diff --git a/src/layout/components/aside.vue b/src/layout/components/aside.vue new file mode 100644 index 0000000..e43fbf2 --- /dev/null +++ b/src/layout/components/aside.vue @@ -0,0 +1,20 @@ + + + + diff --git a/src/layout/components/header.vue b/src/layout/components/header.vue new file mode 100644 index 0000000..5579890 --- /dev/null +++ b/src/layout/components/header.vue @@ -0,0 +1,72 @@ + + + + + + diff --git a/src/layout/components/menu/index.vue b/src/layout/components/menu/index.vue new file mode 100644 index 0000000..1814b4e --- /dev/null +++ b/src/layout/components/menu/index.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/layout/components/menu/item.vue b/src/layout/components/menu/item.vue new file mode 100644 index 0000000..b2b8786 --- /dev/null +++ b/src/layout/components/menu/item.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/layout/components/setting/index.vue b/src/layout/components/setting/index.vue new file mode 100644 index 0000000..a37dd77 --- /dev/null +++ b/src/layout/components/setting/index.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/src/layout/components/tags/index.vue b/src/layout/components/tags/index.vue new file mode 100644 index 0000000..81f3a7e --- /dev/null +++ b/src/layout/components/tags/index.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..0d97f6b --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,85 @@ + + + + + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..a1fd926 --- /dev/null +++ b/src/main.js @@ -0,0 +1,20 @@ +import { createApp } from 'vue' +import 'element-plus/dist/index.css' +import App from './App.vue' +import ElementPlus from 'element-plus' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import { router } from '@/router' +import 'normalize.css/normalize.css' +import { pinia } from '@/store' +import '@/permission' +import '@/styles/style.scss' +import * as ElementIcons from '@element-plus/icons' + +const app = createApp(App) +app.use(ElementPlus, { locale: zhCn }) +app.use(pinia) +app.use(router) +for (let icon in ElementIcons){ + app.component("ElIcon" +icon ,ElementIcons[icon]) +} +app.mount('#app') diff --git a/src/permission.js b/src/permission.js new file mode 100644 index 0000000..7260d47 --- /dev/null +++ b/src/permission.js @@ -0,0 +1,50 @@ +import { router } from '@/router' +import { useRouteStore } from '@/store/router' +import { useUserStore } from '@/store/user' +import { getToken } from '@/utils/auth' +import { pinia } from '@/store' +import NProgress from 'nprogress' // progress bar +import 'nprogress/nprogress.css' +import { useAppStore } from '@/store/app' // progress bar style + +NProgress.configure({ showSpinner: false }) // NProgress Configuration + +const whiteList = ['/login'] +const routeStore = useRouteStore(pinia) +const appStore = useAppStore(pinia) +router.beforeEach(async (to, from, next) => { + + document.title = (to.meta?.title || 'Rust-api-web') + '-' + appStore.setting.title + NProgress.start() + + const token = getToken() + if (!token) { + //无token,跳转到登录 + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + next(`/login?redirect=${to.path}`) + } + + } else { + //有token + + const userStore = useUserStore(pinia) + + if (!userStore.route_names.length) { + const info = await userStore.info() + if (!info) { + userStore.logout() + next(`/login?redirect=${to.path}`) + } else { + next({ ...to, replace: true }) + } + } else { + next() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..3214d03 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,118 @@ +import { createRouter, createWebHashHistory } from 'vue-router' + +const constantRoutes = [ + { + path: '/login', + name: 'Login', + meta: { title: '登录' }, + component: () => import('@/views/login/login.vue'), + }, + + { + path: '/404', + component: () => import('@/views/error-page/404.vue'), + hidden: true, + }, + +] +export const asyncRoutes = [ + // { + // path: '/', + // name: 'Index', + // redirect: '/Home', + // meta: { title: '首页', icon: 'house' }, + // component: () => import('@/layout/index.vue'), + // children: [ + // { + // path: '/Home', + // name: 'Home', + // meta: { title: '首页', icon: 'house' }, + // component: () => import('@/views/index/index.vue'), + // }, + // + // ], + // }, + { + path: '/my', + name: 'My', + redirect: '/my/tag/index', + meta: { title: '我的', icon: 'UserFilled' }, + component: () => import('@/layout/index.vue'), + children: [ + { + path: '/', + name: 'MyAddressBookList', + meta: { title: '地址簿管理', icon: 'Notebook' /*keepAlive: true*/ }, + component: () => import('@/views/my/address_book/index.vue'), + }, + { + path: 'tag/index', + name: 'MyTagList', + meta: { title: '标签管理', icon: 'CollectionTag' /*keepAlive: true*/ }, + component: () => import('@/views/my/tag/index.vue'), + }, + ], + }, + { + path: '/user', + name: 'User', + redirect: '/user/index', + meta: { title: '系统', icon: 'Setting' }, + component: () => import('@/layout/index.vue'), + children: [ + { + path: 'peer', + name: 'Peer', + meta: { title: '设备管理', icon: 'Monitor' /*keepAlive: true*/ }, + component: () => import('@/views/peer/index.vue'), + }, + { + path: 'group', + name: 'UserGroup', + meta: { title: '群组管理', icon: 'ChatRound' /*keepAlive: true*/ }, + component: () => import('@/views/group/index.vue'), + }, + { + path: 'index', + name: 'UserList', + meta: { title: '用户列表', icon: 'User' /*keepAlive: true*/ }, + component: () => import('@/views/user/index.vue'), + }, + { + path: 'add', + name: 'UserAdd', + meta: { title: '用户添加', hide: true }, + component: () => import('@/views/user/edit.vue'), + }, + { + path: 'edit/:id', + name: 'UserEdit', + meta: { title: '用户编辑', hide: true }, + component: () => import('@/views/user/edit.vue'), + }, + + { + path: 'addressBook', + name: 'UserAddressBook', + meta: { title: '地址簿管理', icon: 'Notebook' /*keepAlive: true*/ }, + component: () => import('@/views/address_book/index.vue'), + }, + { + path: 'tag', + name: 'UserTag', + meta: { title: '标签管理', icon: 'CollectionTag' /*keepAlive: true*/ }, + component: () => import('@/views/tag/index.vue'), + }, + + ], + }, +] +export const lastRoutes = [ + { path: '/:catchAll(.*)', redirect: '/404', meta: { hide: true } }, +] + +export const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes, +}) + diff --git a/src/store/app.js b/src/store/app.js new file mode 100644 index 0000000..7c17a34 --- /dev/null +++ b/src/store/app.js @@ -0,0 +1,23 @@ +import { defineStore, acceptHMRUpdate } from 'pinia' +import logo from '@/assets/logo.png' + +export const useAppStore = defineStore({ + id: 'App', + state: () => ({ + setting: { + title: 'Gwen-Admin', + sideIsCollapse: false, + logo, + }, + }), + + actions: { + sideCollapse () { + this.setting.sideIsCollapse = !this.setting.sideIsCollapse + }, + }, +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot)) +} diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..c569ef7 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,3 @@ +import { createPinia } from 'pinia' + +export const pinia = createPinia() diff --git a/src/store/router.js b/src/store/router.js new file mode 100644 index 0000000..e20341f --- /dev/null +++ b/src/store/router.js @@ -0,0 +1,64 @@ +import { defineStore, acceptHMRUpdate } from 'pinia' +import { lastRoutes, asyncRoutes, router } from '@/router' + +function filterRoute (routes, enableNames) { + return routes.filter(route => { + if (route.children && route.children.length) { + return enableNames.includes(route.name) || route.children.some(r => enableNames.includes(r.name)) + } else { + return enableNames.includes(route.name) + } + }).map(route => { + if (route.children && route.children.length) { + return { + ...route, + children: filterRoute(route.children, enableNames), + } + } else { + return { ...route } + } + }) +} + +export const useRouteStore = defineStore({ + id: 'router', + state: () => ({ + routes: [], + activeRoute: '', + loaded: 0, + keepAlive: [], + }), + actions: { + addRoutes (accessRouteNames) { + if (accessRouteNames.includes('*')) { + this.routes = asyncRoutes + } else { + this.routes = filterRoute(asyncRoutes, accessRouteNames) + } + + this.routes.forEach(route => { + router.addRoute(route) + }) + lastRoutes.forEach(route => { + router.addRoute(route) + }) + this.addKeepAlive(this.routes) + }, + addKeepAlive (route) { + if (route instanceof Array) { + route.forEach(r => { + this.addKeepAlive(r) + }) + } else if (route.children && route.children.length) { + this.addKeepAlive(route.children) + } else if (route.meta?.keepAlive && !this.keepAlive.includes(route.name)) { + this.keepAlive.push(route.name) + } + }, + + }, +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useRouteStore, import.meta.hot)) +} diff --git a/src/store/tags.js b/src/store/tags.js new file mode 100644 index 0000000..ccba942 --- /dev/null +++ b/src/store/tags.js @@ -0,0 +1,73 @@ +import { defineStore, acceptHMRUpdate } from 'pinia' + +export const useTagsStore = defineStore({ + id: 'tags', + state: () => ({ + tags: [], + cached: [], + }), + actions: { + initTags () { + this.tags.push( + { + name: 'Home', + path: '/Home', + title: '首页', + active: false, + closeable: false, + keepAlive: false, + }) + }, + addTag (route) { + const tags = this.tags + if (tags.find(t => t.name === route.name)) { + tags.forEach(t => t.active = false) + tags.find(t => t.name === route.name).active = true + } else { + tags.forEach(t => t.active = false) + if (route.meta?.keepAlive) { + this.addCachedTag(route.name) + } + tags.push({ + name: route.name, + path: route.fullPath, + title: route.meta?.title || route.name, + active: true, + closeable: true, + keepAlive: route.meta?.keepAlive, + }) + + } + this.$patch({ tags }) + }, + removeTag (tag) { + let tags = this.tags + if (tags.find(t => t.name === tag.name)) { + const index = tags.findIndex(t => t.name === tag.name) + if (index > -1) { + if (tags[index].keepAlive) { + this.removeCachedTag(tags[index].name) + } + tags.splice(index, 1) + } + } + this.$patch({ tags }) + }, + addCachedTag (name) { + if (!this.cached.includes(name)) { + this.cached.push(name) + } + }, + removeCachedTag (name) { + if (this.cached.includes(name)) { + this.cached.splice(this.cached.indexOf(name), 1) + } + + }, + + }, +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useTagsStore, import.meta.hot)) +} diff --git a/src/store/user.js b/src/store/user.js new file mode 100644 index 0000000..54222ef --- /dev/null +++ b/src/store/user.js @@ -0,0 +1,62 @@ +import { defineStore, acceptHMRUpdate } from 'pinia' +import { current, login } from '@/api/user' +import { setToken, removeToken } from '@/utils/auth' +import { useRouteStore } from '@/store/router' + +export const useUserStore = defineStore({ + id: 'user', + state: () => ({ + nickname: '', + username: '', + token: '', + role: '', + avatar: '', + route_names: [], + }), + + actions: { + logout () { + removeToken() + this.$patch({ + name: '', + role: {}, + }) + }, + + async login (form) { + const res = await login(form).catch(_ => false) + if (res) { + const userData = res.data + setToken(userData.token) + // + localStorage.setItem('user_info', JSON.stringify({ name: userData.username })) + this.$patch({ + ...userData, + }) + if (userData.route_names && userData.route_names.length) { + useRouteStore().addRoutes(userData.route_names) + } + return userData + } else { + return false + } + }, + async info () { + const res = await current().catch(_ => false) + if (res) { + const userData = res.data + setToken(userData.token) + this.$patch({ + ...userData, + }) + useRouteStore().addRoutes(userData.route_names) + return userData + } + return false + }, + }, +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot)) +} diff --git a/src/styles/style.scss b/src/styles/style.scss new file mode 100644 index 0000000..251dc82 --- /dev/null +++ b/src/styles/style.scss @@ -0,0 +1,20 @@ +$basicBlack: #000000; +$basicWhite: #ffffff; + +$primaryColor: #409eff; +$sideBarWidth: 210px; + +:root { + --basicBlack: #000000; + --basicWhite: #ffffff; + --primaryColor: #409eff; +} + +.list-body{ + margin: 10px 0; +} + +.dialog-form{ + max-width: 600px; + margin: 20px auto; +} diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..4266de0 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,13 @@ +const TokenKey = 'access_token' + +export function getToken () { + return localStorage.getItem(TokenKey) +} + +export function setToken (token) { + return localStorage.setItem(TokenKey, token) +} + +export function removeToken () { + return localStorage.removeItem(TokenKey) +} diff --git a/src/utils/common_options.js b/src/utils/common_options.js new file mode 100644 index 0000000..72b9e0f --- /dev/null +++ b/src/utils/common_options.js @@ -0,0 +1,4 @@ + + +export const ENABLE_STATUS = 1 +export const DISABLE_STATUS = 2 diff --git a/src/utils/file.js b/src/utils/file.js new file mode 100644 index 0000000..a4f07cd --- /dev/null +++ b/src/utils/file.js @@ -0,0 +1,34 @@ +export function get_suffix(filename) { + var pos = filename.lastIndexOf('.') + var suffix = '' + if (pos !== -1) { + suffix = filename.substring(pos) + } + return suffix +} + +export function random_string(len) { + len = len || 32 + var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' + var maxPos = chars.length + var pwd = '' + for (let i = 0; i < len; i++) { + pwd += chars.charAt(Math.floor(Math.random() * maxPos)) + } + return pwd +} + +export function random_filename(filename) { + var suffix = get_suffix(filename) + var time = new Date() + var time2 = new Date('2020/01/01') + return Math.ceil((time.getTime() - time2.getTime()) / 1000) + '_' + random_string(10) + suffix +} + +export function utf8_to_b64(str) { + return window.btoa(unescape(encodeURIComponent(str))) +} + +export function b64_to_utf8(str) { + return decodeURIComponent(escape(window.atob(str))) +} diff --git a/src/utils/pca.json b/src/utils/pca.json new file mode 100644 index 0000000..38cb2fb --- /dev/null +++ b/src/utils/pca.json @@ -0,0 +1,4272 @@ +{ + "北京市": { + "市辖区": [ + "东城区", + "西城区", + "朝阳区", + "丰台区", + "石景山区", + "海淀区", + "门头沟区", + "房山区", + "通州区", + "顺义区", + "昌平区", + "大兴区", + "怀柔区", + "平谷区", + "密云区", + "延庆区" + ] + }, + "天津市": { + "市辖区": [ + "和平区", + "河东区", + "河西区", + "南开区", + "河北区", + "红桥区", + "东丽区", + "西青区", + "津南区", + "北辰区", + "武清区", + "宝坻区", + "滨海新区", + "宁河区", + "静海区", + "蓟州区" + ] + }, + "河北省": { + "石家庄市": [ + "长安区", + "桥西区", + "新华区", + "井陉矿区", + "裕华区", + "藁城区", + "鹿泉区", + "栾城区", + "井陉县", + "正定县", + "行唐县", + "灵寿县", + "高邑县", + "深泽县", + "赞皇县", + "无极县", + "平山县", + "元氏县", + "赵县", + "石家庄高新技术产业开发区", + "石家庄循环化工园区", + "辛集市", + "晋州市", + "新乐市" + ], + "唐山市": [ + "路南区", + "路北区", + "古冶区", + "开平区", + "丰南区", + "丰润区", + "曹妃甸区", + "滦南县", + "乐亭县", + "迁西县", + "玉田县", + "唐山市芦台经济技术开发区", + "唐山市汉沽管理区", + "唐山高新技术产业开发区", + "河北唐山海港经济开发区", + "遵化市", + "迁安市", + "滦州市" + ], + "秦皇岛市": [ + "海港区", + "山海关区", + "北戴河区", + "抚宁区", + "青龙满族自治县", + "昌黎县", + "卢龙县", + "秦皇岛市经济技术开发区", + "北戴河新区" + ], + "邯郸市": [ + "邯山区", + "丛台区", + "复兴区", + "峰峰矿区", + "肥乡区", + "永年区", + "临漳县", + "成安县", + "大名县", + "涉县", + "磁县", + "邱县", + "鸡泽县", + "广平县", + "馆陶县", + "魏县", + "曲周县", + "邯郸经济技术开发区", + "邯郸冀南新区", + "武安市" + ], + "邢台市": [ + "桥东区", + "桥西区", + "邢台县", + "临城县", + "内丘县", + "柏乡县", + "隆尧县", + "任县", + "南和县", + "宁晋县", + "巨鹿县", + "新河县", + "广宗县", + "平乡县", + "威县", + "清河县", + "临西县", + "河北邢台经济开发区", + "南宫市", + "沙河市" + ], + "保定市": [ + "竞秀区", + "莲池区", + "满城区", + "清苑区", + "徐水区", + "涞水县", + "阜平县", + "定兴县", + "唐县", + "高阳县", + "容城县", + "涞源县", + "望都县", + "安新县", + "易县", + "曲阳县", + "蠡县", + "顺平县", + "博野县", + "雄县", + "保定高新技术产业开发区", + "保定白沟新城", + "涿州市", + "定州市", + "安国市", + "高碑店市" + ], + "张家口市": [ + "桥东区", + "桥西区", + "宣化区", + "下花园区", + "万全区", + "崇礼区", + "张北县", + "康保县", + "沽源县", + "尚义县", + "蔚县", + "阳原县", + "怀安县", + "怀来县", + "涿鹿县", + "赤城县", + "张家口市高新技术产业开发区", + "张家口市察北管理区", + "张家口市塞北管理区" + ], + "承德市": [ + "双桥区", + "双滦区", + "鹰手营子矿区", + "承德县", + "兴隆县", + "滦平县", + "隆化县", + "丰宁满族自治县", + "宽城满族自治县", + "围场满族蒙古族自治县", + "承德高新技术产业开发区", + "平泉市" + ], + "沧州市": [ + "新华区", + "运河区", + "沧县", + "青县", + "东光县", + "海兴县", + "盐山县", + "肃宁县", + "南皮县", + "吴桥县", + "献县", + "孟村回族自治县", + "河北沧州经济开发区", + "沧州高新技术产业开发区", + "沧州渤海新区", + "泊头市", + "任丘市", + "黄骅市", + "河间市" + ], + "廊坊市": [ + "安次区", + "广阳区", + "固安县", + "永清县", + "香河县", + "大城县", + "文安县", + "大厂回族自治县", + "廊坊经济技术开发区", + "霸州市", + "三河市" + ], + "衡水市": [ + "桃城区", + "冀州区", + "枣强县", + "武邑县", + "武强县", + "饶阳县", + "安平县", + "故城县", + "景县", + "阜城县", + "河北衡水高新技术产业开发区", + "衡水滨湖新区", + "深州市" + ] + }, + "山西省": { + "太原市": [ + "小店区", + "迎泽区", + "杏花岭区", + "尖草坪区", + "万柏林区", + "晋源区", + "清徐县", + "阳曲县", + "娄烦县", + "山西转型综合改革示范区", + "古交市" + ], + "大同市": [ + "新荣区", + "平城区", + "云冈区", + "云州区", + "阳高县", + "天镇县", + "广灵县", + "灵丘县", + "浑源县", + "左云县", + "山西大同经济开发区" + ], + "阳泉市": [ + "城区", + "矿区", + "郊区", + "平定县", + "盂县" + ], + "长治市": [ + "潞州区", + "上党区", + "屯留区", + "潞城区", + "襄垣县", + "平顺县", + "黎城县", + "壶关县", + "长子县", + "武乡县", + "沁县", + "沁源县", + "山西长治高新技术产业园区" + ], + "晋城市": [ + "城区", + "沁水县", + "阳城县", + "陵川县", + "泽州县", + "高平市" + ], + "朔州市": [ + "朔城区", + "平鲁区", + "山阴县", + "应县", + "右玉县", + "山西朔州经济开发区", + "怀仁市" + ], + "晋中市": [ + "榆次区", + "榆社县", + "左权县", + "和顺县", + "昔阳县", + "寿阳县", + "太谷县", + "祁县", + "平遥县", + "灵石县", + "介休市" + ], + "运城市": [ + "盐湖区", + "临猗县", + "万荣县", + "闻喜县", + "稷山县", + "新绛县", + "绛县", + "垣曲县", + "夏县", + "平陆县", + "芮城县", + "永济市", + "河津市" + ], + "忻州市": [ + "忻府区", + "定襄县", + "五台县", + "代县", + "繁峙县", + "宁武县", + "静乐县", + "神池县", + "五寨县", + "岢岚县", + "河曲县", + "保德县", + "偏关县", + "五台山风景名胜区", + "原平市" + ], + "临汾市": [ + "尧都区", + "曲沃县", + "翼城县", + "襄汾县", + "洪洞县", + "古县", + "安泽县", + "浮山县", + "吉县", + "乡宁县", + "大宁县", + "隰县", + "永和县", + "蒲县", + "汾西县", + "侯马市", + "霍州市" + ], + "吕梁市": [ + "离石区", + "文水县", + "交城县", + "兴县", + "临县", + "柳林县", + "石楼县", + "岚县", + "方山县", + "中阳县", + "交口县", + "孝义市", + "汾阳市" + ] + }, + "内蒙古自治区": { + "呼和浩特市": [ + "新城区", + "回民区", + "玉泉区", + "赛罕区", + "土默特左旗", + "托克托县", + "和林格尔县", + "清水河县", + "武川县", + "呼和浩特金海工业园区", + "呼和浩特经济技术开发区" + ], + "包头市": [ + "东河区", + "昆都仑区", + "青山区", + "石拐区", + "白云鄂博矿区", + "九原区", + "土默特右旗", + "固阳县", + "达尔罕茂明安联合旗", + "包头稀土高新技术产业开发区" + ], + "乌海市": [ + "海勃湾区", + "海南区", + "乌达区" + ], + "赤峰市": [ + "红山区", + "元宝山区", + "松山区", + "阿鲁科尔沁旗", + "巴林左旗", + "巴林右旗", + "林西县", + "克什克腾旗", + "翁牛特旗", + "喀喇沁旗", + "宁城县", + "敖汉旗" + ], + "通辽市": [ + "科尔沁区", + "科尔沁左翼中旗", + "科尔沁左翼后旗", + "开鲁县", + "库伦旗", + "奈曼旗", + "扎鲁特旗", + "通辽经济技术开发区", + "霍林郭勒市" + ], + "鄂尔多斯市": [ + "东胜区", + "康巴什区", + "达拉特旗", + "准格尔旗", + "鄂托克前旗", + "鄂托克旗", + "杭锦旗", + "乌审旗", + "伊金霍洛旗" + ], + "呼伦贝尔市": [ + "海拉尔区", + "扎赉诺尔区", + "阿荣旗", + "莫力达瓦达斡尔族自治旗", + "鄂伦春自治旗", + "鄂温克族自治旗", + "陈巴尔虎旗", + "新巴尔虎左旗", + "新巴尔虎右旗", + "满洲里市", + "牙克石市", + "扎兰屯市", + "额尔古纳市", + "根河市" + ], + "巴彦淖尔市": [ + "临河区", + "五原县", + "磴口县", + "乌拉特前旗", + "乌拉特中旗", + "乌拉特后旗", + "杭锦后旗" + ], + "乌兰察布市": [ + "集宁区", + "卓资县", + "化德县", + "商都县", + "兴和县", + "凉城县", + "察哈尔右翼前旗", + "察哈尔右翼中旗", + "察哈尔右翼后旗", + "四子王旗", + "丰镇市" + ], + "兴安盟": [ + "乌兰浩特市", + "阿尔山市", + "科尔沁右翼前旗", + "科尔沁右翼中旗", + "扎赉特旗", + "突泉县" + ], + "锡林郭勒盟": [ + "二连浩特市", + "锡林浩特市", + "阿巴嘎旗", + "苏尼特左旗", + "苏尼特右旗", + "东乌珠穆沁旗", + "西乌珠穆沁旗", + "太仆寺旗", + "镶黄旗", + "正镶白旗", + "正蓝旗", + "多伦县", + "乌拉盖管委会" + ], + "阿拉善盟": [ + "阿拉善左旗", + "阿拉善右旗", + "额济纳旗", + "内蒙古阿拉善经济开发区" + ] + }, + "辽宁省": { + "沈阳市": [ + "和平区", + "沈河区", + "大东区", + "皇姑区", + "铁西区", + "苏家屯区", + "浑南区", + "沈北新区", + "于洪区", + "辽中区", + "康平县", + "法库县", + "新民市" + ], + "大连市": [ + "中山区", + "西岗区", + "沙河口区", + "甘井子区", + "旅顺口区", + "金州区", + "普兰店区", + "长海县", + "瓦房店市", + "庄河市" + ], + "鞍山市": [ + "铁东区", + "铁西区", + "立山区", + "千山区", + "台安县", + "岫岩满族自治县", + "海城市" + ], + "抚顺市": [ + "新抚区", + "东洲区", + "望花区", + "顺城区", + "抚顺县", + "新宾满族自治县", + "清原满族自治县" + ], + "本溪市": [ + "平山区", + "溪湖区", + "明山区", + "南芬区", + "本溪满族自治县", + "桓仁满族自治县" + ], + "丹东市": [ + "元宝区", + "振兴区", + "振安区", + "宽甸满族自治县", + "东港市", + "凤城市" + ], + "锦州市": [ + "古塔区", + "凌河区", + "太和区", + "黑山县", + "义县", + "凌海市", + "北镇市" + ], + "营口市": [ + "站前区", + "西市区", + "鲅鱼圈区", + "老边区", + "盖州市", + "大石桥市" + ], + "阜新市": [ + "海州区", + "新邱区", + "太平区", + "清河门区", + "细河区", + "阜新蒙古族自治县", + "彰武县" + ], + "辽阳市": [ + "白塔区", + "文圣区", + "宏伟区", + "弓长岭区", + "太子河区", + "辽阳县", + "灯塔市" + ], + "盘锦市": [ + "双台子区", + "兴隆台区", + "大洼区", + "盘山县" + ], + "铁岭市": [ + "银州区", + "清河区", + "铁岭县", + "西丰县", + "昌图县", + "调兵山市", + "开原市" + ], + "朝阳市": [ + "双塔区", + "龙城区", + "朝阳县", + "建平县", + "喀喇沁左翼蒙古族自治县", + "北票市", + "凌源市" + ], + "葫芦岛市": [ + "连山区", + "龙港区", + "南票区", + "绥中县", + "建昌县", + "兴城市" + ] + }, + "吉林省": { + "长春市": [ + "南关区", + "宽城区", + "朝阳区", + "二道区", + "绿园区", + "双阳区", + "九台区", + "农安县", + "长春经济技术开发区", + "长春净月高新技术产业开发区", + "长春高新技术产业开发区", + "长春汽车经济技术开发区", + "榆树市", + "德惠市" + ], + "吉林市": [ + "昌邑区", + "龙潭区", + "船营区", + "丰满区", + "永吉县", + "吉林经济开发区", + "吉林高新技术产业开发区", + "吉林中国新加坡食品区", + "蛟河市", + "桦甸市", + "舒兰市", + "磐石市" + ], + "四平市": [ + "铁西区", + "铁东区", + "梨树县", + "伊通满族自治县", + "公主岭市", + "双辽市" + ], + "辽源市": [ + "龙山区", + "西安区", + "东丰县", + "东辽县" + ], + "通化市": [ + "东昌区", + "二道江区", + "通化县", + "辉南县", + "柳河县", + "梅河口市", + "集安市" + ], + "白山市": [ + "浑江区", + "江源区", + "抚松县", + "靖宇县", + "长白朝鲜族自治县", + "临江市" + ], + "松原市": [ + "宁江区", + "前郭尔罗斯蒙古族自治县", + "长岭县", + "乾安县", + "吉林松原经济开发区", + "扶余市" + ], + "白城市": [ + "洮北区", + "镇赉县", + "通榆县", + "吉林白城经济开发区", + "洮南市", + "大安市" + ], + "延边朝鲜族自治州": [ + "延吉市", + "图们市", + "敦化市", + "珲春市", + "龙井市", + "和龙市", + "汪清县", + "安图县" + ] + }, + "黑龙江省": { + "哈尔滨市": [ + "道里区", + "南岗区", + "道外区", + "平房区", + "松北区", + "香坊区", + "呼兰区", + "阿城区", + "双城区", + "依兰县", + "方正县", + "宾县", + "巴彦县", + "木兰县", + "通河县", + "延寿县", + "尚志市", + "五常市" + ], + "齐齐哈尔市": [ + "龙沙区", + "建华区", + "铁锋区", + "昂昂溪区", + "富拉尔基区", + "碾子山区", + "梅里斯达斡尔族区", + "龙江县", + "依安县", + "泰来县", + "甘南县", + "富裕县", + "克山县", + "克东县", + "拜泉县", + "讷河市" + ], + "鸡西市": [ + "鸡冠区", + "恒山区", + "滴道区", + "梨树区", + "城子河区", + "麻山区", + "鸡东县", + "虎林市", + "密山市" + ], + "鹤岗市": [ + "向阳区", + "工农区", + "南山区", + "兴安区", + "东山区", + "兴山区", + "萝北县", + "绥滨县" + ], + "双鸭山市": [ + "尖山区", + "岭东区", + "四方台区", + "宝山区", + "集贤县", + "友谊县", + "宝清县", + "饶河县" + ], + "大庆市": [ + "萨尔图区", + "龙凤区", + "让胡路区", + "红岗区", + "大同区", + "肇州县", + "肇源县", + "林甸县", + "杜尔伯特蒙古族自治县", + "大庆高新技术产业开发区" + ], + "伊春市": [ + "伊春区", + "南岔区", + "友好区", + "西林区", + "翠峦区", + "新青区", + "美溪区", + "金山屯区", + "五营区", + "乌马河区", + "汤旺河区", + "带岭区", + "乌伊岭区", + "红星区", + "上甘岭区", + "嘉荫县", + "铁力市" + ], + "佳木斯市": [ + "向阳区", + "前进区", + "东风区", + "郊区", + "桦南县", + "桦川县", + "汤原县", + "同江市", + "富锦市", + "抚远市" + ], + "七台河市": [ + "新兴区", + "桃山区", + "茄子河区", + "勃利县" + ], + "牡丹江市": [ + "东安区", + "阳明区", + "爱民区", + "西安区", + "林口县", + "牡丹江经济技术开发区", + "绥芬河市", + "海林市", + "宁安市", + "穆棱市", + "东宁市" + ], + "黑河市": [ + "爱辉区", + "嫩江县", + "逊克县", + "孙吴县", + "北安市", + "五大连池市" + ], + "绥化市": [ + "北林区", + "望奎县", + "兰西县", + "青冈县", + "庆安县", + "明水县", + "绥棱县", + "安达市", + "肇东市", + "海伦市" + ], + "大兴安岭地区": [ + "漠河市", + "呼玛县", + "塔河县", + "加格达奇区", + "松岭区", + "新林区", + "呼中区" + ] + }, + "上海市": { + "市辖区": [ + "黄浦区", + "徐汇区", + "长宁区", + "静安区", + "普陀区", + "虹口区", + "杨浦区", + "闵行区", + "宝山区", + "嘉定区", + "浦东新区", + "金山区", + "松江区", + "青浦区", + "奉贤区", + "崇明区" + ] + }, + "江苏省": { + "南京市": [ + "玄武区", + "秦淮区", + "建邺区", + "鼓楼区", + "浦口区", + "栖霞区", + "雨花台区", + "江宁区", + "六合区", + "溧水区", + "高淳区" + ], + "无锡市": [ + "锡山区", + "惠山区", + "滨湖区", + "梁溪区", + "新吴区", + "江阴市", + "宜兴市" + ], + "徐州市": [ + "鼓楼区", + "云龙区", + "贾汪区", + "泉山区", + "铜山区", + "丰县", + "沛县", + "睢宁县", + "徐州经济技术开发区", + "新沂市", + "邳州市" + ], + "常州市": [ + "天宁区", + "钟楼区", + "新北区", + "武进区", + "金坛区", + "溧阳市" + ], + "苏州市": [ + "虎丘区", + "吴中区", + "相城区", + "姑苏区", + "吴江区", + "苏州工业园区", + "常熟市", + "张家港市", + "昆山市", + "太仓市" + ], + "南通市": [ + "崇川区", + "港闸区", + "通州区", + "如东县", + "南通经济技术开发区", + "启东市", + "如皋市", + "海门市", + "海安市" + ], + "连云港市": [ + "连云区", + "海州区", + "赣榆区", + "东海县", + "灌云县", + "灌南县", + "连云港经济技术开发区", + "连云港高新技术产业开发区" + ], + "淮安市": [ + "淮安区", + "淮阴区", + "清江浦区", + "洪泽区", + "涟水县", + "盱眙县", + "金湖县", + "淮安经济技术开发区" + ], + "盐城市": [ + "亭湖区", + "盐都区", + "大丰区", + "响水县", + "滨海县", + "阜宁县", + "射阳县", + "建湖县", + "盐城经济技术开发区", + "东台市" + ], + "扬州市": [ + "广陵区", + "邗江区", + "江都区", + "宝应县", + "扬州经济技术开发区", + "仪征市", + "高邮市" + ], + "镇江市": [ + "京口区", + "润州区", + "丹徒区", + "镇江新区", + "丹阳市", + "扬中市", + "句容市" + ], + "泰州市": [ + "海陵区", + "高港区", + "姜堰区", + "泰州医药高新技术产业开发区", + "兴化市", + "靖江市", + "泰兴市" + ], + "宿迁市": [ + "宿城区", + "宿豫区", + "沭阳县", + "泗阳县", + "泗洪县", + "宿迁经济技术开发区" + ] + }, + "浙江省": { + "杭州市": [ + "上城区", + "下城区", + "江干区", + "拱墅区", + "西湖区", + "滨江区", + "萧山区", + "余杭区", + "富阳区", + "临安区", + "桐庐县", + "淳安县", + "建德市" + ], + "宁波市": [ + "海曙区", + "江北区", + "北仑区", + "镇海区", + "鄞州区", + "奉化区", + "象山县", + "宁海县", + "余姚市", + "慈溪市" + ], + "温州市": [ + "鹿城区", + "龙湾区", + "瓯海区", + "洞头区", + "永嘉县", + "平阳县", + "苍南县", + "文成县", + "泰顺县", + "温州经济技术开发区", + "瑞安市", + "乐清市" + ], + "嘉兴市": [ + "南湖区", + "秀洲区", + "嘉善县", + "海盐县", + "海宁市", + "平湖市", + "桐乡市" + ], + "湖州市": [ + "吴兴区", + "南浔区", + "德清县", + "长兴县", + "安吉县" + ], + "绍兴市": [ + "越城区", + "柯桥区", + "上虞区", + "新昌县", + "诸暨市", + "嵊州市" + ], + "金华市": [ + "婺城区", + "金东区", + "武义县", + "浦江县", + "磐安县", + "兰溪市", + "义乌市", + "东阳市", + "永康市" + ], + "衢州市": [ + "柯城区", + "衢江区", + "常山县", + "开化县", + "龙游县", + "江山市" + ], + "舟山市": [ + "定海区", + "普陀区", + "岱山县", + "嵊泗县" + ], + "台州市": [ + "椒江区", + "黄岩区", + "路桥区", + "三门县", + "天台县", + "仙居县", + "温岭市", + "临海市", + "玉环市" + ], + "丽水市": [ + "莲都区", + "青田县", + "缙云县", + "遂昌县", + "松阳县", + "云和县", + "庆元县", + "景宁畲族自治县", + "龙泉市" + ] + }, + "安徽省": { + "合肥市": [ + "瑶海区", + "庐阳区", + "蜀山区", + "包河区", + "长丰县", + "肥东县", + "肥西县", + "庐江县", + "合肥高新技术产业开发区", + "合肥经济技术开发区", + "合肥新站高新技术产业开发区", + "巢湖市" + ], + "芜湖市": [ + "镜湖区", + "弋江区", + "鸠江区", + "三山区", + "芜湖县", + "繁昌县", + "南陵县", + "无为县", + "芜湖经济技术开发区", + "安徽芜湖长江大桥经济开发区" + ], + "蚌埠市": [ + "龙子湖区", + "蚌山区", + "禹会区", + "淮上区", + "怀远县", + "五河县", + "固镇县", + "蚌埠市高新技术开发区", + "蚌埠市经济开发区" + ], + "淮南市": [ + "大通区", + "田家庵区", + "谢家集区", + "八公山区", + "潘集区", + "凤台县", + "寿县" + ], + "马鞍山市": [ + "花山区", + "雨山区", + "博望区", + "当涂县", + "含山县", + "和县" + ], + "淮北市": [ + "杜集区", + "相山区", + "烈山区", + "濉溪县" + ], + "铜陵市": [ + "铜官区", + "义安区", + "郊区", + "枞阳县" + ], + "安庆市": [ + "迎江区", + "大观区", + "宜秀区", + "怀宁县", + "太湖县", + "宿松县", + "望江县", + "岳西县", + "安徽安庆经济开发区", + "桐城市", + "潜山市" + ], + "黄山市": [ + "屯溪区", + "黄山区", + "徽州区", + "歙县", + "休宁县", + "黟县", + "祁门县" + ], + "滁州市": [ + "琅琊区", + "南谯区", + "来安县", + "全椒县", + "定远县", + "凤阳县", + "苏滁现代产业园", + "滁州经济技术开发区", + "天长市", + "明光市" + ], + "阜阳市": [ + "颍州区", + "颍东区", + "颍泉区", + "临泉县", + "太和县", + "阜南县", + "颍上县", + "阜阳合肥现代产业园区", + "阜阳经济技术开发区", + "界首市" + ], + "宿州市": [ + "埇桥区", + "砀山县", + "萧县", + "灵璧县", + "泗县", + "宿州马鞍山现代产业园区", + "宿州经济技术开发区" + ], + "六安市": [ + "金安区", + "裕安区", + "叶集区", + "霍邱县", + "舒城县", + "金寨县", + "霍山县" + ], + "亳州市": [ + "谯城区", + "涡阳县", + "蒙城县", + "利辛县" + ], + "池州市": [ + "贵池区", + "东至县", + "石台县", + "青阳县" + ], + "宣城市": [ + "宣州区", + "郎溪县", + "广德县", + "泾县", + "绩溪县", + "旌德县", + "宣城市经济开发区", + "宁国市" + ] + }, + "福建省": { + "福州市": [ + "鼓楼区", + "台江区", + "仓山区", + "马尾区", + "晋安区", + "长乐区", + "闽侯县", + "连江县", + "罗源县", + "闽清县", + "永泰县", + "平潭县", + "福清市" + ], + "厦门市": [ + "思明区", + "海沧区", + "湖里区", + "集美区", + "同安区", + "翔安区" + ], + "莆田市": [ + "城厢区", + "涵江区", + "荔城区", + "秀屿区", + "仙游县" + ], + "三明市": [ + "梅列区", + "三元区", + "明溪县", + "清流县", + "宁化县", + "大田县", + "尤溪县", + "沙县", + "将乐县", + "泰宁县", + "建宁县", + "永安市" + ], + "泉州市": [ + "鲤城区", + "丰泽区", + "洛江区", + "泉港区", + "惠安县", + "安溪县", + "永春县", + "德化县", + "金门县", + "石狮市", + "晋江市", + "南安市" + ], + "漳州市": [ + "芗城区", + "龙文区", + "云霄县", + "漳浦县", + "诏安县", + "长泰县", + "东山县", + "南靖县", + "平和县", + "华安县", + "龙海市" + ], + "南平市": [ + "延平区", + "建阳区", + "顺昌县", + "浦城县", + "光泽县", + "松溪县", + "政和县", + "邵武市", + "武夷山市", + "建瓯市" + ], + "龙岩市": [ + "新罗区", + "永定区", + "长汀县", + "上杭县", + "武平县", + "连城县", + "漳平市" + ], + "宁德市": [ + "蕉城区", + "霞浦县", + "古田县", + "屏南县", + "寿宁县", + "周宁县", + "柘荣县", + "福安市", + "福鼎市" + ] + }, + "江西省": { + "南昌市": [ + "东湖区", + "西湖区", + "青云谱区", + "湾里区", + "青山湖区", + "新建区", + "南昌县", + "安义县", + "进贤县" + ], + "景德镇市": [ + "昌江区", + "珠山区", + "浮梁县", + "乐平市" + ], + "萍乡市": [ + "安源区", + "湘东区", + "莲花县", + "上栗县", + "芦溪县" + ], + "九江市": [ + "濂溪区", + "浔阳区", + "柴桑区", + "武宁县", + "修水县", + "永修县", + "德安县", + "都昌县", + "湖口县", + "彭泽县", + "瑞昌市", + "共青城市", + "庐山市" + ], + "新余市": [ + "渝水区", + "分宜县" + ], + "鹰潭市": [ + "月湖区", + "余江区", + "贵溪市" + ], + "赣州市": [ + "章贡区", + "南康区", + "赣县区", + "信丰县", + "大余县", + "上犹县", + "崇义县", + "安远县", + "龙南县", + "定南县", + "全南县", + "宁都县", + "于都县", + "兴国县", + "会昌县", + "寻乌县", + "石城县", + "瑞金市" + ], + "吉安市": [ + "吉州区", + "青原区", + "吉安县", + "吉水县", + "峡江县", + "新干县", + "永丰县", + "泰和县", + "遂川县", + "万安县", + "安福县", + "永新县", + "井冈山市" + ], + "宜春市": [ + "袁州区", + "奉新县", + "万载县", + "上高县", + "宜丰县", + "靖安县", + "铜鼓县", + "丰城市", + "樟树市", + "高安市" + ], + "抚州市": [ + "临川区", + "东乡区", + "南城县", + "黎川县", + "南丰县", + "崇仁县", + "乐安县", + "宜黄县", + "金溪县", + "资溪县", + "广昌县" + ], + "上饶市": [ + "信州区", + "广丰区", + "上饶县", + "玉山县", + "铅山县", + "横峰县", + "弋阳县", + "余干县", + "鄱阳县", + "万年县", + "婺源县", + "德兴市" + ] + }, + "山东省": { + "济南市": [ + "历下区", + "市中区", + "槐荫区", + "天桥区", + "历城区", + "长清区", + "章丘区", + "济阳区", + "平阴县", + "商河县", + "济南高新技术产业开发区" + ], + "青岛市": [ + "市南区", + "市北区", + "黄岛区", + "崂山区", + "李沧区", + "城阳区", + "即墨区", + "青岛高新技术产业开发区", + "胶州市", + "平度市", + "莱西市" + ], + "淄博市": [ + "淄川区", + "张店区", + "博山区", + "临淄区", + "周村区", + "桓台县", + "高青县", + "沂源县" + ], + "枣庄市": [ + "市中区", + "薛城区", + "峄城区", + "台儿庄区", + "山亭区", + "滕州市" + ], + "东营市": [ + "东营区", + "河口区", + "垦利区", + "利津县", + "广饶县", + "东营经济技术开发区", + "东营港经济开发区" + ], + "烟台市": [ + "芝罘区", + "福山区", + "牟平区", + "莱山区", + "长岛县", + "烟台高新技术产业开发区", + "烟台经济技术开发区", + "龙口市", + "莱阳市", + "莱州市", + "蓬莱市", + "招远市", + "栖霞市", + "海阳市" + ], + "潍坊市": [ + "潍城区", + "寒亭区", + "坊子区", + "奎文区", + "临朐县", + "昌乐县", + "潍坊滨海经济技术开发区", + "青州市", + "诸城市", + "寿光市", + "安丘市", + "高密市", + "昌邑市" + ], + "济宁市": [ + "任城区", + "兖州区", + "微山县", + "鱼台县", + "金乡县", + "嘉祥县", + "汶上县", + "泗水县", + "梁山县", + "济宁高新技术产业开发区", + "曲阜市", + "邹城市" + ], + "泰安市": [ + "泰山区", + "岱岳区", + "宁阳县", + "东平县", + "新泰市", + "肥城市" + ], + "威海市": [ + "环翠区", + "文登区", + "威海火炬高技术产业开发区", + "威海经济技术开发区", + "威海临港经济技术开发区", + "荣成市", + "乳山市" + ], + "日照市": [ + "东港区", + "岚山区", + "五莲县", + "莒县", + "日照经济技术开发区" + ], + "莱芜市": [ + "莱城区", + "钢城区" + ], + "临沂市": [ + "兰山区", + "罗庄区", + "河东区", + "沂南县", + "郯城县", + "沂水县", + "兰陵县", + "费县", + "平邑县", + "莒南县", + "蒙阴县", + "临沭县", + "临沂高新技术产业开发区", + "临沂经济技术开发区", + "临沂临港经济开发区" + ], + "德州市": [ + "德城区", + "陵城区", + "宁津县", + "庆云县", + "临邑县", + "齐河县", + "平原县", + "夏津县", + "武城县", + "德州经济技术开发区", + "德州运河经济开发区", + "乐陵市", + "禹城市" + ], + "聊城市": [ + "东昌府区", + "阳谷县", + "莘县", + "茌平县", + "东阿县", + "冠县", + "高唐县", + "临清市" + ], + "滨州市": [ + "滨城区", + "沾化区", + "惠民县", + "阳信县", + "无棣县", + "博兴县", + "邹平市" + ], + "菏泽市": [ + "牡丹区", + "定陶区", + "曹县", + "单县", + "成武县", + "巨野县", + "郓城县", + "鄄城县", + "东明县", + "菏泽经济技术开发区", + "菏泽高新技术开发区" + ] + }, + "河南省": { + "郑州市": [ + "中原区", + "二七区", + "管城回族区", + "金水区", + "上街区", + "惠济区", + "中牟县", + "郑州经济技术开发区", + "郑州高新技术产业开发区", + "郑州航空港经济综合实验区", + "巩义市", + "荥阳市", + "新密市", + "新郑市", + "登封市" + ], + "开封市": [ + "龙亭区", + "顺河回族区", + "鼓楼区", + "禹王台区", + "祥符区", + "杞县", + "通许县", + "尉氏县", + "兰考县" + ], + "洛阳市": [ + "老城区", + "西工区", + "瀍河回族区", + "涧西区", + "吉利区", + "洛龙区", + "孟津县", + "新安县", + "栾川县", + "嵩县", + "汝阳县", + "宜阳县", + "洛宁县", + "伊川县", + "洛阳高新技术产业开发区", + "偃师市" + ], + "平顶山市": [ + "新华区", + "卫东区", + "石龙区", + "湛河区", + "宝丰县", + "叶县", + "鲁山县", + "郏县", + "平顶山高新技术产业开发区", + "平顶山市新城区", + "舞钢市", + "汝州市" + ], + "安阳市": [ + "文峰区", + "北关区", + "殷都区", + "龙安区", + "安阳县", + "汤阴县", + "滑县", + "内黄县", + "安阳高新技术产业开发区", + "林州市" + ], + "鹤壁市": [ + "鹤山区", + "山城区", + "淇滨区", + "浚县", + "淇县", + "鹤壁经济技术开发区" + ], + "新乡市": [ + "红旗区", + "卫滨区", + "凤泉区", + "牧野区", + "新乡县", + "获嘉县", + "原阳县", + "延津县", + "封丘县", + "长垣县", + "新乡高新技术产业开发区", + "新乡经济技术开发区", + "新乡市平原城乡一体化示范区", + "卫辉市", + "辉县市" + ], + "焦作市": [ + "解放区", + "中站区", + "马村区", + "山阳区", + "修武县", + "博爱县", + "武陟县", + "温县", + "焦作城乡一体化示范区", + "沁阳市", + "孟州市" + ], + "濮阳市": [ + "华龙区", + "清丰县", + "南乐县", + "范县", + "台前县", + "濮阳县", + "河南濮阳工业园区", + "濮阳经济技术开发区" + ], + "许昌市": [ + "魏都区", + "建安区", + "鄢陵县", + "襄城县", + "许昌经济技术开发区", + "禹州市", + "长葛市" + ], + "漯河市": [ + "源汇区", + "郾城区", + "召陵区", + "舞阳县", + "临颍县", + "漯河经济技术开发区" + ], + "三门峡市": [ + "湖滨区", + "陕州区", + "渑池县", + "卢氏县", + "河南三门峡经济开发区", + "义马市", + "灵宝市" + ], + "南阳市": [ + "宛城区", + "卧龙区", + "南召县", + "方城县", + "西峡县", + "镇平县", + "内乡县", + "淅川县", + "社旗县", + "唐河县", + "新野县", + "桐柏县", + "南阳高新技术产业开发区", + "南阳市城乡一体化示范区", + "邓州市" + ], + "商丘市": [ + "梁园区", + "睢阳区", + "民权县", + "睢县", + "宁陵县", + "柘城县", + "虞城县", + "夏邑县", + "豫东综合物流产业聚集区", + "河南商丘经济开发区", + "永城市" + ], + "信阳市": [ + "浉河区", + "平桥区", + "罗山县", + "光山县", + "新县", + "商城县", + "固始县", + "潢川县", + "淮滨县", + "息县", + "信阳高新技术产业开发区" + ], + "周口市": [ + "川汇区", + "扶沟县", + "西华县", + "商水县", + "沈丘县", + "郸城县", + "淮阳县", + "太康县", + "鹿邑县", + "河南周口经济开发区", + "项城市" + ], + "驻马店市": [ + "驿城区", + "西平县", + "上蔡县", + "平舆县", + "正阳县", + "确山县", + "泌阳县", + "汝南县", + "遂平县", + "新蔡县", + "河南驻马店经济开发区" + ], + "省直辖县级行政区划": [ + "济源市" + ] + }, + "湖北省": { + "武汉市": [ + "江岸区", + "江汉区", + "硚口区", + "汉阳区", + "武昌区", + "青山区", + "洪山区", + "东西湖区", + "汉南区", + "蔡甸区", + "江夏区", + "黄陂区", + "新洲区" + ], + "黄石市": [ + "黄石港区", + "西塞山区", + "下陆区", + "铁山区", + "阳新县", + "大冶市" + ], + "十堰市": [ + "茅箭区", + "张湾区", + "郧阳区", + "郧西县", + "竹山县", + "竹溪县", + "房县", + "丹江口市" + ], + "宜昌市": [ + "西陵区", + "伍家岗区", + "点军区", + "猇亭区", + "夷陵区", + "远安县", + "兴山县", + "秭归县", + "长阳土家族自治县", + "五峰土家族自治县", + "宜都市", + "当阳市", + "枝江市" + ], + "襄阳市": [ + "襄城区", + "樊城区", + "襄州区", + "南漳县", + "谷城县", + "保康县", + "老河口市", + "枣阳市", + "宜城市" + ], + "鄂州市": [ + "梁子湖区", + "华容区", + "鄂城区" + ], + "荆门市": [ + "东宝区", + "掇刀区", + "沙洋县", + "钟祥市", + "京山市" + ], + "孝感市": [ + "孝南区", + "孝昌县", + "大悟县", + "云梦县", + "应城市", + "安陆市", + "汉川市" + ], + "荆州市": [ + "沙市区", + "荆州区", + "公安县", + "监利县", + "江陵县", + "荆州经济技术开发区", + "石首市", + "洪湖市", + "松滋市" + ], + "黄冈市": [ + "黄州区", + "团风县", + "红安县", + "罗田县", + "英山县", + "浠水县", + "蕲春县", + "黄梅县", + "龙感湖管理区", + "麻城市", + "武穴市" + ], + "咸宁市": [ + "咸安区", + "嘉鱼县", + "通城县", + "崇阳县", + "通山县", + "赤壁市" + ], + "随州市": [ + "曾都区", + "随县", + "广水市" + ], + "恩施土家族苗族自治州": [ + "恩施市", + "利川市", + "建始县", + "巴东县", + "宣恩县", + "咸丰县", + "来凤县", + "鹤峰县" + ], + "省直辖县级行政区划": [ + "仙桃市", + "潜江市", + "天门市", + "神农架林区" + ] + }, + "湖南省": { + "长沙市": [ + "芙蓉区", + "天心区", + "岳麓区", + "开福区", + "雨花区", + "望城区", + "长沙县", + "浏阳市", + "宁乡市" + ], + "株洲市": [ + "荷塘区", + "芦淞区", + "石峰区", + "天元区", + "渌口区", + "攸县", + "茶陵县", + "炎陵县", + "云龙示范区", + "醴陵市" + ], + "湘潭市": [ + "雨湖区", + "岳塘区", + "湘潭县", + "湖南湘潭高新技术产业园区", + "湘潭昭山示范区", + "湘潭九华示范区", + "湘乡市", + "韶山市" + ], + "衡阳市": [ + "珠晖区", + "雁峰区", + "石鼓区", + "蒸湘区", + "南岳区", + "衡阳县", + "衡南县", + "衡山县", + "衡东县", + "祁东县", + "衡阳综合保税区", + "湖南衡阳高新技术产业园区", + "湖南衡阳松木经济开发区", + "耒阳市", + "常宁市" + ], + "邵阳市": [ + "双清区", + "大祥区", + "北塔区", + "邵东县", + "新邵县", + "邵阳县", + "隆回县", + "洞口县", + "绥宁县", + "新宁县", + "城步苗族自治县", + "武冈市" + ], + "岳阳市": [ + "岳阳楼区", + "云溪区", + "君山区", + "岳阳县", + "华容县", + "湘阴县", + "平江县", + "岳阳市屈原管理区", + "汨罗市", + "临湘市" + ], + "常德市": [ + "武陵区", + "鼎城区", + "安乡县", + "汉寿县", + "澧县", + "临澧县", + "桃源县", + "石门县", + "常德市西洞庭管理区", + "津市市" + ], + "张家界市": [ + "永定区", + "武陵源区", + "慈利县", + "桑植县" + ], + "益阳市": [ + "资阳区", + "赫山区", + "南县", + "桃江县", + "安化县", + "益阳市大通湖管理区", + "湖南益阳高新技术产业园区", + "沅江市" + ], + "郴州市": [ + "北湖区", + "苏仙区", + "桂阳县", + "宜章县", + "永兴县", + "嘉禾县", + "临武县", + "汝城县", + "桂东县", + "安仁县", + "资兴市" + ], + "永州市": [ + "零陵区", + "冷水滩区", + "祁阳县", + "东安县", + "双牌县", + "道县", + "江永县", + "宁远县", + "蓝山县", + "新田县", + "江华瑶族自治县", + "永州经济技术开发区", + "永州市金洞管理区", + "永州市回龙圩管理区" + ], + "怀化市": [ + "鹤城区", + "中方县", + "沅陵县", + "辰溪县", + "溆浦县", + "会同县", + "麻阳苗族自治县", + "新晃侗族自治县", + "芷江侗族自治县", + "靖州苗族侗族自治县", + "通道侗族自治县", + "怀化市洪江管理区", + "洪江市" + ], + "娄底市": [ + "娄星区", + "双峰县", + "新化县", + "冷水江市", + "涟源市" + ], + "湘西土家族苗族自治州": [ + "吉首市", + "泸溪县", + "凤凰县", + "花垣县", + "保靖县", + "古丈县", + "永顺县", + "龙山县", + "湖南吉首经济开发区", + "湖南永顺经济开发区" + ] + }, + "广东省": { + "广州市": [ + "荔湾区", + "越秀区", + "海珠区", + "天河区", + "白云区", + "黄埔区", + "番禺区", + "花都区", + "南沙区", + "从化区", + "增城区" + ], + "韶关市": [ + "武江区", + "浈江区", + "曲江区", + "始兴县", + "仁化县", + "翁源县", + "乳源瑶族自治县", + "新丰县", + "乐昌市", + "南雄市" + ], + "深圳市": [ + "罗湖区", + "福田区", + "南山区", + "宝安区", + "龙岗区", + "盐田区", + "龙华区", + "坪山区", + "光明区" + ], + "珠海市": [ + "香洲区", + "斗门区", + "金湾区" + ], + "汕头市": [ + "龙湖区", + "金平区", + "濠江区", + "潮阳区", + "潮南区", + "澄海区", + "南澳县" + ], + "佛山市": [ + "禅城区", + "南海区", + "顺德区", + "三水区", + "高明区" + ], + "江门市": [ + "蓬江区", + "江海区", + "新会区", + "台山市", + "开平市", + "鹤山市", + "恩平市" + ], + "湛江市": [ + "赤坎区", + "霞山区", + "坡头区", + "麻章区", + "遂溪县", + "徐闻县", + "廉江市", + "雷州市", + "吴川市" + ], + "茂名市": [ + "茂南区", + "电白区", + "高州市", + "化州市", + "信宜市" + ], + "肇庆市": [ + "端州区", + "鼎湖区", + "高要区", + "广宁县", + "怀集县", + "封开县", + "德庆县", + "四会市" + ], + "惠州市": [ + "惠城区", + "惠阳区", + "博罗县", + "惠东县", + "龙门县" + ], + "梅州市": [ + "梅江区", + "梅县区", + "大埔县", + "丰顺县", + "五华县", + "平远县", + "蕉岭县", + "兴宁市" + ], + "汕尾市": [ + "城区", + "海丰县", + "陆河县", + "陆丰市" + ], + "河源市": [ + "源城区", + "紫金县", + "龙川县", + "连平县", + "和平县", + "东源县" + ], + "阳江市": [ + "江城区", + "阳东区", + "阳西县", + "阳春市" + ], + "清远市": [ + "清城区", + "清新区", + "佛冈县", + "阳山县", + "连山壮族瑶族自治县", + "连南瑶族自治县", + "英德市", + "连州市" + ], + "东莞市": [ + "东城街道", + "南城街道", + "万江街道", + "莞城街道", + "石碣镇", + "石龙镇", + "茶山镇", + "石排镇", + "企石镇", + "横沥镇", + "桥头镇", + "谢岗镇", + "东坑镇", + "常平镇", + "寮步镇", + "樟木头镇", + "大朗镇", + "黄江镇", + "清溪镇", + "塘厦镇", + "凤岗镇", + "大岭山镇", + "长安镇", + "虎门镇", + "厚街镇", + "沙田镇", + "道滘镇", + "洪梅镇", + "麻涌镇", + "望牛墩镇", + "中堂镇", + "高埗镇", + "松山湖管委会", + "东莞港", + "东莞生态园" + ], + "中山市": [ + "石岐区街道", + "东区街道", + "火炬开发区街道", + "西区街道", + "南区街道", + "五桂山街道", + "小榄镇", + "黄圃镇", + "民众镇", + "东凤镇", + "东升镇", + "古镇镇", + "沙溪镇", + "坦洲镇", + "港口镇", + "三角镇", + "横栏镇", + "南头镇", + "阜沙镇", + "南朗镇", + "三乡镇", + "板芙镇", + "大涌镇", + "神湾镇" + ], + "潮州市": [ + "湘桥区", + "潮安区", + "饶平县" + ], + "揭阳市": [ + "榕城区", + "揭东区", + "揭西县", + "惠来县", + "普宁市" + ], + "云浮市": [ + "云城区", + "云安区", + "新兴县", + "郁南县", + "罗定市" + ] + }, + "广西壮族自治区": { + "南宁市": [ + "兴宁区", + "青秀区", + "江南区", + "西乡塘区", + "良庆区", + "邕宁区", + "武鸣区", + "隆安县", + "马山县", + "上林县", + "宾阳县", + "横县" + ], + "柳州市": [ + "城中区", + "鱼峰区", + "柳南区", + "柳北区", + "柳江区", + "柳城县", + "鹿寨县", + "融安县", + "融水苗族自治县", + "三江侗族自治县" + ], + "桂林市": [ + "秀峰区", + "叠彩区", + "象山区", + "七星区", + "雁山区", + "临桂区", + "阳朔县", + "灵川县", + "全州县", + "兴安县", + "永福县", + "灌阳县", + "龙胜各族自治县", + "资源县", + "平乐县", + "恭城瑶族自治县", + "荔浦市" + ], + "梧州市": [ + "万秀区", + "长洲区", + "龙圩区", + "苍梧县", + "藤县", + "蒙山县", + "岑溪市" + ], + "北海市": [ + "海城区", + "银海区", + "铁山港区", + "合浦县" + ], + "防城港市": [ + "港口区", + "防城区", + "上思县", + "东兴市" + ], + "钦州市": [ + "钦南区", + "钦北区", + "灵山县", + "浦北县" + ], + "贵港市": [ + "港北区", + "港南区", + "覃塘区", + "平南县", + "桂平市" + ], + "玉林市": [ + "玉州区", + "福绵区", + "容县", + "陆川县", + "博白县", + "兴业县", + "北流市" + ], + "百色市": [ + "右江区", + "田阳县", + "田东县", + "平果县", + "德保县", + "那坡县", + "凌云县", + "乐业县", + "田林县", + "西林县", + "隆林各族自治县", + "靖西市" + ], + "贺州市": [ + "八步区", + "平桂区", + "昭平县", + "钟山县", + "富川瑶族自治县" + ], + "河池市": [ + "金城江区", + "宜州区", + "南丹县", + "天峨县", + "凤山县", + "东兰县", + "罗城仫佬族自治县", + "环江毛南族自治县", + "巴马瑶族自治县", + "都安瑶族自治县", + "大化瑶族自治县" + ], + "来宾市": [ + "兴宾区", + "忻城县", + "象州县", + "武宣县", + "金秀瑶族自治县", + "合山市" + ], + "崇左市": [ + "江州区", + "扶绥县", + "宁明县", + "龙州县", + "大新县", + "天等县", + "凭祥市" + ] + }, + "海南省": { + "海口市": [ + "秀英区", + "龙华区", + "琼山区", + "美兰区" + ], + "三亚市": [ + "海棠区", + "吉阳区", + "天涯区", + "崖州区" + ], + "三沙市": [ + "西沙群岛", + "南沙群岛", + "中沙群岛的岛礁及其海域" + ], + "儋州市": [ + "那大镇", + "和庆镇", + "南丰镇", + "大成镇", + "雅星镇", + "兰洋镇", + "光村镇", + "木棠镇", + "海头镇", + "峨蔓镇", + "王五镇", + "白马井镇", + "中和镇", + "排浦镇", + "东成镇", + "新州镇", + "洋浦经济开发区", + "华南热作学院" + ], + "省直辖县级行政区划": [ + "五指山市", + "琼海市", + "文昌市", + "万宁市", + "东方市", + "定安县", + "屯昌县", + "澄迈县", + "临高县", + "白沙黎族自治县", + "昌江黎族自治县", + "乐东黎族自治县", + "陵水黎族自治县", + "保亭黎族苗族自治县", + "琼中黎族苗族自治县" + ] + }, + "重庆市": { + "市辖区": [ + "万州区", + "涪陵区", + "渝中区", + "大渡口区", + "江北区", + "沙坪坝区", + "九龙坡区", + "南岸区", + "北碚区", + "綦江区", + "大足区", + "渝北区", + "巴南区", + "黔江区", + "长寿区", + "江津区", + "合川区", + "永川区", + "南川区", + "璧山区", + "铜梁区", + "潼南区", + "荣昌区", + "开州区", + "梁平区", + "武隆区" + ], + "县": [ + "城口县", + "丰都县", + "垫江县", + "忠县", + "云阳县", + "奉节县", + "巫山县", + "巫溪县", + "石柱土家族自治县", + "秀山土家族苗族自治县", + "酉阳土家族苗族自治县", + "彭水苗族土家族自治县" + ] + }, + "四川省": { + "成都市": [ + "锦江区", + "青羊区", + "金牛区", + "武侯区", + "成华区", + "龙泉驿区", + "青白江区", + "新都区", + "温江区", + "双流区", + "郫都区", + "金堂县", + "大邑县", + "蒲江县", + "新津县", + "都江堰市", + "彭州市", + "邛崃市", + "崇州市", + "简阳市" + ], + "自贡市": [ + "自流井区", + "贡井区", + "大安区", + "沿滩区", + "荣县", + "富顺县" + ], + "攀枝花市": [ + "东区", + "西区", + "仁和区", + "米易县", + "盐边县" + ], + "泸州市": [ + "江阳区", + "纳溪区", + "龙马潭区", + "泸县", + "合江县", + "叙永县", + "古蔺县" + ], + "德阳市": [ + "旌阳区", + "罗江区", + "中江县", + "广汉市", + "什邡市", + "绵竹市" + ], + "绵阳市": [ + "涪城区", + "游仙区", + "安州区", + "三台县", + "盐亭县", + "梓潼县", + "北川羌族自治县", + "平武县", + "江油市" + ], + "广元市": [ + "利州区", + "昭化区", + "朝天区", + "旺苍县", + "青川县", + "剑阁县", + "苍溪县" + ], + "遂宁市": [ + "船山区", + "安居区", + "蓬溪县", + "射洪县", + "大英县" + ], + "内江市": [ + "市中区", + "东兴区", + "威远县", + "资中县", + "内江经济开发区", + "隆昌市" + ], + "乐山市": [ + "市中区", + "沙湾区", + "五通桥区", + "金口河区", + "犍为县", + "井研县", + "夹江县", + "沐川县", + "峨边彝族自治县", + "马边彝族自治县", + "峨眉山市" + ], + "南充市": [ + "顺庆区", + "高坪区", + "嘉陵区", + "南部县", + "营山县", + "蓬安县", + "仪陇县", + "西充县", + "阆中市" + ], + "眉山市": [ + "东坡区", + "彭山区", + "仁寿县", + "洪雅县", + "丹棱县", + "青神县" + ], + "宜宾市": [ + "翠屏区", + "南溪区", + "叙州区", + "江安县", + "长宁县", + "高县", + "珙县", + "筠连县", + "兴文县", + "屏山县" + ], + "广安市": [ + "广安区", + "前锋区", + "岳池县", + "武胜县", + "邻水县", + "华蓥市" + ], + "达州市": [ + "通川区", + "达川区", + "宣汉县", + "开江县", + "大竹县", + "渠县", + "达州经济开发区", + "万源市" + ], + "雅安市": [ + "雨城区", + "名山区", + "荥经县", + "汉源县", + "石棉县", + "天全县", + "芦山县", + "宝兴县" + ], + "巴中市": [ + "巴州区", + "恩阳区", + "通江县", + "南江县", + "平昌县", + "巴中经济开发区" + ], + "资阳市": [ + "雁江区", + "安岳县", + "乐至县" + ], + "阿坝藏族羌族自治州": [ + "马尔康市", + "汶川县", + "理县", + "茂县", + "松潘县", + "九寨沟县", + "金川县", + "小金县", + "黑水县", + "壤塘县", + "阿坝县", + "若尔盖县", + "红原县" + ], + "甘孜藏族自治州": [ + "康定市", + "泸定县", + "丹巴县", + "九龙县", + "雅江县", + "道孚县", + "炉霍县", + "甘孜县", + "新龙县", + "德格县", + "白玉县", + "石渠县", + "色达县", + "理塘县", + "巴塘县", + "乡城县", + "稻城县", + "得荣县" + ], + "凉山彝族自治州": [ + "西昌市", + "木里藏族自治县", + "盐源县", + "德昌县", + "会理县", + "会东县", + "宁南县", + "普格县", + "布拖县", + "金阳县", + "昭觉县", + "喜德县", + "冕宁县", + "越西县", + "甘洛县", + "美姑县", + "雷波县" + ] + }, + "贵州省": { + "贵阳市": [ + "南明区", + "云岩区", + "花溪区", + "乌当区", + "白云区", + "观山湖区", + "开阳县", + "息烽县", + "修文县", + "清镇市" + ], + "六盘水市": [ + "钟山区", + "六枝特区", + "水城县", + "盘州市" + ], + "遵义市": [ + "红花岗区", + "汇川区", + "播州区", + "桐梓县", + "绥阳县", + "正安县", + "道真仡佬族苗族自治县", + "务川仡佬族苗族自治县", + "凤冈县", + "湄潭县", + "余庆县", + "习水县", + "赤水市", + "仁怀市" + ], + "安顺市": [ + "西秀区", + "平坝区", + "普定县", + "镇宁布依族苗族自治县", + "关岭布依族苗族自治县", + "紫云苗族布依族自治县" + ], + "毕节市": [ + "七星关区", + "大方县", + "黔西县", + "金沙县", + "织金县", + "纳雍县", + "威宁彝族回族苗族自治县", + "赫章县" + ], + "铜仁市": [ + "碧江区", + "万山区", + "江口县", + "玉屏侗族自治县", + "石阡县", + "思南县", + "印江土家族苗族自治县", + "德江县", + "沿河土家族自治县", + "松桃苗族自治县" + ], + "黔西南布依族苗族自治州": [ + "兴义市", + "兴仁市", + "普安县", + "晴隆县", + "贞丰县", + "望谟县", + "册亨县", + "安龙县" + ], + "黔东南苗族侗族自治州": [ + "凯里市", + "黄平县", + "施秉县", + "三穗县", + "镇远县", + "岑巩县", + "天柱县", + "锦屏县", + "剑河县", + "台江县", + "黎平县", + "榕江县", + "从江县", + "雷山县", + "麻江县", + "丹寨县" + ], + "黔南布依族苗族自治州": [ + "都匀市", + "福泉市", + "荔波县", + "贵定县", + "瓮安县", + "独山县", + "平塘县", + "罗甸县", + "长顺县", + "龙里县", + "惠水县", + "三都水族自治县" + ] + }, + "云南省": { + "昆明市": [ + "五华区", + "盘龙区", + "官渡区", + "西山区", + "东川区", + "呈贡区", + "晋宁区", + "富民县", + "宜良县", + "石林彝族自治县", + "嵩明县", + "禄劝彝族苗族自治县", + "寻甸回族彝族自治县", + "安宁市" + ], + "曲靖市": [ + "麒麟区", + "沾益区", + "马龙区", + "陆良县", + "师宗县", + "罗平县", + "富源县", + "会泽县", + "宣威市" + ], + "玉溪市": [ + "红塔区", + "江川区", + "澄江县", + "通海县", + "华宁县", + "易门县", + "峨山彝族自治县", + "新平彝族傣族自治县", + "元江哈尼族彝族傣族自治县" + ], + "保山市": [ + "隆阳区", + "施甸县", + "龙陵县", + "昌宁县", + "腾冲市" + ], + "昭通市": [ + "昭阳区", + "鲁甸县", + "巧家县", + "盐津县", + "大关县", + "永善县", + "绥江县", + "镇雄县", + "彝良县", + "威信县", + "水富市" + ], + "丽江市": [ + "古城区", + "玉龙纳西族自治县", + "永胜县", + "华坪县", + "宁蒗彝族自治县" + ], + "普洱市": [ + "思茅区", + "宁洱哈尼族彝族自治县", + "墨江哈尼族自治县", + "景东彝族自治县", + "景谷傣族彝族自治县", + "镇沅彝族哈尼族拉祜族自治县", + "江城哈尼族彝族自治县", + "孟连傣族拉祜族佤族自治县", + "澜沧拉祜族自治县", + "西盟佤族自治县" + ], + "临沧市": [ + "临翔区", + "凤庆县", + "云县", + "永德县", + "镇康县", + "双江拉祜族佤族布朗族傣族自治县", + "耿马傣族佤族自治县", + "沧源佤族自治县" + ], + "楚雄彝族自治州": [ + "楚雄市", + "双柏县", + "牟定县", + "南华县", + "姚安县", + "大姚县", + "永仁县", + "元谋县", + "武定县", + "禄丰县" + ], + "红河哈尼族彝族自治州": [ + "个旧市", + "开远市", + "蒙自市", + "弥勒市", + "屏边苗族自治县", + "建水县", + "石屏县", + "泸西县", + "元阳县", + "红河县", + "金平苗族瑶族傣族自治县", + "绿春县", + "河口瑶族自治县" + ], + "文山壮族苗族自治州": [ + "文山市", + "砚山县", + "西畴县", + "麻栗坡县", + "马关县", + "丘北县", + "广南县", + "富宁县" + ], + "西双版纳傣族自治州": [ + "景洪市", + "勐海县", + "勐腊县" + ], + "大理白族自治州": [ + "大理市", + "漾濞彝族自治县", + "祥云县", + "宾川县", + "弥渡县", + "南涧彝族自治县", + "巍山彝族回族自治县", + "永平县", + "云龙县", + "洱源县", + "剑川县", + "鹤庆县" + ], + "德宏傣族景颇族自治州": [ + "瑞丽市", + "芒市", + "梁河县", + "盈江县", + "陇川县" + ], + "怒江傈僳族自治州": [ + "泸水市", + "福贡县", + "贡山独龙族怒族自治县", + "兰坪白族普米族自治县" + ], + "迪庆藏族自治州": [ + "香格里拉市", + "德钦县", + "维西傈僳族自治县" + ] + }, + "西藏自治区": { + "拉萨市": [ + "城关区", + "堆龙德庆区", + "达孜区", + "林周县", + "当雄县", + "尼木县", + "曲水县", + "墨竹工卡县", + "格尔木藏青工业园区", + "拉萨经济技术开发区", + "西藏文化旅游创意园区", + "达孜工业园区" + ], + "日喀则市": [ + "桑珠孜区", + "南木林县", + "江孜县", + "定日县", + "萨迦县", + "拉孜县", + "昂仁县", + "谢通门县", + "白朗县", + "仁布县", + "康马县", + "定结县", + "仲巴县", + "亚东县", + "吉隆县", + "聂拉木县", + "萨嘎县", + "岗巴县" + ], + "昌都市": [ + "卡若区", + "江达县", + "贡觉县", + "类乌齐县", + "丁青县", + "察雅县", + "八宿县", + "左贡县", + "芒康县", + "洛隆县", + "边坝县" + ], + "林芝市": [ + "巴宜区", + "工布江达县", + "米林县", + "墨脱县", + "波密县", + "察隅县", + "朗县" + ], + "山南市": [ + "乃东区", + "扎囊县", + "贡嘎县", + "桑日县", + "琼结县", + "曲松县", + "措美县", + "洛扎县", + "加查县", + "隆子县", + "错那县", + "浪卡子县" + ], + "那曲市": [ + "色尼区", + "嘉黎县", + "比如县", + "聂荣县", + "安多县", + "申扎县", + "索县", + "班戈县", + "巴青县", + "尼玛县", + "双湖县" + ], + "阿里地区": [ + "普兰县", + "札达县", + "噶尔县", + "日土县", + "革吉县", + "改则县", + "措勤县" + ] + }, + "陕西省": { + "西安市": [ + "新城区", + "碑林区", + "莲湖区", + "灞桥区", + "未央区", + "雁塔区", + "阎良区", + "临潼区", + "长安区", + "高陵区", + "鄠邑区", + "蓝田县", + "周至县" + ], + "铜川市": [ + "王益区", + "印台区", + "耀州区", + "宜君县" + ], + "宝鸡市": [ + "渭滨区", + "金台区", + "陈仓区", + "凤翔县", + "岐山县", + "扶风县", + "眉县", + "陇县", + "千阳县", + "麟游县", + "凤县", + "太白县" + ], + "咸阳市": [ + "秦都区", + "杨陵区", + "渭城区", + "三原县", + "泾阳县", + "乾县", + "礼泉县", + "永寿县", + "长武县", + "旬邑县", + "淳化县", + "武功县", + "兴平市", + "彬州市" + ], + "渭南市": [ + "临渭区", + "华州区", + "潼关县", + "大荔县", + "合阳县", + "澄城县", + "蒲城县", + "白水县", + "富平县", + "韩城市", + "华阴市" + ], + "延安市": [ + "宝塔区", + "安塞区", + "延长县", + "延川县", + "子长县", + "志丹县", + "吴起县", + "甘泉县", + "富县", + "洛川县", + "宜川县", + "黄龙县", + "黄陵县" + ], + "汉中市": [ + "汉台区", + "南郑区", + "城固县", + "洋县", + "西乡县", + "勉县", + "宁强县", + "略阳县", + "镇巴县", + "留坝县", + "佛坪县" + ], + "榆林市": [ + "榆阳区", + "横山区", + "府谷县", + "靖边县", + "定边县", + "绥德县", + "米脂县", + "佳县", + "吴堡县", + "清涧县", + "子洲县", + "神木市" + ], + "安康市": [ + "汉滨区", + "汉阴县", + "石泉县", + "宁陕县", + "紫阳县", + "岚皋县", + "平利县", + "镇坪县", + "旬阳县", + "白河县" + ], + "商洛市": [ + "商州区", + "洛南县", + "丹凤县", + "商南县", + "山阳县", + "镇安县", + "柞水县" + ] + }, + "甘肃省": { + "兰州市": [ + "城关区", + "七里河区", + "西固区", + "安宁区", + "红古区", + "永登县", + "皋兰县", + "榆中县", + "兰州新区" + ], + "嘉峪关市": [ + "新城镇", + "峪泉镇", + "文殊镇", + "雄关区", + "镜铁区", + "长城区" + ], + "金昌市": [ + "金川区", + "永昌县" + ], + "白银市": [ + "白银区", + "平川区", + "靖远县", + "会宁县", + "景泰县" + ], + "天水市": [ + "秦州区", + "麦积区", + "清水县", + "秦安县", + "甘谷县", + "武山县", + "张家川回族自治县" + ], + "武威市": [ + "凉州区", + "民勤县", + "古浪县", + "天祝藏族自治县" + ], + "张掖市": [ + "甘州区", + "肃南裕固族自治县", + "民乐县", + "临泽县", + "高台县", + "山丹县" + ], + "平凉市": [ + "崆峒区", + "泾川县", + "灵台县", + "崇信县", + "庄浪县", + "静宁县", + "华亭市" + ], + "酒泉市": [ + "肃州区", + "金塔县", + "瓜州县", + "肃北蒙古族自治县", + "阿克塞哈萨克族自治县", + "玉门市", + "敦煌市" + ], + "庆阳市": [ + "西峰区", + "庆城县", + "环县", + "华池县", + "合水县", + "正宁县", + "宁县", + "镇原县" + ], + "定西市": [ + "安定区", + "通渭县", + "陇西县", + "渭源县", + "临洮县", + "漳县", + "岷县" + ], + "陇南市": [ + "武都区", + "成县", + "文县", + "宕昌县", + "康县", + "西和县", + "礼县", + "徽县", + "两当县" + ], + "临夏回族自治州": [ + "临夏市", + "临夏县", + "康乐县", + "永靖县", + "广河县", + "和政县", + "东乡族自治县", + "积石山保安族东乡族撒拉族自治县" + ], + "甘南藏族自治州": [ + "合作市", + "临潭县", + "卓尼县", + "舟曲县", + "迭部县", + "玛曲县", + "碌曲县", + "夏河县" + ] + }, + "青海省": { + "西宁市": [ + "城东区", + "城中区", + "城西区", + "城北区", + "大通回族土族自治县", + "湟中县", + "湟源县" + ], + "海东市": [ + "乐都区", + "平安区", + "民和回族土族自治县", + "互助土族自治县", + "化隆回族自治县", + "循化撒拉族自治县" + ], + "海北藏族自治州": [ + "门源回族自治县", + "祁连县", + "海晏县", + "刚察县" + ], + "黄南藏族自治州": [ + "同仁县", + "尖扎县", + "泽库县", + "河南蒙古族自治县" + ], + "海南藏族自治州": [ + "共和县", + "同德县", + "贵德县", + "兴海县", + "贵南县" + ], + "果洛藏族自治州": [ + "玛沁县", + "班玛县", + "甘德县", + "达日县", + "久治县", + "玛多县" + ], + "玉树藏族自治州": [ + "玉树市", + "杂多县", + "称多县", + "治多县", + "囊谦县", + "曲麻莱县" + ], + "海西蒙古族藏族自治州": [ + "格尔木市", + "德令哈市", + "茫崖市", + "乌兰县", + "都兰县", + "天峻县", + "大柴旦行政委员会" + ] + }, + "宁夏回族自治区": { + "银川市": [ + "兴庆区", + "西夏区", + "金凤区", + "永宁县", + "贺兰县", + "灵武市" + ], + "石嘴山市": [ + "大武口区", + "惠农区", + "平罗县" + ], + "吴忠市": [ + "利通区", + "红寺堡区", + "盐池县", + "同心县", + "青铜峡市" + ], + "固原市": [ + "原州区", + "西吉县", + "隆德县", + "泾源县", + "彭阳县" + ], + "中卫市": [ + "沙坡头区", + "中宁县", + "海原县" + ] + }, + "新疆维吾尔自治区": { + "乌鲁木齐市": [ + "天山区", + "沙依巴克区", + "新市区", + "水磨沟区", + "头屯河区", + "达坂城区", + "米东区", + "乌鲁木齐县", + "乌鲁木齐经济技术开发区", + "乌鲁木齐高新技术产业开发区" + ], + "克拉玛依市": [ + "独山子区", + "克拉玛依区", + "白碱滩区", + "乌尔禾区" + ], + "吐鲁番市": [ + "高昌区", + "鄯善县", + "托克逊县" + ], + "哈密市": [ + "伊州区", + "巴里坤哈萨克自治县", + "伊吾县" + ], + "昌吉回族自治州": [ + "昌吉市", + "阜康市", + "呼图壁县", + "玛纳斯县", + "奇台县", + "吉木萨尔县", + "木垒哈萨克自治县" + ], + "博尔塔拉蒙古自治州": [ + "博乐市", + "阿拉山口市", + "精河县", + "温泉县" + ], + "巴音郭楞蒙古自治州": [ + "库尔勒市", + "轮台县", + "尉犁县", + "若羌县", + "且末县", + "焉耆回族自治县", + "和静县", + "和硕县", + "博湖县", + "库尔勒经济技术开发区" + ], + "阿克苏地区": [ + "阿克苏市", + "温宿县", + "库车县", + "沙雅县", + "新和县", + "拜城县", + "乌什县", + "阿瓦提县", + "柯坪县" + ], + "克孜勒苏柯尔克孜自治州": [ + "阿图什市", + "阿克陶县", + "阿合奇县", + "乌恰县" + ], + "喀什地区": [ + "喀什市", + "疏附县", + "疏勒县", + "英吉沙县", + "泽普县", + "莎车县", + "叶城县", + "麦盖提县", + "岳普湖县", + "伽师县", + "巴楚县", + "塔什库尔干塔吉克自治县" + ], + "和田地区": [ + "和田市", + "和田县", + "墨玉县", + "皮山县", + "洛浦县", + "策勒县", + "于田县", + "民丰县" + ], + "伊犁哈萨克自治州": [ + "伊宁市", + "奎屯市", + "霍尔果斯市", + "伊宁县", + "察布查尔锡伯自治县", + "霍城县", + "巩留县", + "新源县", + "昭苏县", + "特克斯县", + "尼勒克县" + ], + "塔城地区": [ + "塔城市", + "乌苏市", + "额敏县", + "沙湾县", + "托里县", + "裕民县", + "和布克赛尔蒙古自治县" + ], + "阿勒泰地区": [ + "阿勒泰市", + "布尔津县", + "富蕴县", + "福海县", + "哈巴河县", + "青河县", + "吉木乃县" + ], + "自治区直辖县级行政区划": [ + "石河子市", + "阿拉尔市", + "图木舒克市", + "五家渠市", + "铁门关市" + ] + }, + "香港特别行政区": { + "香港岛": [ + "中西区", + "湾仔区", + "东区", + "南区" + ], + "九龙": [ + "油尖旺区", + "深水埗区", + "九龙城区", + "黄大仙区", + "观塘区" + ], + "新界": [ + "葵青区", + "荃湾区", + "屯门区", + "元朗区", + "北区", + "大埔区", + "沙田区", + "西贡区", + "离岛区" + ] + }, + "澳门特别行政区": { + "澳门半岛": [ + "大堂区", + "望德堂区", + "风顺堂区", + "花地玛堂区", + "圣安多尼堂区" + ], + "澳门外岛": [ + "嘉模堂区(氹仔)", + "圣方济各堂区(路环)" + ] + }, + "台湾省": { + "台北市": [ + "大安区", + "内湖区", + "士林区", + "文山区", + "北投区", + "中山区", + "信义区", + "松山区", + "万华区", + "中正区", + "大同区", + "南港区" + ], + "新北市": [ + "板桥区", + "新庄区", + "中和区", + "三重区", + "新店区", + "土城区", + "永和区", + "芦洲区", + "汐止区", + "树林区", + "淡水区", + "三峡区", + "林口区", + "莺歌区", + "五股区", + "泰山区", + "瑞芳区", + "八里区", + "深坑区", + "三芝区", + "万里区", + "金山区", + "贡寮区", + "石门区", + "双溪区", + "石碇区", + "坪林区", + "乌来区", + "平溪区" + ], + "桃园市": [ + "桃园区", + "中坜区", + "平镇区", + "八德区", + "杨梅区", + "芦竹区", + "龟山区", + "龙潭区", + "大溪区", + "大园区", + "观音区", + "新屋区", + "复兴区" + ], + "台中市": [ + "北屯区", + "西屯区", + "大里区", + "太平区", + "南屯区", + "丰原区", + "东区", + "南区", + "西区", + "北区", + "中区", + "潭子区", + "大雅区", + "沙鹿区", + "清水区", + "龙井区", + "大甲区", + "乌日区", + "神冈区", + "雾峰区", + "梧栖区", + "大肚区", + "后里区", + "东势区", + "外埔区", + "新社区", + "大安区", + "石冈区", + "和平区" + ], + "台南市": [ + "永康区", + "安南区", + "东区", + "北区", + "南区", + "中西区", + "新营区", + "仁德区", + "归仁区", + "安平区", + "佳里区", + "善化区", + "麻豆区", + "新化区", + "新市区", + "关庙区", + "安定区", + "白河区", + "学甲区", + "盐水区", + "西港区", + "下营区", + "后壁区", + "七股区", + "六甲区", + "官田区", + "柳营区", + "东山区", + "将军区", + "玉井区", + "北门区", + "大内区", + "楠西区", + "南化区", + "山上区", + "左镇区", + "龙崎区" + ], + "高雄市": [ + "凤山区", + "三民区", + "左营区", + "前镇区", + "楠梓区", + "苓雅区", + "小港区", + "鼓山区", + "大寮区", + "冈山区", + "仁武区", + "林园区", + "路竹区", + "新兴区", + "鸟松区", + "大树区", + "美浓区", + "桥头区", + "旗山区", + "梓官区", + "大社区", + "茄萣区", + "燕巢区", + "湖内区", + "阿莲区", + "旗津区", + "前金区", + "盐埕区", + "弥陀区", + "内门区", + "永安区", + "六龟区", + "杉林区", + "田寮区", + "甲仙区", + "桃源区", + "那玛夏区", + "茂林区" + ], + "基隆市": [ + "安乐区", + "七堵区", + "信义区", + "中正区", + "中山区", + "仁爱区", + "暖暖区" + ], + "新竹市": [ + "东区", + "北区", + "香山区" + ], + "嘉义市": [ + "东区", + "西区" + ], + "新竹县": [ + "竹北市", + "竹东镇", + "新埔镇", + "关西镇", + "湖口乡", + "新丰乡", + "芎林乡", + "宝山乡", + "横山乡", + "尖石乡", + "北埔乡", + "峨眉乡", + "五峰乡" + ], + "苗栗县": [ + "头份市", + "苗栗市", + "竹南镇", + "苑里镇", + "后龙镇", + "通霄镇", + "卓兰镇", + "公馆乡", + "铜锣乡", + "三义乡", + "大湖乡", + "造桥乡", + "头屋乡", + "南庄乡", + "西湖乡", + "三湾乡", + "泰安乡", + "狮潭乡" + ], + "彰化县": [ + "彰化市", + "员林市", + "和美镇", + "鹿港镇", + "溪湖镇", + "二林镇", + "田中镇", + "北斗镇", + "福兴乡", + "花坛乡", + "社头乡", + "秀水乡", + "伸港乡", + "大村乡", + "永靖乡", + "埔心乡", + "芳苑乡", + "埔盐乡", + "埤头乡", + "溪州乡", + "田尾乡", + "芬园乡", + "线西乡", + "大城乡", + "二水乡", + "竹塘乡" + ], + "南投县": [ + "南投市", + "草屯镇", + "埔里镇", + "竹山镇", + "集集镇", + "名间乡", + "国姓乡", + "鹿谷乡", + "水里乡", + "信义乡", + "仁爱乡", + "鱼池乡", + "中寮乡" + ], + "云林县": [ + "斗六市", + "虎尾镇", + "西螺镇", + "斗南镇", + "北港镇", + "土库镇", + "麦寮乡", + "古坑乡", + "莿桐乡", + "口湖乡", + "二仑乡", + "元长乡", + "水林乡", + "仑背乡", + "台西乡", + "四湖乡", + "大埤乡", + "林内乡", + "东势乡", + "褒忠乡" + ], + "嘉义县": [ + "朴子市", + "太保市", + "大林镇", + "布袋镇", + "民雄乡", + "水上乡", + "中埔乡", + "竹崎乡", + "新港乡", + "东石乡", + "六脚乡", + "梅山乡", + "义竹乡", + "鹿草乡", + "溪口乡", + "番路乡", + "阿里山乡", + "大埔乡" + ], + "屏东县": [ + "屏东市", + "潮州镇", + "东港镇", + "恒春镇", + "内埔乡", + "万丹乡", + "新园乡", + "长治乡", + "里港乡", + "盐埔乡", + "高树乡", + "枋寮乡", + "九如乡", + "万峦乡", + "佳冬乡", + "林边乡", + "竹田乡", + "崁顶乡", + "琉球乡", + "麟洛乡", + "南州乡", + "新埤乡", + "车城乡", + "满州乡", + "三地门乡", + "来义乡", + "玛家乡", + "枋山乡", + "泰武乡", + "牡丹乡", + "狮子乡", + "春日乡", + "雾台乡" + ], + "宜兰县": [ + "宜兰市", + "罗东镇", + "苏澳镇", + "头城镇", + "冬山乡", + "五结乡", + "礁溪乡", + "员山乡", + "壮围乡", + "三星乡", + "大同乡", + "南澳乡" + ], + "花莲县": [ + "花莲市", + "玉里镇", + "凤林镇", + "吉安乡", + "新城乡", + "寿丰乡", + "秀林乡", + "光复乡", + "瑞穗乡", + "富里乡", + "万荣乡", + "卓溪乡", + "丰滨乡" + ], + "台东县": [ + "台东市", + "卑南乡", + "成功镇", + "太麻里乡", + "关山镇", + "东河乡", + "池上乡", + "鹿野乡", + "长滨乡", + "大武乡", + "兰屿乡", + "海端乡", + "绿岛乡", + "金峰乡", + "延平乡", + "达仁乡" + ], + "澎湖县": [ + "马公市", + "湖西乡", + "白沙乡", + "西屿乡", + "望安乡", + "七美乡" + ] + } +} diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 0000000..efea2ba --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,80 @@ +import axios from 'axios' +import { ElMessage } from 'element-plus' +import { getToken, removeToken } from '@/utils/auth' +import { useUserStore } from '@/store/user' +import { pinia } from '@/store' + +// create an axios instance +const service = axios.create({ + baseURL: import.meta.env.VITE_SERVER_API, + withCredentials: true, // send cookies when cross-domain requests + timeout: 50000, // request timeout +}) + +// request interceptor +service.interceptors.request.use( + config => { + const userStore = useUserStore(pinia) + + const token = userStore.token || getToken() + if (token) { + if (!config.headers) { + config.headers = {} + } + config.headers['api-token'] = token + } + return config + }, + error => { + // do something with request error + return Promise.reject(error) + }, +) + +// response interceptor +service.interceptors.response.use( + /** + * If you want to get http information such as headers or status + * Please return response => response + */ + + /** + * Determine the request status by custom code + * Here is just an example + * You can also judge the status by HTTP Status Code + */ + response => { + const res = response.data + + // if the custom code is not 20000, it is judged as an error. + if (res.code !== 0) { + ElMessage({ + message: res.message || 'error', + type: 'error', + duration: 5 * 1000, + }) + + if (res.code === 403) { + removeToken() + window.location.reload() + } + return Promise.reject(res.message || 'error') + } else { + return res + } + }, + error => { + if (error.code === 'ECONNABORTED' + && error.message.indexOf('timeout') > -1) { + error.message = '请求超时!' + } + ElMessage({ + message: error.message, + type: 'error', + duration: 5 * 1000, + }) + return Promise.reject(error) + }, +) + +export default service diff --git a/src/utils/webclient.js b/src/utils/webclient.js new file mode 100644 index 0000000..85be818 --- /dev/null +++ b/src/utils/webclient.js @@ -0,0 +1,26 @@ +import { ref } from 'vue' +import { config } from '@/api/rustdesk' + +export const toWebClientLink = (row) => { + window.open(`${rustdeskConfig.value.api_server}/webclient/#/?id=${row.id}`) +} + +export function loadRustdeskConfig () { + const rustdeskConfig = ref({}) + const fetchConfig = async () => { + const res = await config().catch(_ => false) + if (res) { + rustdeskConfig.value = res.data + localStorage.setItem('custom-rendezvous-server', res.data.id_server) + localStorage.setItem('key', res.data.key) + localStorage.setItem('api-server', res.data.api_server) + } + } + if (rustdeskConfig.value.id_server === undefined || rustdeskConfig.value.key === undefined) { + fetchConfig() + } + return { + rustdeskConfig, + } +} +const { rustdeskConfig } = loadRustdeskConfig() diff --git a/src/views/address_book/index.js b/src/views/address_book/index.js new file mode 100644 index 0000000..c7ac243 --- /dev/null +++ b/src/views/address_book/index.js @@ -0,0 +1,132 @@ +import { onActivated, onMounted, reactive, ref, watch } from 'vue' +import { create, list, remove, update } from '@/api/address_book' +import { ElMessage, ElMessageBox } from 'element-plus' +import { useRoute } from 'vue-router' + +export function useRepositories () { + const route = useRoute() + const user_id = route.query?.user_id + + const listRes = reactive({ + list: [], total: 0, loading: false, + }) + const listQuery = reactive({ + page: 1, + page_size: 10, + is_my: 0, + user_id: user_id ? parseInt(user_id) : null, + }) + + const getList = async () => { + listRes.loading = true + const res = await list(listQuery).catch(_ => false) + listRes.loading = false + if (res) { + listRes.list = res.data.list + listRes.total = res.data.total + } + } + const handlerQuery = () => { + if (listQuery.page === 1) { + getList() + } else { + listQuery.page = 1 + } + } + + const del = async (row) => { + const cf = await ElMessageBox.confirm('确定删除么?', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }).catch(_ => false) + if (!cf) { + return false + } + + const res = await remove({ row_id: row.row_id }).catch(_ => false) + if (res) { + ElMessage.success('操作成功') + getList() + } + } + + const platformList = [ + { label: 'Windows', value: 'Windows' }, + { label: 'Linux', value: 'Linux' }, + { label: 'Mac OS', value: 'Mac OS' }, + { label: 'Android', value: 'Android' }, + ] + const formVisible = ref(false) + const formData = reactive({ + 'row_id': 0, + 'alias': '', + 'force_always_relay': false, + 'hash': '', + 'hostname': '', + 'id': '', + 'login_name': '', + 'online': false, + 'password': '', + 'platform': '', + 'rdp_port': '', + 'rdp_username': '', + 'same_server': false, + 'tags': [], + 'user_id': null, + 'username': '', + }) + + const toEdit = (row) => { + formVisible.value = true + //将row中的数据赋值给formData + Object.keys(formData).forEach(key => { + formData[key] = row[key] + }) + + } + const toAdd = () => { + formVisible.value = true + //重置formData + formData.row_id = 0 + formData.alias = '' + formData.force_always_relay = false + formData.hash = '' + formData.hostname = '' + formData.id = '' + formData.login_name = '' + formData.online = false + formData.password = '' + formData.platform = '' + formData.rdp_port = '' + formData.rdp_username = '' + formData.same_server = false + formData.tags = [] + formData.user_id = null + formData.username = '' + + } + const submit = async () => { + const api = formData.row_id ? update : create + const res = await api(formData).catch(_ => false) + if (res) { + ElMessage.success('操作成功') + formVisible.value = false + getList() + } + } + + return { + listRes, + listQuery, + getList, + handlerQuery, + del, + platformList, + formVisible, + formData, + toEdit, + toAdd, + submit, + } +} diff --git a/src/views/address_book/index.vue b/src/views/address_book/index.vue new file mode 100644 index 0000000..c049d5e --- /dev/null +++ b/src/views/address_book/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/views/error-page/404.vue b/src/views/error-page/404.vue new file mode 100644 index 0000000..35b24a6 --- /dev/null +++ b/src/views/error-page/404.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/src/views/group/index.vue b/src/views/group/index.vue new file mode 100644 index 0000000..91c778d --- /dev/null +++ b/src/views/group/index.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/views/index/index.vue b/src/views/index/index.vue new file mode 100644 index 0000000..468065f --- /dev/null +++ b/src/views/index/index.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/views/login/login.vue b/src/views/login/login.vue new file mode 100644 index 0000000..b4a156b --- /dev/null +++ b/src/views/login/login.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/src/views/my/address_book/index.vue b/src/views/my/address_book/index.vue new file mode 100644 index 0000000..4bc6a9b --- /dev/null +++ b/src/views/my/address_book/index.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/views/my/tag/index.vue b/src/views/my/tag/index.vue new file mode 100644 index 0000000..a011b69 --- /dev/null +++ b/src/views/my/tag/index.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/src/views/peer/index.vue b/src/views/peer/index.vue new file mode 100644 index 0000000..c8b3b5c --- /dev/null +++ b/src/views/peer/index.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/src/views/tag/index.js b/src/views/tag/index.js new file mode 100644 index 0000000..ffecd8e --- /dev/null +++ b/src/views/tag/index.js @@ -0,0 +1,155 @@ +import { onActivated, onMounted, reactive, ref, watch } from 'vue' +import { create, list, remove, update } from '@/api/tag' +import { ElMessage, ElMessageBox } from 'element-plus' +import { useRoute } from 'vue-router' + +export function useRepositories () { + //获取query + const route = useRoute() + const user_id = route.query?.user_id + const listRes = reactive({ + list: [], total: 0, loading: false, + }) + const listQuery = reactive({ + page: 1, + page_size: 10, + is_my: 0, + user_id: user_id ? parseInt(user_id) : null, + }) + + const flutterColor2rgba = (color) => { + // color 是十进制的数字,先转成16进制 + let hex = color.toString(16) + console.log('hex', hex) + //前两位是透明度 + let alpha = hex.slice(0, 2) + //后六位是颜色 + let rgba = hex.slice(2) + return `rgba(${parseInt(rgba.slice(0, 2), 16)}, ${parseInt(rgba.slice(2, 4), 16)}, ${parseInt(rgba.slice(4, 6), 16)}, ${parseInt(alpha, 16) / 255})` + } + + const rgba2flutterColor = (color) => { + console.log('color', color) + //rgba(133, 33, 33, 0.81) + let rgba = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*(\d+(\.\d+)?)\)/) + console.log('rgba', rgba) + let alpha = Math.round(parseFloat(rgba[4]) * 255).toString(16) + let r = parseInt(rgba[1]).toString(16) + let g = parseInt(rgba[2]).toString(16) + let b = parseInt(rgba[3]).toString(16) + //如果是1位要补位 + if (alpha.length === 1) { + alpha = '0' + alpha + } + if (r.length === 1) { + r = '0' + r + } + if (g.length === 1) { + g = '0' + g + } + if (b.length === 1) { + b = '0' + b + } + return parseInt(alpha + r + g + b, 16) + } + + const getList = async () => { + listRes.loading = true + const res = await list(listQuery).catch(_ => false) + listRes.loading = false + if (res) { + listRes.list = res.data.list.map(item => { + item.color = flutterColor2rgba(item.color) + return item + }) + listRes.total = res.data.total + } + } + const handlerQuery = () => { + if (listQuery.page === 1) { + getList() + } else { + listQuery.page = 1 + } + } + + const del = async (row) => { + const cf = await ElMessageBox.confirm('确定删除么?', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }).catch(_ => false) + if (!cf) { + return false + } + + const res = await remove({ id: row.id }).catch(_ => false) + if (res) { + ElMessage.success('操作成功') + getList() + } + } + onMounted(getList) + onActivated(getList) + + watch(() => listQuery.page, getList) + + watch(() => listQuery.page_size, handlerQuery) + + const formVisible = ref(false) + const formData = reactive({ + id: 0, + name: '', + color: 0, + user_id: 0, + }) + const currentColor = ref('') + const activeChange = (c) => { + console.log(c) + currentColor.value = c + } + const toEdit = (row) => { + console.log('row', row) + formVisible.value = true + formData.id = row.id + formData.name = row.name + formData.color = row.color + formData.user_id = row.user_id + } + const toAdd = () => { + formVisible.value = true + formData.id = 0 + formData.name = '' + formData.color = 0 + formData.user_id = 0 + } + const submit = async () => { + console.log(formData) + const api = formData.id ? update : create + const data = { + ...formData, + color: rgba2flutterColor(formData.color), + } + console.log(data) + const res = await api(data).catch(_ => false) + if (res) { + ElMessage.success('操作成功') + formVisible.value = false + getList() + } + } + return { + listRes, + listQuery, + getList, + handlerQuery, + del, + formVisible, + formData, + toEdit, + toAdd, + submit, + activeChange, + currentColor, + } +} diff --git a/src/views/tag/index.vue b/src/views/tag/index.vue new file mode 100644 index 0000000..27012ce --- /dev/null +++ b/src/views/tag/index.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/views/user/composables/edit.js b/src/views/user/composables/edit.js new file mode 100644 index 0000000..4681f02 --- /dev/null +++ b/src/views/user/composables/edit.js @@ -0,0 +1,86 @@ +import { ref, onMounted, reactive, watch } from 'vue' +import { create, detail, update, remove } from '@/api/user' +import { ElMessage, ElMessageBox } from 'element-plus' +import { useRouter } from 'vue-router' +import { list as groups } from '@/api/group' + +export function useGetDetail (id) { + let item = ref({}) //保留原始值 + let form = ref({}) + const groupsList = ref([]) + const getDetail = async (id) => { + const res = await detail(id) + item.value = { ...res.data } + form.value = { ...res.data } + } + if (id > 0) { + onMounted(getDetail(id)) + } + + const getGroups = async () => { + const res = await groups({ page_size: 9999 }).catch(_ => false) + if (res) { + groupsList.value = res.data.list + } + } + onMounted(getGroups) + return { + form, + item, + getDetail, + groupsList + } +} + +export function useSubmit (form, id) { + const root = ref(null) + const router = useRouter() + const rules = reactive({ + username: [{ required: true, message: '用户名是必须的' }], + // nickname: [{ required: true, message: '昵称是必须的' }], + status: [{ required: true, message: '请选择状态' }], + }) + + const validate = async () => { + const res = await root.value.validate().catch(err => false) + return res + } + + const submitCreate = async () => { + const res = await create(form.value).catch(_ => false) + return res.code === 0 + } + + const submitUpdate = async () => { + const res = await update(form.value).catch(_ => false) + return res.code === 0 + } + const submitFunc = id > 0 ? submitUpdate : submitCreate + + const submit = async () => { + const v = await validate() + if (!v) { + return + } + + const res = await submitFunc() + if (res) { + ElMessage.success('操作成功') + router.back() + } + } + + const cancel = () => { + router.back() + } + + return { + root, + rules, + validate, + submit, + cancel, + } +} + + diff --git a/src/views/user/composables/index.js b/src/views/user/composables/index.js new file mode 100644 index 0000000..a6542e6 --- /dev/null +++ b/src/views/user/composables/index.js @@ -0,0 +1,124 @@ +import { onMounted, reactive, watch } from 'vue' +import { list, remove, changePwd } from '@/api/user' +import { list as groups } from '@/api/group' +import { useRouter } from 'vue-router' +import { ElMessageBox, ElMessage } from 'element-plus' + +export function useRepositories () { + + const listRes = reactive({ + list: [], total: 0, loading: false, + groups: [], + }) + const listQuery = reactive({ + page: 1, + page_size: 10, + username: '', + }) + + const getList = async () => { + listRes.loading = true + const res = await list(listQuery).catch(_ => false) + listRes.loading = false + if (res) { + listRes.list = res.data.list + listRes.total = res.data.total + } + } + + const handlerQuery = () => { + if (listQuery.page === 1) { + getList() + } else { + listQuery.page = 1 + //由watch 触发 + } + } + + const getGroups = async () => { + const res = await groups({ page_size: 9999 }).catch(_ => false) + if (res) { + listRes.groups = res.data.list + } + } + onMounted(getGroups) + + onMounted(getList) + + watch(() => listQuery.page, getList) + watch(() => listQuery.page_size, handlerQuery) + return { + listRes, + listQuery, + handlerQuery, + getList, + getGroups, + } +} + +export function useToEditOrAdd () { + const router = useRouter() + const toEdit = (row) => { + router.push('/user/edit/' + row.id) + } + const toAdd = () => { + router.push('/user/add') + } + const toTag = (row) => { + router.push('/user/tag/?user_id=' + row.id) + } + const toAddressBook = (row) => { + router.push('/user/addressBook/?user_id=' + row.id) + } + return { + toAdd, + toEdit, + toTag, + toAddressBook + } +} + +export function useDel () { + const del = async (id) => { + const cf = await ElMessageBox.confirm('确定删除么?', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }).catch(_ => false) + if (!cf) { + return false + } + + const res = remove({ id }).catch(_ => false) + return res + } + return { + del, + } +} + +export function useChangePwd () { + const changePass = async (admin) => { + const input = await ElMessageBox.prompt('请输入新密码', '重置密码', { + confirmButtonText: '确定', + cancelButtonText: '取消', + }).catch(_ => false) + if (!input) { + return + } + const confirm = await ElMessageBox.confirm('确定重置密码么?', { + confirmButtonText: '确定', + cancelButtonText: '取消', + }).catch(_ => false) + if (!confirm) { + return + } + const res = await changePwd({ id: admin.id, password: input.value }).catch(_ => false) + if (!res) { + return + } + ElMessage.success('修改成功') + } + + return { changePass } +} diff --git a/src/views/user/edit.vue b/src/views/user/edit.vue new file mode 100644 index 0000000..137dc96 --- /dev/null +++ b/src/views/user/edit.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/src/views/user/index.vue b/src/views/user/index.vue new file mode 100644 index 0000000..d91a1ee --- /dev/null +++ b/src/views/user/index.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..faeae16 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,82 @@ +import { defineConfig } from 'vite' +import * as path from 'path' +import * as dotenv from 'dotenv' +import * as fs from 'fs' +import vue from '@vitejs/plugin-vue' + +const NODE_ENV = process.env.NODE_ENV || 'development' +const envFile = `.env.${NODE_ENV}` +const envConfig = dotenv.parse(fs.readFileSync(envFile)) +for (const k in envConfig) { + process.env[k] = envConfig[k] +} + +let alias = { + '@': path.resolve(__dirname, './src'), + 'vue$': 'vue/dist/vue.runtime.esm-bundler.js', +} + +const conf = { + base: './', // index.html文件所在位置 + root: './', // js导入的资源路径,src + server: { + open: true, + port: process.env.VITE_DEV_PORT, + proxy: { + [process.env.VITE_SERVER_API]: { + target: process.env.VITE_SERVER_PATH, + // rewrite: path => path.replace(/^\/api/, '/api'), //为了模拟 + changeOrigin: true, + }, + }, + }, + build: { + target: 'es2015', + minify: 'terser', // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用 esbuild + manifest: false, // 是否产出maifest.json + sourcemap: false, // 是否产出soucemap.json + emptyOutDir: true, + outDir: 'dist', // 产出目录 + rollupOptions: { + output: { + manualChunks (id) { + if (id.includes('node_modules')) { + const arr = id.toString().split('node_modules/')[1].split('/') + switch (arr[0]) { + case '@popperjs': + case '@vue': + case 'axios': + case 'element-plus': + case '@element-plus': + return '_' + arr[0] + default : + return '__vendor' + } + }else if(id.includes('Gwen-admin/src')){ + //src 下的都打包到一起 不然很多小文件 + return 'gwen' + } + }, + chunkFileNames: 'static/chunk/[name]-[hash].js', + entryFileNames: 'static/entry/[name]-[hash].js', + assetFileNames: 'static/[ext]/[name]-[hash].[ext]' + }, + }, + }, + css: { + preprocessorOptions: { + scss: { + javascriptEnabled: true, + }, + }, + }, + resolve: { + alias, + }, + plugins: [ + vue(), + ], +} + +// https://vitejs.dev/config/ +export default defineConfig(conf)