diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index df6d9f84..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * InulaJS is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -module.exports = { - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'prettier', - ], - root: true, - - plugins: ['jest', 'no-for-of-loops', 'no-function-declare-after-return', 'react', '@typescript-eslint'], - - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 8, - sourceType: 'module', - ecmaFeatures: { - jsx: true, - modules: true, - experimentalObjectRestSpread: true, - }, - }, - env: { - browser: true, - jest: true, - node: true, - es6: true, - }, - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-empty-function': 'off', - semi: ['warn', 'always'], - quotes: ['warn', 'single'], - 'accessor-pairs': 'off', - 'brace-style': ['error', '1tbs'], - 'func-style': ['warn', 'declaration', { allowArrowFunctions: true }], - 'max-lines-per-function': 'off', - 'object-curly-newline': 'off', - // 尾随逗号 - 'comma-dangle': ['error', 'only-multiline'], - - 'no-constant-condition': 'off', - 'no-for-of-loops/no-for-of-loops': 'error', - 'no-function-declare-after-return/no-function-declare-after-return': 'error', - }, - globals: { - isDev: true, - isTest: true, - }, - overrides: [ - { - files: ['scripts/__tests__/**/*.js'], - globals: { - container: true, - }, - }, - ], -}; diff --git a/.gitignore b/.gitignore index 7fa547da..6a0e585e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ -node_modules -build/ +/node_modules .idea .vscode package-lock.json -libs/**/dist -scripts/*.ejs +pnpm-lock.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 8b137891..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app_define.json b/app_define.json deleted file mode 100644 index a674d06c..00000000 --- a/app_define.json +++ /dev/null @@ -1 +0,0 @@ -{"fileVersion":"1","name":"Inula","serviceId":"067e5bef6cd240ae9460229d107a99a6","description":"","version":"1.0.0","type":"microService","processes":{"Inula":{"subscribes":[]}}} diff --git a/libs/inula/README.md b/libs/inula/README.md deleted file mode 100644 index 95818b1f..00000000 --- a/libs/inula/README.md +++ /dev/null @@ -1 +0,0 @@ -# `inula` diff --git a/libs/inula/package.json b/libs/inula/package.json deleted file mode 100644 index 0e57bbe8..00000000 --- a/libs/inula/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "inulajs", - "description": "InulaJS is a JavaScript framework library.", - "keywords": [ - "inula" - ], - "version": "0.0.52", - "homepage": "", - "bugs": "", - "main": "index.js", - "repository": {}, - "engines": { - "node": ">=0.10.0" - }, - "dependencies": {}, - "types": "@types/index.d.ts" -} diff --git a/package.json b/package.json index d2c20795..b32ee29c 100644 --- a/package.json +++ b/package.json @@ -3,19 +3,9 @@ "description": "InulaJS is a JavaScript framework library.", "version": "0.0.52", "private": true, - "workspaces": [ - "libs/*" - ], "scripts": { "lint": "eslint . --ext .ts --fix", - "prettier": "prettier -w libs/**/*.ts", - "build": "rollup --config ./scripts/rollup/rollup.config.js", - "build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js", - "build:inula3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js build:inula3rdLib-dev", - "build-types": "tsc -p libs/inula/index.ts --emitDeclarationOnly --declaration --declarationDir ./build/inula/@types --skipLibCheck || echo \\\"WARNING: TSC exited with status $?\\\"", - "debug-test": "yarn test --debug", - "test": "jest --config=jest.config.js", - "watch-test": "yarn test --watch --dev" + "prettier": "prettier -w libs/**/*.ts" }, "devDependencies": { "@babel/core": "7.16.7", @@ -69,6 +59,5 @@ "engines": { "node": ">=10.x", "npm": ">=7.x" - }, - "dependencies": {} + } } diff --git a/.editorconfig b/packages/create-inula/.editorconfig similarity index 100% rename from .editorconfig rename to packages/create-inula/.editorconfig diff --git a/packages/create-inula/.gitignore b/packages/create-inula/.gitignore new file mode 100644 index 00000000..30ca8b5e --- /dev/null +++ b/packages/create-inula/.gitignore @@ -0,0 +1,5 @@ +/node_modules +.idea +.vscode +package-lock.json +/build diff --git a/packages/create-inula/bin/cli.js b/packages/create-inula/bin/cli.js new file mode 100644 index 00000000..5933b66e --- /dev/null +++ b/packages/create-inula/bin/cli.js @@ -0,0 +1,3 @@ +#! /usr/bin/env node + +require('../index.js'); diff --git a/packages/create-inula/index.js b/packages/create-inula/index.js new file mode 100644 index 00000000..1e0c565f --- /dev/null +++ b/packages/create-inula/index.js @@ -0,0 +1,21 @@ +#! /usr/bin/env node + +const yParser = require('yargs-parser'); +const run = require('./lib/run'); + +// args 为文件名后所有输入 +const args = yParser(process.argv.slice(2)); + +const name = args._[0] || ''; + +const { type } = args; +delete args.type; + +(async () => { + await run({ + name, + type, + args, + }); + process.exit(0); +})(); diff --git a/packages/create-inula/lib/BasicGenerator.js b/packages/create-inula/lib/BasicGenerator.js new file mode 100644 index 00000000..ec38bd0a --- /dev/null +++ b/packages/create-inula/lib/BasicGenerator.js @@ -0,0 +1,154 @@ +const fs = require('fs'); +const path = require('path'); +const Generator = require('yeoman-generator'); +const { globSync } = require('glob'); +const { statSync } = require('fs'); +const { basename } = require('path'); +const _ = require('lodash'); +_.extend(Generator.prototype, require('yeoman-generator/lib/actions/install')); + +function noop() { + return true; +} + +class BasicGenerator extends Generator { + constructor(opts) { + super(opts); + this.opts = opts; + this.name = basename(opts.env.cwd); + } + + isTsFile(f) { + return f.endsWith('.ts') || f.endsWith('.tsx') || !!/(tsconfig\.json)/g.test(f); + } + + /** + * 拷贝文件 + * @param {string} src 源文件路径 + * @param {string} dest 目标文件路径 + * @param {function} filter 过滤函数 + */ + copyFile(src, dest, filter = () => true) { + if (src.indexOf('package.json') !== -1 && fs.existsSync(dest)) { + this.mergePackage(filePath, destFilePath); + return; + } + const readStream = fs.createReadStream(src); + const writeStream = fs.createWriteStream(dest); + readStream.pipe(writeStream); + readStream.on('error', err => console.error(err)); + writeStream.on('error', err => console.error(err)); + // writeStream.on('finish', () => console.log(`Copied ${src} to ${dest}`)); + } + + /** + * 拷贝文件夹 + * @param {string} src 源文件夹路径 + * @param {string} dest 目标文件夹路径 + * @param {function} filter 过滤函数 + */ + copyFolder(src, dest, filter = () => true) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }); + } + const files = fs.readdirSync(src); + for (const file of files) { + const srcPath = path.join(src, file); + const destPath = path.join(dest, file); + const stats = fs.statSync(srcPath); + if (stats.isFile() && filter(srcPath)) { + this.copyFile(srcPath, destPath); + } else if (stats.isDirectory()) { + this.copyFolder(srcPath, destPath, filter); + } + } + } + + writeFiles(src, dest, { context, filterFiles = noop }) { + const files = globSync('**/*', { + cwd: src, + dot: true, + }) + .filter(filterFiles) + .forEach(file => { + const filePath = path.join(src, file); + if (statSync(filePath).isFile()) { + this.fs.copyTpl(filePath, path.join(dest, file), context); + } + }); + } + + copyFiles({ src, dest, context, filterFiles = noop }) { + const files = globSync('**/*', { + cwd: src, + dot: true, + }) + .filter(filterFiles) + .forEach(file => { + const filePath = path.resolve(src, file); + const destFilePath = path.resolve(dest, file); + if (statSync(filePath).isFile()) { + if (file === 'package.json' && fs.existsSync(destFilePath)) { + this.mergePackage(filePath, destFilePath); + } else { + this.fs.copyTpl(filePath, path.resolve(dest, file.replace(/^_/, '.')), context); + } + } + }); + } + + mergePackage(src, dest) { + if (fs.existsSync(dest)) { + const existing = JSON.parse(fs.readFileSync(dest, 'utf8')); + const newPackage = JSON.parse(fs.readFileSync(src, 'utf8')); + const pkg = Object.assign({}, existing, newPackage); + fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n'); + } + } + + traverseDirCapture(dir, dirCallback, fileCallback) { + for (const filename of fs.readdirSync(dir)) { + const fullpath = path.resolve(dir, filename); + if (fs.lstatSync(fullpath).isDirectory()) { + dirCallback(fullpath); + if (fs.existsSync(fullpath)) { + this.traverseDirCapture(fullpath, dirCallback, fileCallback); + } + continue; + } + fileCallback(fullpath); + } + } + + traverseDirBubble(dir, dirCallback, fileCallback) { + for (const filename of fs.readdirSync(dir)) { + const fullpath = path.resolve(dir, filename); + if (fs.lstatSync(fullpath).isDirectory()) { + this.traverseDirBubble(fullpath, dirCallback, fileCallback); + dirCallback(fullpath); + continue; + } + fileCallback(fullpath); + } + } + + emptyDir(dir) { + if (!fs.existsSync(dir)) { + return; + } + + this.traverseDirBubble( + dir, + dir => fs.rmdirSync(dir), + file => fs.unlinkSync(file) + ); + } + + prompt(questions) { + process.send && process.send({ type: 'prompt' }); + process.emit('message', { type: 'prompt' }); + return super.prompt(questions); + } +} + +module.exports = BasicGenerator; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/index.js b/packages/create-inula/lib/generators/InulaJS-antd/index.js new file mode 100644 index 00000000..d37dabab --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/index.js @@ -0,0 +1,30 @@ +const debug = require('debug')('create-umi:generator'); +const BasicGenerator = require('../../BasicGenerator'); +const fs = require('fs'); + +class Generator extends BasicGenerator { + prompting() { + return this.prompt([ + { + type: 'list', + name: 'bundlerType', + message: 'Please select the build type', + choices: ['webpack', 'vite'], + }, + ]).then(props => { + this.prompts = props; + console.log('finish prompting'); + }); + } + writing() { + const src = this.templatePath(this.prompts.bundlerType); + const dest = this.destinationPath(); + this.writeFiles(src, dest, { + context: { + ...this.prompts, + }, + }); + } +} + +module.exports = Generator; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/meta.json b/packages/create-inula/lib/generators/InulaJS-antd/meta.json new file mode 100644 index 00000000..40f5e08d --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/meta.json @@ -0,0 +1,3 @@ +{ + "description": "inula-antd template." +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/.gitignore b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/index.html b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/index.html new file mode 100644 index 00000000..1453e8e6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/index.html @@ -0,0 +1,11 @@ + + + + + + + Vite + React + + + + diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/_utils.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/_utils.js new file mode 100644 index 00000000..1a1e2358 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/_utils.js @@ -0,0 +1,58 @@ +/** + * Query objects that specify keys and values in an array where all values are objects. + * @param {array} array An array where all values are objects, like [{key:1},{key:2}]. + * @param {string} key The key of the object that needs to be queried. + * @param {string} value The value of the object that needs to be queried. + * @return {object|undefined} Return frist object when query success. + */ +export function queryArray(array, key, value) { + if (!Array.isArray(array)) { + return; + } + return array.filter(_ => _[key] === value); +} + +export function randomNumber(min, max) { + return Math.floor(Math.random() * (max - min) + min); +} + +export function randomAvatar() { + const avatarList = [ + 'photo-1549492864-2ec7d66ffb04.jpeg', + 'photo-1480535339474-e083439a320d.jpeg', + 'photo-1523419409543-a5e549c1faa8.jpeg', + 'photo-1519648023493-d82b5f8d7b8a.jpeg', + 'photo-1523307730650-594bc63f9d67.jpeg', + 'photo-1522962506050-a2f0267e4895.jpeg', + 'photo-1489779162738-f81aed9b0a25.jpeg', + 'photo-1534308143481-c55f00be8bd7.jpeg', + 'photo-1519336555923-59661f41bb45.jpeg', + 'photo-1551438632-e8c7d9a5d1b7.jpeg', + 'photo-1525879000488-bff3b1c387cf.jpeg', + 'photo-1487412720507-e7ab37603c6f.jpeg', + 'photo-1510227272981-87123e259b17.jpeg', + ]; + return `//image.zuiidea.com/${ + avatarList[randomNumber(0, avatarList.length - 1)] + }?imageView2/1/w/200/h/200/format/webp/q/75|imageslim`; +} + +export const Constant = { + ApiPrefix: '/api/v1', + NotFound: { + message: 'Not Found', + documentation_url: '', + }, + Color: { + green: '#64ea91', + blue: '#8fc9fb', + purple: '#d897eb', + red: '#f69899', + yellow: '#f8c82e', + peach: '#f797d6', + borderBase: '#e5e5e5', + borderSplit: '#f4f4f4', + grass: '#d6fbb5', + sky: '#c1e0fc', + }, +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/dashboard.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/dashboard.js new file mode 100644 index 00000000..36135aea --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/dashboard.js @@ -0,0 +1,142 @@ +import { Constant } from './_utils'; +import Mock from 'mockjs'; + +const { ApiPrefix, Color } = Constant; + +const Dashboard = Mock.mock({ + 'sales|8': [ + { + 'name|+1': 2008, + 'Clothes|200-500': 1, + 'Food|180-400': 1, + 'Electronics|300-550': 1, + }, + ], + cpu: { + 'usage|50-600': 1, + space: 825, + 'cpu|40-90': 1, + 'data|20': [ + { + 'cpu|20-80': 1, + }, + ], + }, + browser: [ + { + name: 'Google Chrome', + percent: 43.3, + status: 1, + }, + { + name: 'Mozilla Firefox', + percent: 33.4, + status: 2, + }, + { + name: 'Apple Safari', + percent: 34.6, + status: 3, + }, + { + name: 'Internet Explorer', + percent: 12.3, + status: 4, + }, + { + name: 'Opera Mini', + percent: 3.3, + status: 1, + }, + { + name: 'Chromium', + percent: 2.53, + status: 1, + }, + ], + user: { + name: 'github', + sales: 3241, + sold: 3556, + }, + 'completed|12': [ + { + 'name|+1': 2008, + 'Task complete|200-1000': 1, + 'Cards Complete|200-1000': 1, + }, + ], + 'comments|5': [ + { + name: '@last', + 'status|1-3': 1, + content: '@sentence', + avatar() { + return Mock.Random.image('48x48', Mock.Random.color(), '#757575', 'png', this.name.substr(0, 1)); + }, + date() { + return `2016-${Mock.Random.date('MM-dd')} ${Mock.Random.time('HH:mm:ss')}`; + }, + }, + ], + 'recentSales|36': [ + { + 'id|+1': 1, + name: '@last', + 'status|1-4': 1, + date() { + return `${Mock.Random.integer(2015, 2016)}-${Mock.Random.date('MM-dd')} ${Mock.Random.time('HH:mm:ss')}`; + }, + 'price|10-200.1-2': 1, + }, + ], + quote: { + name: 'Joho Doe', + title: 'Graphic Designer', + content: + "I'm selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can't handle me at my worst, then you sure as hell don't deserve me at my best.", + avatar: '//cdn.antd-admin.zuiidea.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236', + }, + numbers: [ + { + icon: 'pay-circle-o', + color: Color.green, + title: 'Online Review', + number: 2781, + }, + { + icon: 'team', + color: Color.blue, + title: 'New Customers', + number: 3241, + }, + { + icon: 'message', + color: Color.purple, + title: 'Active Projects', + number: 253, + }, + { + icon: 'shopping-cart', + color: Color.red, + title: 'Referrals', + number: 4324, + }, + ], +}); + +// module.exports = { +// [`get ${ApiPrefix}/dashboard`](req, res) { +// res.json(Dashboard); +// }, +// }; + +export default [ + { + url: `${ApiPrefix}/dashboard`, + method: 'get', + response: () => { + return Dashboard; + }, + }, +]; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/route.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/route.js new file mode 100644 index 00000000..fc19d968 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/route.js @@ -0,0 +1,41 @@ +import { Constant } from './_utils'; + +const { ApiPrefix } = Constant; + +const database = [ + { + id: '1', + icon: 'dashboard', + name: 'Dashboard', + zh: { + name: '仪表盘', + }, + 'pt-br': { + name: 'Dashboard', + }, + route: '/dashboard', + }, + { + id: '2', + breadcrumbParentId: '', + name: 'User Management', + zh: { + name: '用户管理', + }, + 'pt-br': { + name: 'Usuário', + }, + icon: 'user', + route: '/user', + }, +]; + +export default [ + { + url: `${ApiPrefix}/routes`, + method: 'get', + response: () => { + return database; + }, + }, +]; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/user.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/user.js new file mode 100644 index 00000000..18dd168e --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/mock/user.js @@ -0,0 +1,155 @@ +import { Constant } from './_utils'; +import Mock from 'mockjs'; +import qs from 'qs'; +import { randomAvatar } from './_utils'; +import url from 'url'; + +const { ApiPrefix } = Constant; + +let usersListData = Mock.mock({ + 'data|80-100': [ + { + id: '@id', + name: '@name', + nickName: '@last', + phone: /^1[34578]\d{9}$/, + 'age|11-99': 1, + address: '@county(true)', + isMale: '@boolean', + email: '@email', + createTime: '@datetime', + avatar() { + return randomAvatar(); + }, + }, + ], +}); + +let database = usersListData.data; + +const EnumRoleType = { + ADMIN: 'admin', + DEFAULT: 'guest', + DEVELOPER: 'developer', +}; + +const userPermission = { + DEFAULT: { + visit: ['1', '2', '21', '7', '5', '51', '52', '53'], + role: EnumRoleType.DEFAULT, + }, + ADMIN: { + role: EnumRoleType.ADMIN, + }, + DEVELOPER: { + role: EnumRoleType.DEVELOPER, + }, +}; + +const adminUsers = [ + { + id: 0, + username: 'admin', + password: 'admin', + permissions: userPermission.ADMIN, + avatar: randomAvatar(), + }, + { + id: 1, + username: 'guest', + password: 'guest', + permissions: userPermission.DEFAULT, + avatar: randomAvatar(), + }, + { + id: 2, + username: '吴彦祖', + password: '123456', + permissions: userPermission.DEVELOPER, + avatar: randomAvatar(), + }, +]; + +const queryArray = (array, key, keyAlias = 'key') => { + if (!(array instanceof Array)) { + return null; + } + let data; + + for (let item of array) { + if (item[keyAlias] === key) { + data = item; + break; + } + } + + if (data) { + return data; + } + return null; +}; + +const NOTFOUND = { + message: 'Not Found', + documentation_url: 'http://localhost:8000/request', +}; + +export default [ + { + url: `${ApiPrefix}/user`, + method: 'get', + response: () => { + return { + success: true, + user: adminUsers[0], + }; + }, + }, + { + url: `${ApiPrefix}/users`, + method: 'get', + response: req => { + const { query } = url.parse(req.url, true); + let { pageSize, page, ...other } = query; + pageSize = pageSize || 10; + page = page || 1; + + let newData = database; + for (let key in other) { + if ({}.hasOwnProperty.call(other, key)) { + newData = newData.filter(item => { + if ({}.hasOwnProperty.call(item, key)) { + if (key === 'address') { + return other[key].every(iitem => item[key].indexOf(iitem) > -1); + } else if (key === 'createTime') { + const start = new Date(other[key][0]).getTime(); + const end = new Date(other[key][1]).getTime(); + const now = new Date(item[key]).getTime(); + + if (start && end) { + return now >= start && now <= end; + } + return true; + } + return String(item[key]).trim().indexOf(decodeURI(other[key]).trim()) > -1; + } + return true; + }); + } + } + return { + data: newData.slice((page - 1) * pageSize, page * pageSize), + total: newData.length, + }; + }, + }, + { + url: `${ApiPrefix}/users/delete`, + method: 'post', + response: req => { + const { ids = [] } = req.body; + database = database.filter(item => !ids.some(_ => _ === item.id)); + }, + }, + {}, +]; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/package.json new file mode 100644 index 00000000..4bc6e5fe --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/package.json @@ -0,0 +1,80 @@ +{ + "name": "my-react-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons": "^5.0.1", + "@babel/plugin-proposal-export-default-from": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.21.0", + "@babel/runtime": "7.6.2", + "inula": "0.0.52", + "antd": "^4.0.0", + "axios": "^0.21.0", + "classnames": "^2.2.6", + "echarts": "^5.0.0", + "history": "^5.3.0", + "lodash": "^4.17.11", + "moment": "2.24.0", + "nprogress": "^0.2.0", + "path-to-regexp": "^6.1.0", + "prop-types": "^15.7.0", + "qs": "^6.10.0", + "react-countup": "^4.2.0", + "react-dom": "17.0.2", + "react-draft-wysiwyg": "^1.13.0", + "react-helmet": "^6.0.0", + "react-intl": "^6.3.2", + "react-perfect-scrollbar": "^1.5.0", + "react-router": "^6.10.0", + "react-router-dom": "^6.10.0", + "recharts": "^2.0.0", + "store": "^2.0.0" + }, + "devDependencies": { + "@babel/cli": "7.18.6", + "@babel/core": "^7.18.6", + "@babel/eslint-parser": "7.18.2", + "@babel/plugin-proposal-class-properties": "7.18.6", + "@babel/plugin-syntax-dynamic-import": "7.8.3", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/plugin-transform-flow-strip-types": "^7.18.6", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "7.18.6", + "@babel/types": "7.18.6", + "@types/react": "^18.0.27", + "@types/react-dom": "^18.0.10", + "@vitejs/plugin-react": "^3.1.0", + "@vitejs/plugin-react-refresh": "^1.3.5", + "babel-loader": "8.2.5", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "cross-env": "7.0.3", + "css-loader": "^4.3.0", + "eslint": "^7.0.0", + "express-interceptor": "1.2.0", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.0", + "less": "^4.1.3", + "less-vars-to-js": "^1.3.0", + "mockjs": "^1.1.0", + "prettier": "^2.0.0", + "react-router": "5.2.0", + "react-router-dom": "5.0.1", + "style-loader": "^3.2.2", + "ts-loader": "^9.3.1", + "tsconfig-paths-webpack-plugin": "^4.0.1", + "typescript": "^4.2.3", + "url-loader": "^4.1.1", + "vite": "^4.2.1", + "vite-plugin-mock": "^2.9.6", + "vite-plugin-require-transform": "^1.0.12" + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/america.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/america.svg new file mode 100644 index 00000000..8cab835c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/america.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/china.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/china.svg new file mode 100644 index 00000000..4e3dbbfe --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/china.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/favicon.ico b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/favicon.ico new file mode 100644 index 00000000..7ca2170e Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/favicon.ico differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo.svg new file mode 100644 index 00000000..ce7afe5c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo.svg @@ -0,0 +1,24 @@ + + + + Created with Sketch. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@128.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@128.png new file mode 100644 index 00000000..5e218d8c Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@128.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@144.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@144.png new file mode 100644 index 00000000..347e0dfe Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@144.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@152.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@152.png new file mode 100644 index 00000000..900dc3c7 Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@152.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@192.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@192.png new file mode 100644 index 00000000..342ab1e9 Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@192.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@384.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@384.png new file mode 100644 index 00000000..c487f36b Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@384.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@512.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@512.png new file mode 100644 index 00000000..c2b44dc8 Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@512.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@72.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@72.png new file mode 100644 index 00000000..126e5efe Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@72.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@96.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@96.png new file mode 100644 index 00000000..de9a9fba Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/logo/logo@96.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/portugal.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/portugal.svg new file mode 100644 index 00000000..a535e696 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/portugal.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/vite.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/assets/react.svg b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/DropOption.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/DropOption.js new file mode 100644 index 00000000..454409f3 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/DropOption.js @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { BarsOutlined, DownOutlined } from '@ant-design/icons'; +import { Dropdown, Button, Menu } from 'antd'; + +const DropOption = ({ onMenuClick, menuOptions = [], buttonStyle, dropdownProps }) => { + const menu = menuOptions.map(item => {item.name}); + return ( + {menu}} {...dropdownProps}> + + + ); +}; + +DropOption.propTypes = { + onMenuClick: PropTypes.func, + menuOptions: PropTypes.array.isRequired, + buttonStyle: PropTypes.object, + dropdownProps: PropTypes.object, +}; + +export default DropOption; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/package.json new file mode 100644 index 00000000..a15afe7a --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/DropOption/package.json @@ -0,0 +1,6 @@ +{ + "name": "DropOption", + "version": "0.0.0", + "private": true, + "main": "DropOption.js" +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.js new file mode 100644 index 00000000..9b500b18 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { Editor } from 'react-draft-wysiwyg'; +import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; +import styles from './Editor.module.less'; + +const DraftEditor = props => { + return ( + + ); +}; + +export default DraftEditor; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.module.less new file mode 100644 index 00000000..2cea380a --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/Editor.module.less @@ -0,0 +1,106 @@ +.wrapper { + height: 500px; + + :global { + .rdw-dropdownoption-default { + padding: 6px; + } + + .rdw-dropdown-optionwrapper { + box-sizing: content-box; + width: 100%; + border-radius: 0 0 2px 2px; + &:hover { + box-shadow: none; + } + } + + .rdw-inline-wrapper { + flex-wrap: wrap; + margin-bottom: 0; + + .rdw-option-wrapper { + margin-bottom: 6px; + } + } + + .rdw-option-active { + box-shadow: 1px 1px 0 #e8e8e8 inset; + } + + .rdw-colorpicker-option { + box-shadow: none; + } + + .rdw-colorpicker-modal, + .rdw-embedded-modal, + .rdw-emoji-modal, + .rdw-image-modal, + .rdw-link-modal { + box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); + } + + .rdw-colorpicker-modal, + .rdw-embedded-modal, + .rdw-link-modal { + height: auto; + } + + .rdw-emoji-modal { + width: 214px; + } + + .rdw-colorpicker-modal { + width: auto; + } + + .rdw-embedded-modal-btn, + .rdw-image-modal-btn, + .rdw-link-modal-btn { + height: 32px; + margin-top: 12px; + } + + .rdw-embedded-modal-input, + .rdw-embedded-modal-size-input, + .rdw-link-modal-input { + padding: 2px 6px; + height: 32px; + } + + .rdw-dropdown-selectedtext { + color: #000; + } + + .rdw-dropdown-wrapper, + .rdw-option-wrapper { + min-width: 36px; + transition: all 0.2s ease; + height: 30px; + + &:active { + box-shadow: 1px 1px 0 #e8e8e8 inset; + } + + &:hover { + box-shadow: 1px 1px 0 #e8e8e8; + } + } + + .rdw-dropdown-wrapper { + min-width: 60px; + } + + .rdw-editor-main { + box-sizing: border-box; + } + } + + .editor { + border: 1px solid #f1f1f1; + padding: 5px; + border-radius: 2px; + height: auto; + min-height: 200px; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/package.json new file mode 100644 index 00000000..3ea063e3 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Editor/package.json @@ -0,0 +1,6 @@ +{ + "name": "Editor", + "version": "0.0.0", + "private": true, + "main": "Editor.js" +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.d.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.d.ts new file mode 100644 index 00000000..075fa789 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.d.ts @@ -0,0 +1,21 @@ +import React from 'react'; +import { TooltipProps } from 'antd/lib/tooltip'; + +export interface EllipsisTooltipProps extends TooltipProps { + title?: undefined; + overlayStyle?: undefined; +} + +export interface EllipsisProps { + tooltip?: boolean | EllipsisTooltipProps; + length?: number; + lines?: number; + style?: React.CSSProperties; + className?: string; + fullWidthRecognition?: boolean; +} + +export function getStrFullLength(str: string): number; +export function cutStrByFullLength(str: string, maxLength: number): string; + +export default class Ellipsis extends React.Component {} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.js new file mode 100644 index 00000000..1fa1936c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.js @@ -0,0 +1,262 @@ +import React, { Component } from 'react'; +import { Tooltip } from 'antd'; +import classNames from 'classnames'; +import styles from './index.module.less'; + +/* eslint react/no-did-mount-set-state: 0 */ +/* eslint no-param-reassign: 0 */ + +const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined; + +const TooltipOverlayStyle = { + overflowWrap: 'break-word', + wordWrap: 'break-word', +}; + +export const getStrFullLength = (str = '') => + str.split('').reduce((pre, cur) => { + const charCode = cur.charCodeAt(0); + if (charCode >= 0 && charCode <= 128) { + return pre + 1; + } + return pre + 2; + }, 0); + +export const cutStrByFullLength = (str = '', maxLength) => { + let showLength = 0; + return str.split('').reduce((pre, cur) => { + const charCode = cur.charCodeAt(0); + if (charCode >= 0 && charCode <= 128) { + showLength += 1; + } else { + showLength += 2; + } + if (showLength <= maxLength) { + return pre + cur; + } + return pre; + }, ''); +}; + +const getTooltip = ({ tooltip, overlayStyle, title, children }) => { + if (tooltip) { + const props = tooltip === true ? { overlayStyle, title } : { ...tooltip, overlayStyle, title }; + return {children}; + } + return children; +}; + +const EllipsisText = ({ text, length, tooltip, fullWidthRecognition, ...other }) => { + if (typeof text !== 'string') { + throw new Error('Ellipsis children must be string.'); + } + const textLength = fullWidthRecognition ? getStrFullLength(text) : text.length; + if (textLength <= length || length < 0) { + return {text}; + } + const tail = '...'; + let displayText; + if (length - tail.length <= 0) { + displayText = ''; + } else { + displayText = fullWidthRecognition ? cutStrByFullLength(text, length) : text.slice(0, length); + } + + const spanAttrs = tooltip ? {} : { ...other }; + return getTooltip({ + tooltip, + overlayStyle: TooltipOverlayStyle, + title: text, + children: ( + + {displayText} + {tail} + + ), + }); +}; + +export default class Ellipsis extends Component { + state = { + text: '', + targetCount: 0, + }; + + componentDidMount() { + if (this.node) { + this.computeLine(); + } + } + + componentDidUpdate(perProps) { + const { lines } = this.props; + if (lines !== perProps.lines) { + this.computeLine(); + } + } + + computeLine = () => { + const { lines } = this.props; + if (lines && !isSupportLineClamp) { + const text = this.shadowChildren.innerText || this.shadowChildren.textContent; + const lineHeight = parseInt(getComputedStyle(this.root).lineHeight, 10); + const targetHeight = lines * lineHeight; + this.content.style.height = `${targetHeight}px`; + const totalHeight = this.shadowChildren.offsetHeight; + const shadowNode = this.shadow.firstChild; + + if (totalHeight <= targetHeight) { + this.setState({ + text, + targetCount: text.length, + }); + return; + } + + // bisection + const len = text.length; + const mid = Math.ceil(len / 2); + + const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode); + + this.setState({ + text, + targetCount: count, + }); + } + }; + + bisection = (th, m, b, e, text, shadowNode) => { + const suffix = '...'; + let mid = m; + let end = e; + let begin = b; + shadowNode.innerHTML = text.substring(0, mid) + suffix; + let sh = shadowNode.offsetHeight; + + if (sh <= th) { + shadowNode.innerHTML = text.substring(0, mid + 1) + suffix; + sh = shadowNode.offsetHeight; + if (sh > th || mid === begin) { + return mid; + } + begin = mid; + if (end - begin === 1) { + mid = 1 + begin; + } else { + mid = Math.floor((end - begin) / 2) + begin; + } + return this.bisection(th, mid, begin, end, text, shadowNode); + } + if (mid - 1 < 0) { + return mid; + } + shadowNode.innerHTML = text.substring(0, mid - 1) + suffix; + sh = shadowNode.offsetHeight; + if (sh <= th) { + return mid - 1; + } + end = mid; + mid = Math.floor((end - begin) / 2) + begin; + return this.bisection(th, mid, begin, end, text, shadowNode); + }; + + handleRoot = n => { + this.root = n; + }; + + handleContent = n => { + this.content = n; + }; + + handleNode = n => { + this.node = n; + }; + + handleShadow = n => { + this.shadow = n; + }; + + handleShadowChildren = n => { + this.shadowChildren = n; + }; + + render() { + const { text, targetCount } = this.state; + const { children, lines, length, className, tooltip, fullWidthRecognition, ...restProps } = this.props; + + const cls = classNames(styles.ellipsis, className, { + [styles.lines]: lines && !isSupportLineClamp, + [styles.lineClamp]: lines && isSupportLineClamp, + }); + + if (!lines && !length) { + return ( + + {children} + + ); + } + + // length + if (!lines) { + return ( + + ); + } + + const id = `antd-pro-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`; + + // support document.body.style.webkitLineClamp + if (isSupportLineClamp) { + const style = `#${id}{-webkit-line-clamp:${lines};-webkit-box-orient: vertical;}`; + + const node = ( +
+ + {children} +
+ ); + + return getTooltip({ + tooltip, + overlayStyle: TooltipOverlayStyle, + title: children, + children: node, + }); + } + + const childNode = ( + + {targetCount > 0 && text.substring(0, targetCount)} + {targetCount > 0 && targetCount < text.length && '...'} + + ); + + return ( +
+
+ {getTooltip({ + tooltip, + overlayStyle: TooltipOverlayStyle, + title: text, + children: childNode, + })} +
+ {children} +
+
+ {text} +
+
+
+ ); + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.md b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.md new file mode 100644 index 00000000..1875b17c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.md @@ -0,0 +1,17 @@ +--- +title: Ellipsis +subtitle: 文本自动省略号 +cols: 1 +order: 10 +--- + +文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 + +## API + +| 参数 | 说明 | 类型 | 默认值 | +| -------------------- | ------------------------------------------------ | ------- | ------ | +| tooltip | 移动到文本展示完整内容的提示 | boolean | - | +| length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - | +| lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1` | +| fullWidthRecognition | 是否将全角字符的长度视为 2 来计算字符串长度 | boolean | - | diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.module.less new file mode 100644 index 00000000..3c0360c1 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.module.less @@ -0,0 +1,24 @@ +.ellipsis { + display: inline-block; + width: 100%; + overflow: hidden; + word-break: break-all; +} + +.lines { + position: relative; + .shadow { + position: absolute; + z-index: -999; + display: block; + color: transparent; + opacity: 0; + } +} + +.lineClamp { + position: relative; + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.test.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.test.js new file mode 100644 index 00000000..4d057b24 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Ellipsis/index.test.js @@ -0,0 +1,13 @@ +import { getStrFullLength, cutStrByFullLength } from './index'; + +describe('test calculateShowLength', () => { + it('get full length', () => { + expect(getStrFullLength('一二,a,')).toEqual(8); + }); + it('cut str by full length', () => { + expect(cutStrByFullLength('一二,a,', 7)).toEqual('一二,a'); + }); + it('cut str when length small', () => { + expect(cutStrByFullLength('一22三', 5)).toEqual('一22'); + }); +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.js new file mode 100644 index 00000000..ab54eed2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styles from './FilterItem.module.less'; + +const FilterItem = ({ label = '', children }) => { + const labelArray = label.split(''); + return ( +
+ {labelArray.length > 0 && ( +
+ {labelArray.map((item, index) => ( + + {item} + + ))} +
+ )} +
{children}
+
+ ); +}; + +FilterItem.propTypes = { + label: PropTypes.string, + children: PropTypes.element.isRequired, +}; + +export default FilterItem; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.module.less new file mode 100644 index 00000000..c2e98ec5 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/FilterItem.module.less @@ -0,0 +1,17 @@ +.filterItem { + display: flex; + justify-content: space-between; + + .labelWrap { + width: 64px; + line-height: 28px; + margin-right: 12px; + justify-content: space-between; + display: flex; + overflow: hidden; + } + + .item { + flex: 1; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/package.json new file mode 100644 index 00000000..b266204f --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/FilterItem/package.json @@ -0,0 +1,6 @@ +{ + "name": "FilterItem", + "version": "0.0.0", + "private": true, + "main": "FilterItem.js" +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.d.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.d.ts new file mode 100644 index 00000000..efde77db --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.d.ts @@ -0,0 +1,14 @@ +import React from 'react'; +export interface GlobalFooterProps { + links?: Array<{ + key?: string; + title: React.ReactNode; + href: string; + blankTarget?: boolean; + }>; + copyright?: React.ReactNode; + style?: React.CSSProperties; + className?: string; +} + +export default class GlobalFooter extends React.Component {} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.js new file mode 100644 index 00000000..f912e5ee --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.js @@ -0,0 +1,23 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './index.module.less'; + +const GlobalFooter = ({ className, links, copyright }) => { + const clsString = classNames(styles.globalFooter, className); + return ( +
+ {links && ( +
+ {links.map(link => ( + + {link.title} + + ))} +
+ )} + {copyright &&
{copyright}
} +
+ ); +}; + +export default GlobalFooter; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.md b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.md new file mode 100644 index 00000000..5d6058f6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.md @@ -0,0 +1,15 @@ +--- +title: GlobalFooter +subtitle: 全局页脚 +cols: 1 +order: 7 +--- + +页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。 + +## API + +| 参数 | 说明 | 类型 | 默认值 | +| --------- | -------- | ---------------------------------------------------------------- | ------ | +| links | 链接数据 | array<{ title: ReactNode, href: string, blankTarget?: boolean }> | - | +| copyright | 版权信息 | ReactNode | - | diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.module.less new file mode 100644 index 00000000..c9d375c2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/GlobalFooter/index.module.less @@ -0,0 +1,29 @@ +@import '../../../../node_modules/antd/lib/style/themes/default.less'; + +.globalFooter { + margin: 48px 0 24px 0; + padding: 0 16px; + text-align: center; + + .links { + margin-bottom: 8px; + + a { + color: @text-color-secondary; + transition: all 0.3s; + + &:not(:last-child) { + margin-right: 40px; + } + + &:hover { + color: @text-color; + } + } + } + + .copyright { + color: @text-color-secondary; + font-size: @font-size-base; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.js new file mode 100644 index 00000000..0b410e62 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.js @@ -0,0 +1,51 @@ +import React, { Fragment } from 'react'; +import { Breadcrumb } from 'antd'; +import { Link } from 'react-router-dom'; +import { t } from 'utils/intl'; +import iconMap from 'utils/iconMap'; + +import { pathToRegexp } from 'path-to-regexp'; +import { queryAncestors } from 'utils'; +import styles from './Bread.module.less'; +import { withRouter } from 'react-router-dom'; + +function Bread({ routeList, history }) { + const generateBreadcrumbs = paths => { + return paths.map((item, key) => { + const content = item && ( + + {item.icon && {iconMap[item.icon]}} + {item.name} + + ); + + return ( + item && ( + + {paths.length - 1 !== key ? {content} : content} + + ) + ); + }); + }; + + // Find a route that matches the pathname. + const currentRoute = routeList.find(_ => _.route && pathToRegexp(_.route).exec(history.location.pathname)); + + // Find the breadcrumb navigation of the current route match and all its ancestors. + const paths = + history.location.pathname === '/' + ? [routeList[0]] + : currentRoute + ? queryAncestors(routeList, currentRoute, 'breadcrumbParentId').reverse() + : [ + { + id: 404, + name: t`Not Found`, + }, + ]; + + return {generateBreadcrumbs(paths)}; +} + +export default withRouter(Bread); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.module.less new file mode 100644 index 00000000..29f247b2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Bread.module.less @@ -0,0 +1,16 @@ +.bread { + margin-bottom: 24px !important; + + :global { + .ant-breadcrumb { + display: flex; + align-items: center; + } + } +} + +@media (max-width: 767px) { + .bread { + margin-bottom: 12px !important; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.js new file mode 100644 index 00000000..a1f86941 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.js @@ -0,0 +1,116 @@ +import React, { Fragment } from 'react'; +import { Menu, Layout, Avatar, Popover, List } from 'antd'; +import { Ellipsis } from 'components'; +import { RightOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; +import { Trans } from 'utils/intl'; +import { getLocale, setLocale } from 'utils'; +import moment from 'moment'; +import classnames from 'classnames'; +import config from 'config'; +import styles from './Header.module.less'; + +const { SubMenu } = Menu; + +function Header({ fixed, avatar, username, collapsed, notifications, onCollapseChange, onAllNotificationsRead }) { + const rightContent = [ + + + + Hi, + + {username} + + + } + > + , + ]; + + if (config.i18n) { + const { languages } = config.i18n; + const language = getLocale(); + const currentLanguage = languages.find(item => item.key === language); + + rightContent.unshift( + { + setLocale(data.key); + }} + mode="horizontal" + > + }> + {languages.map(item => ( + + + {item.title} + + ))} + + + ); + } + + rightContent.unshift( + document.querySelector('#primaryLayout')} + content={ +
+ You have viewed all notifications., + }} + renderItem={item => ( + + + {item.title} + + } + description={moment(item.date).fromNow()} + /> + + + )} + /> + {notifications.length ? ( +
+ Clear notifications +
+ ) : null} +
+ } + > + {/* + + */} +
+ ); + + return ( + +
+ {collapsed ? : } +
+
{rightContent}
+
+ ); +} + +export default Header; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.module.less new file mode 100644 index 00000000..4b487d66 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Header.module.less @@ -0,0 +1,154 @@ +@import '../../themes/vars.less'; + +.header { + padding: 0 !important; + box-shadow: @shadow-2; + position: relative; + display: flex; + justify-content: space-between; + height: 72px !important; + z-index: 9; + align-items: center; + background-color: #fff !important; + + &.fixed { + position: fixed; + top: 0; + right: 0; + width: ~'calc(100% - 256px)'; + z-index: 29; + transition: width 0.2s; + + &.collapsed { + width: ~'calc(100% - 80px)'; + } + } + + :global { + .ant-menu-submenu-title { + height: 72px; + } + + .ant-menu-horizontal { + line-height: 72px; + + & > .ant-menu-submenu:hover { + color: @primary-color; + background-color: @hover-color; + } + } + + .ant-menu { + border-bottom: none; + height: 72px; + } + + .ant-menu-horizontal > .ant-menu-submenu { + top: 0; + margin-top: 0; + } + + .ant-menu-horizontal > .ant-menu-item, + .ant-menu-horizontal > .ant-menu-submenu { + border-bottom: none; + } + + .ant-menu-horizontal > .ant-menu-item-active, + .ant-menu-horizontal > .ant-menu-item-open, + .ant-menu-horizontal > .ant-menu-item-selected, + .ant-menu-horizontal > .ant-menu-item:hover, + .ant-menu-horizontal > .ant-menu-submenu-active, + .ant-menu-horizontal > .ant-menu-submenu-open, + .ant-menu-horizontal > .ant-menu-submenu-selected, + .ant-menu-horizontal > .ant-menu-submenu:hover { + border-bottom: none; + } + } + + .rightContainer { + display: flex; + align-items: center; + } + + .button { + width: 72px; + height: 72px; + line-height: 72px; + text-align: center; + font-size: 18px; + cursor: pointer; + transition: @transition-ease-in; + + &:hover { + color: @primary-color; + background-color: @hover-color; + } + } +} + +.iconButton { + width: 48px; + height: 48px; + display: flex !important; + justify-content: center; + align-items: center; + border-radius: 24px; + cursor: pointer; + .background-hover(); + + &:hover { + .iconFont { + color: @primary-color; + } + } + + & + .iconButton { + margin-left: 8px; + } + + .iconFont { + color: #b2b0c7; + font-size: 24px; + } +} + +.notification { + padding: 24px 0; + width: 320px; + .notificationItem { + transition: all 0.3s; + padding: 12px 24px; + cursor: pointer; + &:hover { + background-color: @hover-color; + } + } + .clearButton { + text-align: center; + height: 48px; + line-height: 48px; + cursor: pointer; + .background-hover(); + } +} + +.notificationPopover { + :global { + .ant-popover-inner-content { + padding: 0; + } + .ant-popover-arrow { + display: none; + } + .ant-list-item-content { + flex: 0; + margin-left: 16px; + } + } +} + +@media (max-width: 767px) { + .header { + width: 100% !important; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.js new file mode 100644 index 00000000..5a8bab0b --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.js @@ -0,0 +1,64 @@ +import React from 'react'; +import { Switch, Layout } from 'antd'; +import { t } from 'utils/intl'; +import { Trans } from 'utils/intl'; +import { BulbOutlined } from '@ant-design/icons'; +import ScrollBar from '../ScrollBar'; +import config from 'utils/config'; +import SiderMenu from './SiderMenu'; +import styles from './Sider.module.less'; + +function Sider({ menus, theme, isMobile, collapsed, onThemeChange, onCollapseChange }) { + return ( + +
+
+ logo + {!collapsed &&

{config.siteName}

} +
+
+ +
+ + + +
+ {!collapsed && ( +
+ + + Switch Theme + + +
+ )} +
+ ); +} + +export default Sider; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.module.less new file mode 100644 index 00000000..28dfa1fc --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/Sider.module.less @@ -0,0 +1,114 @@ +@import '../../themes/vars.less'; + +.sider { + box-shadow: fade(@primary-color, 10%) 0 0 28px 0; + z-index: 10; + :global { + .ant-layout-sider-children { + display: flex; + flex-direction: column; + justify-content: space-between; + } + } +} + +.brand { + z-index: 1; + height: 72px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 24px; + box-shadow: 0 1px 9px -3px rgba(0, 0, 0, 0.2); + .logo { + display: flex; + align-items: center; + justify-content: center; + + img { + width: 36px; + margin-right: 8px; + } + + h1 { + vertical-align: text-bottom; + font-size: 16px; + text-transform: uppercase; + display: inline-block; + font-weight: 700; + color: @primary-color; + white-space: nowrap; + margin-bottom: 0; + .text-gradient(); + + :local { + animation: fadeRightIn 300ms @ease-in-out; + animation-fill-mode: both; + } + } + } +} + +.menuContainer { + height: ~'calc(100vh - 120px)'; + overflow-x: hidden; + flex: 1; + padding: 24px 0; + + &::-webkit-scrollbar-thumb { + background-color: transparent; + } + + &:hover { + &::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + } + } + + :global { + .ant-menu-inline { + border-right: none; + } + } +} + +.darkTheme { + color: rgb(102, 102, 102); +} + +.switchTheme { + width: 100%; + height: 48px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 16px; + overflow: hidden; + transition: all 0.3s; + + span { + white-space: nowrap; + overflow: hidden; + font-size: 12px; + } + + :global { + .anticon { + min-width: 14px; + margin-right: 4px; + font-size: 14px; + } + } +} + +@keyframes fadeLeftIn { + 0% { + transform: translateX(5px); + opacity: 0; + } + + 100% { + transform: translateX(0); + opacity: 1; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/SiderMenu.tsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/SiderMenu.tsx new file mode 100644 index 00000000..80ddaa29 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/SiderMenu.tsx @@ -0,0 +1,97 @@ +import React, { useState, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { Menu } from 'antd'; +import { NavLink } from 'react-router-dom'; +import { pathToRegexp } from 'path-to-regexp'; +import { arrayToTree, queryAncestors } from '../../utils'; +import iconMap from '../../utils/iconMap'; +import store from 'store'; +import { getStore } from '../../models/app-model'; +import { withRouter } from 'react-router-dom'; + +const { SubMenu } = Menu; + +function SiderMenu({ theme, menus, history, isMobile, onCollapseChange }) { + const st = getStore(); + + const [openKeys, setOpenKeys] = useState(store.get('openKeys') || []); + + const onOpenChange = openKeys => { + const rootSubmenuKeys = menus.filter(_ => !_.menuParentId).map(_ => _.id); + + const latestOpenKey = openKeys.find(key => openKeys.indexOf(key) === -1); + + let newOpenKeys = openKeys; + if (rootSubmenuKeys.indexOf(latestOpenKey) !== -1) { + newOpenKeys = latestOpenKey ? [latestOpenKey] : []; + } + + setOpenKeys(newOpenKeys); + + store.set('openKeys', newOpenKeys); + }; + + const generateMenus = data => { + return data.map(item => { + if (item.children) { + return ( + + {item.icon && iconMap[item.icon]} + {item.name} + + } + > + {generateMenus(item.children)} + + ); + } + return ( + + + {item.icon && iconMap[item.icon]} + {item.name} + + + ); + }); + }; + + // Generating tree-structured data for menu content. + const menuTree = arrayToTree(menus, 'id', 'menuParentId'); + + // Find a menu that matches the pathname. + const currentMenu = menus.find(_ => _.route && pathToRegexp(_.route).exec(history.location.pathname)); + + // Find the key that should be selected according to the current menu. + const selectedKeys = currentMenu ? queryAncestors(menus, currentMenu, 'menuParentId').map(_ => _.id) : []; + + const menuProps = st.collapsed + ? {} + : { + openKeys: openKeys, + }; + + return ( + { + onCollapseChange(true); + } + : undefined + } + {...menuProps} + > + {generateMenus(menuTree)} + + ); +} + +export default withRouter(SiderMenu); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/index.js new file mode 100644 index 00000000..934351a8 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Layout/index.js @@ -0,0 +1,6 @@ +import Header from './Header'; +import Menu from './SiderMenu'; +import Bread from './Bread'; +import Sider from './Sider'; + +export { Header, Menu, Bread, Sider }; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.js new file mode 100644 index 00000000..bf2f0c3c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import styles from './Loader.module.less'; + +const Loader = ({ spinning = false, fullScreen }) => { + return ( +
+
+
+
LOADING
+
+
+ ); +}; + +Loader.propTypes = { + spinning: PropTypes.bool, + fullScreen: PropTypes.bool, +}; + +export default Loader; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.module.less new file mode 100644 index 00000000..11b4d417 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/Loader.module.less @@ -0,0 +1,69 @@ +.loader { + background-color: #fff; + width: 100%; + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 100000; + display: flex; + justify-content: center; + align-items: center; + opacity: 1; + text-align: center; + + &.fullScreen { + position: fixed; + } + + .warpper { + width: 100px; + height: 100px; + display: inline-flex; + flex-direction: column; + justify-content: space-around; + } + + .inner { + width: 40px; + height: 40px; + margin: 0 auto; + text-indent: -12345px; + border-top: 1px solid rgba(0, 0, 0, 0.08); + border-right: 1px solid rgba(0, 0, 0, 0.08); + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + border-left: 1px solid rgba(0, 0, 0, 0.7); + border-radius: 50%; + z-index: 100001; + + :local { + animation: spinner 600ms infinite linear; + } + } + + .text { + width: 100px; + height: 20px; + text-align: center; + font-size: 12px; + letter-spacing: 4px; + color: #000; + } + + &.hidden { + z-index: -1; + opacity: 0; + transition: + opacity 1s ease 0.5s, + z-index 0.1s ease 1.5s; + } +} +@keyframes spinner { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/package.json new file mode 100644 index 00000000..b1718b55 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Loader/package.json @@ -0,0 +1,6 @@ +{ + "name": "Loader", + "version": "0.0.0", + "private": true, + "main": "Loader.js" +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.js new file mode 100644 index 00000000..36c7f12e --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Loader from '../Loader'; +import styles from './Page.module.less'; + +export default class Page extends Component { + render() { + const { className, children, loading = false, inner = false } = this.props; + const loadingStyle = { + height: 'calc(100vh - 184px)', + overflow: 'hidden', + }; + return ( +
+ {loading ? : ''} + {children} +
+ ); + } +} + +Page.propTypes = { + className: PropTypes.string, + children: PropTypes.node, + loading: PropTypes.bool, + inner: PropTypes.bool, +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.module.less new file mode 100644 index 00000000..fa8713aa --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/Page.module.less @@ -0,0 +1,16 @@ +@import '../../themes/vars.less'; + +.contentInner { + background: #fff; + padding: 24px; + box-shadow: @shadow-1; + min-height: ~'calc(100vh - 230px)'; + position: relative; +} + +@media (max-width: 767px) { + .contentInner { + padding: 12px; + min-height: ~'calc(100vh - 160px)'; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/package.json b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/package.json new file mode 100644 index 00000000..0b7aa2d8 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/Page/package.json @@ -0,0 +1,6 @@ +{ + "name": "Page", + "version": "0.0.0", + "private": true, + "main": "Page.js" +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.js new file mode 100644 index 00000000..3b171ce6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.js @@ -0,0 +1,5 @@ +import ScrollBar from 'react-perfect-scrollbar'; +import 'react-perfect-scrollbar/dist/css/styles.css'; +import './index.less'; + +export default ScrollBar; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.less new file mode 100644 index 00000000..31e149bc --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/ScrollBar/index.less @@ -0,0 +1,31 @@ +:global { + .ps--active-x > .ps__rail-x, + .ps--active-y > .ps__rail-y { + background-color: transparent; + } + + .ps__rail-x:hover > .ps__thumb-x, + .ps__rail-x:focus > .ps__thumb-x { + height: 8px; + } + + .ps__rail-y:hover > .ps__thumb-y, + .ps__rail-y:focus > .ps__thumb-y { + width: 8px; + } + + .ps__rail-y, + .ps__rail-x { + z-index: 9; + } + + .ps__thumb-y { + width: 4px; + right: 4px; + } + + .ps__thumb-x { + height: 4px; + bottom: 4px; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/index.js new file mode 100644 index 00000000..81a96eb2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/components/index.js @@ -0,0 +1,11 @@ +import Editor from './Editor'; +import FilterItem from './FilterItem'; +import DropOption from './DropOption'; +import Loader from './Loader'; +import ScrollBar from './ScrollBar'; +import GlobalFooter from './GlobalFooter'; +import Ellipsis from './Ellipsis'; +import * as MyLayout from './Layout'; +import Page from './Page'; + +export { MyLayout, Editor, GlobalFooter, Ellipsis, FilterItem, DropOption, Loader, Page, ScrollBar }; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.js new file mode 100644 index 00000000..e55716f4 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.js @@ -0,0 +1,29 @@ +import React, { Fragment } from 'react'; +import { Helmet } from 'react-helmet'; +import { queryLayout } from 'utils'; +import config from 'utils/config'; + +import PublicLayout from './PublicLayout'; +import PrimaryLayout from './PrimaryLayout'; +import { withRouter } from 'react-router-dom'; +import './BaseLayout.less'; + +const LayoutMap = { + primary: PrimaryLayout, + public: PublicLayout, +}; + +function BaseLayout({ children, location }) { + const Container = LayoutMap[queryLayout(config.layouts, location.pathname)]; + + return ( + + + {config.siteName} + + {children} + + ); +} + +export default withRouter(BaseLayout); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.less new file mode 100644 index 00000000..ab1fdbd7 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/BaseLayout.less @@ -0,0 +1,76 @@ +@import '../themes/vars.less'; +@import '../themes/index.less'; + +:global { + #nprogress { + pointer-events: none; + + .bar { + background: @primary-color; + position: fixed; + z-index: 2048; + top: 0; + left: 0; + right: 0; + width: 100%; + height: 2px; + } + + .peg { + display: block; + position: absolute; + right: 0; + width: 100px; + height: 100%; + box-shadow: + 0 0 10px @primary-color, + 0 0 5px @primary-color; + opacity: 1; + transform: rotate(3deg) translate(0, -4px); + } + + .spinner { + display: block; + position: fixed; + z-index: 1031; + top: 15px; + right: 15px; + } + + .spinner-icon { + width: 18px; + height: 18px; + box-sizing: border-box; + border: solid 2px transparent; + border-top-color: @primary-color; + border-left-color: @primary-color; + border-radius: 50%; + + :local { + animation: nprogress-spinner 400ms linear infinite; + } + } + } + + .nprogress-custom-parent { + overflow: hidden; + position: relative; + + #nprogress { + .bar, + .spinner { + position: absolute; + } + } + } +} + +@keyframes nprogress-spinner { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.module.less new file mode 100644 index 00000000..d10017fb --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.module.less @@ -0,0 +1,58 @@ +@import '../themes/vars.less'; + +.backTop { + right: 50px; + + :global { + .ant-back-top-content { + background: @primary-color; + opacity: 0.3; + transition: all 0.3s; + box-shadow: 0 0 15px 1px rgba(69, 65, 78, 0.1); + + &:hover { + opacity: 1; + } + } + } +} + +.content { + padding: 24px; + min-height: ~'calc(100% - 72px)'; + // overflow-y: scroll; +} + +.container { + height: 100vh; + flex: 1; + width: ~'calc(100% - 256px)'; + overflow-y: scroll; + overflow-x: hidden; +} + +.footer { + background: #fff; + margin-top: 0; + margin-bottom: 0; + padding-top: 24px; + padding-bottom: 24px; + min-height: 72px; +} + +@media (max-width: 767px) { + .content { + padding: 12px; + } + + .backTop { + right: 20px; + bottom: 20px; + } + + .container { + height: 100vh; + flex: 1; + width: 100%; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.tsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.tsx new file mode 100644 index 00000000..465e1cf0 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PrimaryLayout.tsx @@ -0,0 +1,100 @@ +import React, { useEffect, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { MyLayout, GlobalFooter } from '../components'; +import { BackTop, Layout, Drawer } from 'antd'; + +import { pathToRegexp } from 'path-to-regexp'; +import { getLocale } from '../utils'; +import config from '../utils/config'; +import Error from '../pages/404'; +import styles from './PrimaryLayout.module.less'; +import store from 'store'; +import { getStore } from '../models/app-model'; +import { withRouter } from 'react-router-dom'; + +const { Content } = Layout; +const { Header, Bread, Sider } = MyLayout; + +function PrimaryLayout({ children, location, history }) { + const st = getStore(); + + useEffect(() => { + st.query(history); + }, []); + + const onCollapseChange = collapsed => { + st.handleCollapseChange(collapsed); + }; + + const theme = st.theme; + const collapsed = st.collapsed; + const notifications = st.notifications; + const user = store.get('user') || {}; + const permissions = st.permissions; + const routeList = st.routeList || []; + + const lang = getLocale(); + + const newRouteList = + lang !== 'en' + ? routeList.map(item => { + const { name, ...other } = item; + return { + ...other, + name: (item[lang] || {}).name || name, + }; + }) + : routeList; + + // Find a route that matches the pathname. + const currentRoute = newRouteList.find(_ => _.route && pathToRegexp(`${_.route}`).exec(location.pathname)); + + // Query whether you have permission to enter this page + const hasPermission = currentRoute ? permissions.visit.includes(currentRoute.id) : false; + + // MenuParentId is equal to -1 is not a available menu. + const menus = newRouteList.filter(_ => _.menuParentId !== '-1'); + + const headerProps = { + menus, + collapsed, + notifications, + onCollapseChange, + avatar: user.avatar, + username: user.username, + fixed: config.fixedHeader, + onAllNotificationsRead: () => { + st.allNotificationsRead(); + }, + }; + + const siderProps = { + theme, + menus, + collapsed, + onCollapseChange, + onThemeChange: theme => { + st.handleThemeChange(theme); + }, + }; + + return ( + + + + +
+
+ + + {hasPermission ? children : } + + document.querySelector('#primaryLayout')} /> + +
+
+
+ ); +} + +export default withRouter(PrimaryLayout); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PublicLayout.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PublicLayout.js new file mode 100644 index 00000000..962ebb4e --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/PublicLayout.js @@ -0,0 +1,3 @@ +export default ({ children }) => { + return children; +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/index.js new file mode 100644 index 00000000..5b6b76c7 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/layouts/index.js @@ -0,0 +1,34 @@ +import React, { Suspense } from 'react'; +import { ConfigProvider } from 'antd'; +import { IntlProvider } from 'react-intl'; +import { getLocale } from '../utils'; +import BaseLayout from './BaseLayout'; +import { Route, withRouter } from 'react-router-dom'; +import { getRoutes } from '../pages/routes'; +import { getLangResource } from '../utils/intl'; + +function Layout() { + let language = getLocale(); + + let langResource = getLangResource(language); + + const routes = getRoutes(); + + return ( + + + + + + {routes[0].childRoutes.map(item => { + return ; + })} + + + + + + ); +} + +export default withRouter(Layout); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/en/messages.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/en/messages.js new file mode 100644 index 00000000..c6dbd55a --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/en/messages.js @@ -0,0 +1,54 @@ +export default { + '/dashboard': '/dashboard', + 'Add Param': 'Add Param', + Address: 'Address', + Age: 'Age', + 'Are you sure delete this record?': 'Are you sure delete this record?', + Author: 'Author', + Avatar: 'Avatar', + Categories: 'Categories', + 'Clear notifications': 'Clear notifications', + Comments: 'Comments', + Create: 'Create', + 'Create User': 'Create User', + CreateTime: 'CreateTime', + Dark: 'Dark', + Delete: 'Delete', + Email: 'Email', + Female: 'Female', + Gender: 'Gender', + 'Hi,': 'Hi,', + Image: 'Image', + Light: 'Light', + Male: 'Male', + Name: 'Name', + NickName: 'NickName', + 'Not Found': 'Not Found', + Operation: 'Operation', + Params: 'Params', + Password: 'Password', + Phone: 'Phone', + 'Pick an address': 'Pick an address', + 'Please pick an address': 'Please pick an address', + Published: 'Published', + 'Publish Date': 'Publish Date', + Reset: 'Reset', + Search: 'Search', + 'Search Name': 'Search Name', + Send: 'Send', + 'Sign in': 'Sign in', + 'Sign out': 'Sign out', + 'Switch Theme': 'Switch Theme', + Tags: 'Tags', + 'The input is not valid E-mail!': 'The input is not valid E-mail!', + 'The input is not valid phone!': 'The input is not valid phone!', + Title: 'Title', + Total: 'Total {total} Items', + Unpublished: 'Unpublished', + Update: 'Update', + 'Update User': 'Update User', + Username: 'Username', + Views: 'Views', + Visibility: 'Visibility', + 'You have viewed all notifications.': 'You have viewed all notifications.', +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/pt-br/messages.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/pt-br/messages.js new file mode 100644 index 00000000..bbbab040 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/pt-br/messages.js @@ -0,0 +1,54 @@ +export default { + '/dashboard': '/dashboard', + 'Add Param': 'Add Parametro', + Address: 'Endereço', + Age: 'Ano', + 'Are you sure delete this record?': 'Tem certeza de excluir este registro?', + Author: 'Autor', + Avatar: 'Avatar', + Categories: 'Categorias', + 'Clear notifications': 'limpar notificações', + Comments: 'Comentarios', + Create: 'Criar', + 'Create User': 'Criar Usuário', + CreateTime: 'CreateTime', + Dark: 'Escuro', + Delete: 'Deletar', + Email: 'Email', + Female: 'Feminino', + Gender: 'Genero', + 'Hi,': 'Olá,', + Image: 'Imagem', + Light: 'Claro', + Male: 'masculino', + Name: 'Nome', + NickName: 'NickName', + 'Not Found': 'Não Encontrado', + Operation: 'Operation', + Params: 'Parametros', + Password: 'Senha', + Phone: 'Fone', + 'Pick an address': 'Escolha um endereço', + 'Please pick an address': 'Por favor, escolha um endereço', + Published: 'Publicado', + 'Publish Date': 'Data de publicação', + Reset: 'Reset', + Search: 'procurar', + 'Search Name': 'Search Name', + Send: 'Enviar', + 'Sign in': 'Sign in', + 'Sign out': 'Sign out', + 'Switch Theme': 'Trocar tema', + Tags: 'Tags', + 'The input is not valid E-mail!': 'Não é um E-mail valido!', + 'The input is not valid phone!': 'Não é um telefone Valido!', + Title: 'Titulo', + Total: 'Total {total} Items', + Unpublished: 'Não publicado', + Update: 'Atualizar', + 'Update User': 'Atualizar Usuário', + Username: 'Usuário', + Views: 'visualizações', + Visibility: 'Visibilidade', + 'You have viewed all notifications.': 'Você visualizou todas as notificações.', +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/zh/messages.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/zh/messages.js new file mode 100644 index 00000000..fb51ae8c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/locales/zh/messages.js @@ -0,0 +1,54 @@ +export default { + '/dashboard': '/zh/dashboard', + 'Add Param': '添加参数', + Address: '地址', + Age: '年龄', + 'Are you sure delete this record?': '您确定要删除这条记录吗?', + Author: '作者', + Avatar: '头像', + Categories: '类别', + 'Clear notifications': '清空消息', + Comments: '评论数', + Create: '创建', + 'Create User': '创建用户', + CreateTime: '创建时间', + Dark: '暗', + Delete: '删除', + Email: '电子邮件', + Female: '女', + Gender: '性别', + 'Hi,': '你好,', + Image: '图像', + Light: '明', + Male: '男性', + Name: '名字', + NickName: '昵称', + 'Not Found': '未找到', + Operation: '操作', + Params: '参数', + Password: '密码', + Phone: '电话', + 'Pick an address': '选择地址', + 'Please pick an address': '选择地址', + Published: '已发布', + 'Publish Date': '发布日期', + Reset: '重置', + Search: '搜索', + 'Search Name': '搜索名字', + Send: '发送', + 'Sign in': '登录', + 'Sign out': '退出登录', + 'Switch Theme': '切换主题', + Tags: '标签', + 'The input is not valid E-mail!': '输入的电子邮件无效!', + 'The input is not valid phone!': '输入无效的手机!', + Title: '标题', + Total: '总共 {total} 条记录', + Unpublished: '未发布', + Update: '更新', + 'Update User': '更新用户', + Username: '用户名', + Views: '浏览数', + Visibility: '可见性', + 'You have viewed all notifications.': '您已查看所有通知', +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/main.jsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/main.jsx new file mode 100644 index 00000000..c285ca91 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/main.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDom from 'react-dom'; +import { BrowserRouter } from 'react-router-dom'; + +import 'antd/dist/antd.css'; + +import Layout from './layouts'; + +ReactDom.render( + + + , + document.getElementsByTagName('body')[0] +); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/models/app-model.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/models/app-model.ts new file mode 100644 index 00000000..f7ae9c81 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/models/app-model.ts @@ -0,0 +1,82 @@ +import { stringify } from 'qs'; +import store from 'store'; +import { pathToRegexp } from 'path-to-regexp'; +import { ROLE_TYPE } from '../utils/constant'; +import { queryLayout } from '../utils'; +import api from '../services'; +import config from '../utils/config'; +// @ts-ignore +const { queryRouteList, queryUserInfo } = api; + +import { createStore } from 'inula'; + +const goDashboard = history => { + if (pathToRegexp(['/', '/login']).exec(window.location.pathname)) { + history.push({ + pathname: '/dashboard', + }); + } +}; + +export const getStore = createStore({ + id: 'app', + state: { + routeList: [], + permissions: { visit: [] }, + locationPathname: '', + locationQuery: {}, + theme: store.get('theme') || 'light', + collapsed: store.get('collapsed') || false, + notifications: [ + { + title: 'New User is registered.', + date: new Date(Date.now() - 10000000), + }, + { + title: 'Application has been approved.', + date: new Date(Date.now() - 50000000), + }, + ], + }, + actions: { + handleThemeChange(state, val) { + store.set('theme', val); + state.theme = val; + }, + + handleCollapseChange(state, val) { + store.set('collapsed', val); + state.collapsed = val; + }, + + allNotificationsRead(state) { + state.notifications = []; + }, + + async query(state, history) { + const locationPathname = state.locationPathname; + const { success, user } = await queryUserInfo(); + if (success && user) { + const { list } = await queryRouteList(); + const { permissions } = user; + let routeList = list; + if (permissions.role === ROLE_TYPE.ADMIN || permissions.role === ROLE_TYPE.DEVELOPER) { + permissions.visit = list.map(item => item.id); + } else { + routeList = list.filter(item => { + const cases = [ + permissions.visit.includes(item.id), + item.mpid ? permissions.visit.includes(item.mpid) || item.mpid === '-1' : true, + item.bpid ? permissions.visit.includes(item.bpid) : true, + ]; + return cases.every(_ => _); + }); + } + state.routeList = routeList; + state.permissions = permissions; + store.set('user', user); + goDashboard(history); + } + }, + }, +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.jsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.jsx new file mode 100644 index 00000000..6720f6ef --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { FrownOutlined } from '@ant-design/icons'; +import { Page } from 'components'; +import styles from './404.module.less'; + +const Error = () => ( + +
+ +

404 Not Found

+
+
+); + +export default Error; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.module.less new file mode 100644 index 00000000..f5300b1f --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/404.module.less @@ -0,0 +1,19 @@ +.error { + color: black; + text-align: center; + position: absolute; + top: 30%; + margin-top: -50px; + left: 50%; + margin-left: -100px; + width: 200px; + + :global .anticon { + font-size: 48px; + margin-bottom: 16px; + } + + h1 { + font-family: cursive; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.js new file mode 100644 index 00000000..955c1643 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table, Tag } from 'antd'; +import Color from 'utils/theme'; +import styles from './browser.module.less'; + +const status = { + 1: { + color: Color.green, + }, + 2: { + color: Color.red, + }, + 3: { + color: Color.blue, + }, + 4: { + color: Color.yellow, + }, +}; + +function Browser({ data }) { + const columns = [ + { + title: 'name', + dataIndex: 'name', + className: styles.name, + }, + { + title: 'percent', + dataIndex: 'percent', + className: styles.percent, + render: (text, it) => {text}%, + }, + ]; + return ; +} + +Browser.propTypes = { + data: PropTypes.array, +}; + +export default Browser; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.module.less new file mode 100644 index 00000000..631ad64f --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/browser.module.less @@ -0,0 +1,7 @@ +.percent { + text-align: right !important; +} + +.name { + text-align: left !important; +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.js new file mode 100644 index 00000000..e5eb8551 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.js @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table, Tag } from 'antd'; +import Color from '../../../utils/theme'; +import styles from './comments.module.less'; + +const status = { + 1: { + color: Color.green, + text: 'APPROVED', + }, + 2: { + color: Color.yellow, + text: 'PENDING', + }, + 3: { + color: Color.red, + text: 'REJECTED', + }, +}; + +function Comments({ data }) { + const columns = [ + { + title: 'avatar', + dataIndex: 'avatar', + width: 48, + className: styles.avatarcolumn, + render: text => , + }, + { + title: 'content', + dataIndex: 'content', + render: (text, it) => ( +
+
{it.name}
+

{it.content}

+
+ {status[it.status].text} + {it.date} +
+
+ ), + }, + ]; + return ( +
+
key < 3)} + /> + + ); +} + +Comments.propTypes = { + data: PropTypes.array, +}; + +export default Comments; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.module.less new file mode 100644 index 00000000..f6fa2dec --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/comments.module.less @@ -0,0 +1,43 @@ +@import '../../../themes/vars.less'; + +.comments { + :global .ant-table-thead > tr > th { + background: #fff; + border-bottom: solid 1px @border-color-base; + } + + .avatar { + width: 48px; + height: 48px; + background-position: center; + background-size: cover; + border-radius: 50%; + background: #f8f8f8; + display: inline-block; + } + + .content { + text-align: left; + color: #757575; + } + + .date { + color: #a3a3a3; + line-height: 30px; + } + + .daterow { + display: flex; + justify-content: space-between; + } + + .name { + font-size: 14px; + color: #474747; + text-align: left; + } + + .avatarcolumn { + vertical-align: top; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.js new file mode 100644 index 00000000..06908f3c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.js @@ -0,0 +1,86 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Color from 'utils/theme'; +import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import styles from './completed.module.less'; + +function Completed({ data }) { + return ( +
+
TEAM TOTAL COMPLETED
+ + + { + const { payload } = prop; + return ( +
    + {payload.map((item, key) => ( +
  • + + {item.value} +
  • + ))} +
+ ); + }} + /> + + + + { + const list = content.payload.map((item, key) => ( +
  • + + {`${item.name}:${item.value}`} +
  • + )); + return ( +
    +

    {content.label}

    + {content.payload &&
      {list}
    } +
    + ); + }} + /> + + +
    +
    +
    + ); +} + +Completed.propTypes = { + data: PropTypes.array, +}; + +export default Completed; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.module.less new file mode 100644 index 00000000..4dc79a16 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/completed.module.less @@ -0,0 +1,49 @@ +@import '../../../themes/vars.less'; + +.sales { + .title { + margin-left: 32px; + font-size: 16px; + } +} + +.radiusdot { + width: 12px; + height: 12px; + margin-right: 8px; + border-radius: 50%; + display: inline-block; +} + +.legend { + text-align: right; + color: #999; + font-size: 14px; + + li { + height: 48px; + line-height: 48px; + display: inline-block; + + & + li { + margin-left: 24px; + } + } +} + +.tooltip { + background: #fff; + padding: 20px; + font-size: 14px; + + .tiptitle { + font-weight: 700; + font-size: 16px; + margin-bottom: 8px; + } + + .tipitem { + height: 32px; + line-height: 32px; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.js new file mode 100644 index 00000000..81dea61c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.js @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Color from 'utils/theme'; +import CountUp from 'react-countup'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, ResponsiveContainer } from 'recharts'; +import styles from './cpu.module.less'; + +const countUpProps = { + start: 0, + duration: 2.75, + useEasing: true, + useGrouping: true, + separator: ',', +}; + +function Cpu({ usage = 0, space = 0, cpu = 0, data }) { + return ( +
    +
    +
    +

    usage

    +

    + +

    +
    +
    +

    space

    +

    + +

    +
    +
    +

    cpu

    +

    + +

    +
    +
    + + + + + + + + +
    + ); +} + +Cpu.propTypes = { + data: PropTypes.array, + usage: PropTypes.number, + space: PropTypes.number, + cpu: PropTypes.number, +}; + +export default Cpu; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.module.less new file mode 100644 index 00000000..d23163b1 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/cpu.module.less @@ -0,0 +1,40 @@ +.cpu { + .number { + display: flex; + height: 64px; + justify-content: space-between; + margin-bottom: 32px; + + .item { + text-align: center; + height: 64px; + width: 100%; + position: relative; + + & + .item { + &::before { + content: ''; + display: block; + width: 1px; + height: 40px; + position: absolute; + background: #f5f5f5; + top: 12px; + } + } + + p { + color: #757575; + + &:first-child { + font-size: 16px; + } + + &:last-child { + font-size: 20px; + font-weight: 700; + } + } + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/index.js new file mode 100644 index 00000000..f52fc524 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/index.js @@ -0,0 +1,12 @@ +import NumberCard from './numberCard'; +import Quote from './quote'; +import Sales from './sales'; +import Weather from './weather'; +import RecentSales from './recentSales'; +import Comments from './comments'; +import Completed from './completed'; +import Browser from './browser'; +import Cpu from './cpu'; +import User from './user'; + +export { NumberCard, Quote, Sales, Weather, RecentSales, Comments, Completed, Browser, Cpu, User }; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.js new file mode 100644 index 00000000..204c9faf --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.js @@ -0,0 +1,32 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Card } from 'antd'; +import CountUp from 'react-countup'; +import iconMap from 'utils/iconMap'; +import styles from './numberCard.module.less'; + +function NumberCard({ icon, color, title, number, countUp }) { + return ( + + + {iconMap[icon]} + +
    +

    {title || 'No Title'}

    +

    + +

    +
    +
    + ); +} + +NumberCard.propTypes = { + icon: PropTypes.string, + color: PropTypes.string, + title: PropTypes.string, + number: PropTypes.number, + countUp: PropTypes.object, +}; + +export default NumberCard; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.module.less new file mode 100644 index 00000000..b8c74ca8 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/numberCard.module.less @@ -0,0 +1,33 @@ +@import '../../../themes/vars'; + +.numberCard { + padding: 32px; + margin-bottom: 24px; + cursor: pointer; + + .iconWarp { + font-size: 54px; + float: left; + } + + .content { + width: 100%; + padding-left: 78px; + + .title { + line-height: 16px; + font-size: 16px; + margin-bottom: 8px; + height: 16px; + .text-overflow(); + } + + .number { + line-height: 32px; + font-size: 24px; + height: 32px; + .text-overflow(); + margin-bottom: 0; + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.js new file mode 100644 index 00000000..a0a12d0c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styles from './quote.module.less'; + +function Quote({ name, content, title, avatar }) { + return ( +
    +
    {content}
    +
    +
    +

    -{name}-

    +

    {title}

    +
    +
    +
    +
    + ); +} + +Quote.propTypes = { + name: PropTypes.string, + content: PropTypes.string, + title: PropTypes.string, + avatar: PropTypes.string, +}; + +export default Quote; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.module.less new file mode 100644 index 00000000..7d60b714 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/quote.module.less @@ -0,0 +1,52 @@ +@import '../../../themes/vars'; + +.quote { + color: #fff; + height: 100%; + width: 100%; + padding: 24px; + font-size: 16px; + font-weight: 700; + + .inner { + text-overflow: ellipsis; + word-wrap: normal; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; + overflow: hidden; + text-indent: 24px; + } + + .footer { + position: relative; + margin-top: 14px; + + .description { + width: 100%; + + p { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 64px; + text-align: right; + + &:last-child { + font-weight: 100; + } + } + } + + .avatar { + width: 48px; + height: 48px; + background-position: center; + background-size: cover; + border-radius: 50%; + position: absolute; + right: 0; + top: 0; + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.js new file mode 100644 index 00000000..8ba3d6b6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.js @@ -0,0 +1,60 @@ +import React from 'react'; +import moment from 'moment'; +import PropTypes from 'prop-types'; +import { Table, Tag } from 'antd'; +import Color from '../../../utils/theme'; +import styles from './recentSales.module.less'; + +const status = { + 1: { + color: Color.green, + text: 'SALE', + }, + 2: { + color: Color.yellow, + text: 'REJECT', + }, + 3: { + color: Color.red, + text: 'TAX', + }, + 4: { + color: Color.blue, + text: 'EXTENDED', + }, +}; + +function RecentSales({ data }) { + const columns = [ + { + title: 'NAME', + dataIndex: 'name', + }, + { + title: 'STATUS', + dataIndex: 'status', + render: text => {status[text].text}, + }, + { + title: 'DATE', + dataIndex: 'date', + render: text => moment(text).format('YYYY-MM-DD'), + }, + { + title: 'PRICE', + dataIndex: 'price', + render: (text, it) => ${text}, + }, + ]; + return ( +
    +
    key < 5)} /> + + ); +} + +RecentSales.propTypes = { + data: PropTypes.array, +}; + +export default RecentSales; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.module.less new file mode 100644 index 00000000..ac9aea34 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/recentSales.module.less @@ -0,0 +1,8 @@ +@import '../../../themes/vars'; + +.recentsales { + :global .ant-table-thead > tr > th { + background: #fff; + border-bottom: solid 1px @border-color-base; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.js new file mode 100644 index 00000000..4556e04c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.js @@ -0,0 +1,93 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Color from '../../../utils/theme'; +// import { Color } from '../../../utils/theme'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import styles from './sales.module.less'; + +function Sales({ data }) { + return ( +
    +
    Yearly Sales
    + + + { + const { payload } = prop; + return ( +
      + {payload.map((item, key) => ( +
    • + + {item.value} +
    • + ))} +
    + ); + }} + /> + + + + { + const list = content.payload.map((item, key) => ( +
  • + + {`${item.name}:${item.value}`} +
  • + )); + return ( +
    +

    {content.label}

    + {content.payload &&
      {list}
    } +
    + ); + }} + /> + + + +
    +
    +
    + ); +} + +Sales.propTypes = { + data: PropTypes.array, +}; + +export default Sales; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.module.less new file mode 100644 index 00000000..5de99060 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/sales.module.less @@ -0,0 +1,50 @@ +@import '../../../themes/vars'; + +.sales { + overflow: hidden; + .title { + margin-left: 32px; + font-size: 16px; + } +} + +.radiusdot { + width: 12px; + height: 12px; + margin-right: 8px; + border-radius: 50%; + display: inline-block; +} + +.legend { + text-align: right; + color: #999; + font-size: 14px; + + li { + height: 48px; + line-height: 48px; + display: inline-block; + + & + li { + margin-left: 24px; + } + } +} + +.tooltip { + background: #fff; + padding: 20px; + font-size: 14px; + + .tiptitle { + font-weight: 700; + font-size: 16px; + margin-bottom: 8px; + } + + .tipitem { + height: 32px; + line-height: 32px; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user-background.png b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user-background.png new file mode 100644 index 00000000..5f99e137 Binary files /dev/null and b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user-background.png differ diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.js new file mode 100644 index 00000000..8ad9be7b --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button, Avatar } from 'antd'; +import CountUp from 'react-countup'; +import Color from 'utils/theme'; +import styles from './user.module.less'; + +const countUpProps = { + start: 0, + duration: 2.75, + useEasing: true, + useGrouping: true, + separator: ',', +}; + +function User({ avatar, username, sales = 0, sold = 0 }) { + return ( +
    +
    +
    + +
    {username}
    +
    +
    +
    +
    +

    EARNING SALES

    +

    + +

    +
    +
    +

    ITEM SOLD

    +

    + +

    +
    +
    +
    + +
    +
    + ); +} + +User.propTypes = { + avatar: PropTypes.string, + username: PropTypes.string, + sales: PropTypes.number, + sold: PropTypes.number, +}; + +export default User; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.module.less new file mode 100644 index 00000000..cfcc51b2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/user.module.less @@ -0,0 +1,88 @@ +@import '../../../themes/vars'; + +.user { + .header { + display: flex; + justify-content: center; + text-align: center; + color: #fff; + height: 200px; + background-size: cover; + align-items: center; + + .headerinner { + z-index: 2; + } + + &::after { + content: ''; + background-image: url('user-background.png'); + background-size: cover; + position: absolute; + width: 100%; + height: 200px; + left: 0; + top: 0; + opacity: 0.4; + z-index: 1; + } + + .name { + font-size: 16px; + margin-top: 8px; + } + } + + .number { + display: flex; + height: 116px; + justify-content: space-between; + border-bottom: solid 1px #f5f5f5; + + .item { + text-align: center; + height: 116px; + width: 100%; + position: relative; + padding: 30px 0; + + & + .item { + &::before { + content: ''; + display: block; + width: 1px; + height: 116px; + position: absolute; + background: #f5f5f5; + top: 0; + } + } + + p { + color: #757575; + + &:first-child { + font-size: 16px; + } + + &:last-child { + font-size: 20px; + font-weight: 700; + } + } + } + } + + .footer { + height: 116px; + display: flex; + justify-content: center; + align-items: center; + + :global .ant-btn { + color: @purple; + border-color: @purple; + padding: 6px 16px; + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.js new file mode 100644 index 00000000..8a5acf03 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Spin } from 'antd'; +import styles from './weather.module.less'; + +function Weather({ city, icon, dateTime, temperature, name, loading }) { + return ( + +
    +
    +
    +

    {name}

    +
    +
    +

    {`${temperature}°`}

    +

    + {city},{dateTime} +

    +
    +
    + + ); +} + +Weather.propTypes = { + city: PropTypes.string, + icon: PropTypes.string, + dateTime: PropTypes.string, + temperature: PropTypes.string, + name: PropTypes.string, + loading: PropTypes.bool, +}; + +export default Weather; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.module.less new file mode 100644 index 00000000..5ebec21c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/components/weather.module.less @@ -0,0 +1,48 @@ +@import '../../../themes/vars'; + +.weather { + color: #fff; + height: 204px; + padding: 24px; + justify-content: space-between; + display: flex; + font-size: 14px; + + .left { + display: flex; + flex-direction: column; + width: 64px; + padding-top: 55px; + + .icon { + width: 64px; + height: 64px; + background-position: center; + background-size: contain; + } + + p { + margin-top: 16px; + } + } + + .right { + display: flex; + flex-direction: column; + width: 50%; + + .temperature { + font-size: 36px; + text-align: right; + height: 64px; + color: #fff; + } + + .description { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: right; + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.module.less new file mode 100644 index 00000000..596b1c45 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.module.less @@ -0,0 +1,27 @@ +.dashboard { + position: relative; + :global { + .ant-card { + border-radius: 0; + margin-bottom: 24px; + &:hover { + box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); + } + } + .ant-card-body { + overflow-x: hidden; + } + } + + .weather { + &:hover { + box-shadow: 4px 4px 40px rgba(143, 201, 251, 0.6); + } + } + + .quote { + &:hover { + box-shadow: 4px 4px 40px rgba(246, 152, 153, 0.6); + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.tsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.tsx new file mode 100644 index 00000000..3bf72938 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/index.tsx @@ -0,0 +1,134 @@ +import React, { useEffect } from 'react'; +import { Row, Col, Card } from 'antd'; +import Color from '../../utils/theme'; +import { Page, ScrollBar } from '../../components'; +import { NumberCard, Quote, Sales, Weather, RecentSales, Comments, Completed, Browser, Cpu, User } from './components'; +import styles from './index.module.less'; +import store from 'store'; +import { getStore } from './model'; + +const bodyStyle = { + bodyStyle: { + height: 432, + background: '#fff', + }, +}; + +function Dashboard() { + const st = getStore(); + + useEffect(() => { + st.query(); + }, []); + + const userDetail = store.get('user'); + const { avatar, username } = userDetail; + + const weather = st.weather; + const sales = st.sales; + const quote = st.quote; + const numbers = st.numbers; + const recentSales = st.recentSales; + const comments = st.comments; + const completed = st.completed; + const browser = st.browser; + const cpu = st.cpu; + const user = st.user; + + const numberCards = numbers.map((item, key) => ( +
    + + + )); + + return ( + + + {numberCards} + + + + + + + + + + + + + + + + + + + + + + {/* + + + + + + + + + + + */} + {/* + + + + */} + {/* + + + + + + + + + + + + + + + + */} + + + ); +} + +export default Dashboard; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/model.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/model.ts new file mode 100644 index 00000000..72e5e19a --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/model.ts @@ -0,0 +1,69 @@ +import { parse } from 'qs'; +import api from '../../services'; + +import { pathToRegexp } from 'path-to-regexp'; +import { createStore } from 'inula'; + +const { queryDashboard, queryWeather } = api; +const avatar = '//cdn.antd-admin.zuiidea.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236.jpeg'; + +export const getStore = createStore({ + id: 'dashboard', + state: { + weather: { + city: '深圳', + temperature: '30', + name: '晴', + icon: '//cdn.antd-admin.zuiidea.com/sun.png', + }, + message: null, + sales: [], + quote: { + avatar, + }, + numbers: [], + recentSales: [], + comments: [], + completed: [], + browser: [], + cpu: {}, + user: { + avatar, + }, + loading: false, + }, + actions: { + async query(state, payload) { + state.loading = true; + const data = await queryDashboard(parse(payload)); + state.loading = false; + + state.browser = data.browser; + state.comments = data.comments; + state.completed = data.completed; + state.cpu = data.cpu; + state.message = data.message; + state.numbers = data.numbers; + state.quote = data.quote; + state.recentSales = data.recentSales; + state.sales = data.sales; + state.user = data.user; + }, + async queryWeather(state, payload = {}) { + payload.location = 'shenzhen'; + const result = await queryWeather(payload); + const { success } = result; + if (success) { + const data = result.results[0]; + const weather = { + city: data.location.name, + temperature: data.now.temperature, + name: data.now.text, + icon: `//cdn.antd-admin.zuiidea.com/web/icons/3d_50/${data.now.code}.png`, + }; + + state.weather = weather; + } + }, + }, +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/dashboard.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/dashboard.js new file mode 100644 index 00000000..b832a809 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/dashboard.js @@ -0,0 +1,12 @@ +import { request, config } from 'utils'; + +const { api } = config; +const { dashboard } = api; + +export function query(params) { + return request({ + url: dashboard, + method: 'get', + data: params, + }); +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/weather.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/weather.js new file mode 100644 index 00000000..4af783e4 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/dashboard/services/weather.js @@ -0,0 +1,12 @@ +import { request, config } from 'utils'; + +const { APIV1 } = config; + +export function query(params) { + params.key = 'i7sau1babuzwhycn'; + return request({ + url: `${APIV1}/weather/now.json`, + method: 'get', + data: params, + }); +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/index.js new file mode 100644 index 00000000..fd5d3b25 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/index.js @@ -0,0 +1,8 @@ +import React from 'react'; +import { Redirect } from 'react-router-dom'; + +function Index() { + return ; +} + +export default Index; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/routes.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/routes.js new file mode 100644 index 00000000..cb7e03c2 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/routes.js @@ -0,0 +1,26 @@ +import React, { lazy } from 'react'; + +export function getRoutes() { + const routes = [ + { + path: '/', + component: lazy(() => import(/* webpackChunkName: 'layouts__index' */ '@/layouts/index.js')), + childRoutes: [ + { + path: '/404', + component: lazy(() => import(/* webpackChunkName: 'p__404' */ '@/pages/404.jsx')), + }, + { + path: '/dashboard', + component: lazy(() => import(/* webpackChunkName: 'p__dashboard__index' */ './dashboard/index.tsx')), + }, + { + path: '/user', + component: lazy(() => import(/* webpackChunkName: 'p__user__index' */ '@/pages/user/index.tsx')), + }, + ], + }, + ]; + + return routes; +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.js new file mode 100644 index 00000000..3138a2ff --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.js @@ -0,0 +1,114 @@ +import React, { useRef } from 'react'; +import moment from 'moment'; +import { FilterItem } from 'components'; +import { Button, Row, Col, DatePicker, Form, Input, Cascader } from 'antd'; +import city from 'utils/city'; +import { Trans, t } from 'utils/intl'; +import styles from './Filter.module.less'; + +const { Search } = Input; +const { RangePicker } = DatePicker; + +const ColProps = { + xs: 24, + sm: 12, + style: { + marginBottom: 16, + }, +}; + +const TwoColProps = { + ...ColProps, + xl: 96, +}; + +function Filter({ onFilterChange, onAdd, filter }) { + const formRef = useRef(); + + const handleFields = fields => { + const { createTime } = fields; + if (createTime && createTime.length) { + fields.createTime = [moment(createTime[0]).format('YYYY-MM-DD'), moment(createTime[1]).format('YYYY-MM-DD')]; + } + return fields; + }; + + const handleSubmit = () => { + const values = formRef.current.getFieldsValue(); + const fields = handleFields(values); + onFilterChange(fields); + }; + + const handleReset = () => { + const fields = formRef.current.getFieldsValue(); + for (let item in fields) { + if ({}.hasOwnProperty.call(fields, item)) { + if (fields[item] instanceof Array) { + fields[item] = []; + } else { + fields[item] = undefined; + } + } + } + formRef.current.setFieldsValue(fields); + handleSubmit(); + }; + + const handleChange = (key, values) => { + let fields = formRef.current.getFieldsValue(); + fields[key] = values; + fields = handleFields(fields); + onFilterChange(fields); + }; + + const { name, address } = filter; + + let initialCreateTime = []; + if (filter.createTime && filter.createTime[0]) { + initialCreateTime[0] = moment(filter.createTime[0]); + } + if (filter.createTime && filter.createTime[1]) { + initialCreateTime[1] = moment(filter.createTime[1]); + } + + return ( +
    + +
    + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    + + + + ); +} + +export default Filter; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.module.less new file mode 100644 index 00000000..0cd55ada --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Filter.module.less @@ -0,0 +1,3 @@ +.margin-right { + margin-right: 16px; +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.js new file mode 100644 index 00000000..4ca59147 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.js @@ -0,0 +1,113 @@ +import React from 'react'; +import { Table, Modal, Avatar } from 'antd'; +import { DropOption } from 'components'; +import { t } from 'utils/intl'; +import { Trans } from 'utils/intl'; +import { Link } from 'react-router-dom'; +import styles from './List.module.less'; + +const { confirm } = Modal; + +function List({ onDeleteItem, onEditItem, ...tableProps }) { + const handleMenuClick = (record, e) => { + if (e.key === '1') { + onEditItem(record); + } else if (e.key === '2') { + confirm({ + title: t`Are you sure delete this record?`, + onOk() { + onDeleteItem(record.id); + }, + }); + } + }; + + const columns = [ + { + title: Avatar, + dataIndex: 'avatar', + key: 'avatar', + width: '7%', + fixed: 'left', + render: text => , + }, + { + title: Name, + dataIndex: 'name', + key: 'name', + }, + { + title: NickName, + dataIndex: 'nickName', + key: 'nickName', + }, + { + title: Age, + dataIndex: 'age', + width: '6%', + key: 'age', + }, + { + title: Gender, + dataIndex: 'isMale', + key: 'isMale', + width: '7%', + render: text => {text ? 'Male' : 'Female'}, + }, + { + title: Phone, + dataIndex: 'phone', + key: 'phone', + }, + { + title: Email, + dataIndex: 'email', + key: 'email', + }, + { + title: Address, + dataIndex: 'address', + key: 'address', + }, + { + title: CreateTime, + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: Operation, + key: 'operation', + fixed: 'right', + width: '8%', + render: (text, record) => { + return ( + handleMenuClick(record, e)} + menuOptions={[ + { key: '1', name: t`Update` }, + { key: '2', name: t`Delete` }, + ]} + /> + ); + }, + }, + ]; + + return ( +
    t(`Total`, { total }), + }} + className={styles.table} + bordered + scroll={{ x: 1200 }} + columns={columns} + simple + rowKey={record => record.id} + /> + ); +} + +export default List; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.module.less new file mode 100644 index 00000000..6d35765f --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/List.module.less @@ -0,0 +1,7 @@ +.table { + :global { + .ant-table td { + white-space: nowrap; + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Modal.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Modal.js new file mode 100644 index 00000000..a90e4dc3 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/components/Modal.js @@ -0,0 +1,95 @@ +import React, { useRef } from 'react'; +import { Form, Input, InputNumber, Radio, Modal, Cascader } from 'antd'; +import { t, Trans } from 'utils/intl'; +import city from 'utils/city'; + +const FormItem = Form.Item; + +const formItemLayout = { + labelCol: { + span: 6, + }, + wrapperCol: { + span: 14, + }, +}; + +function UserModal({ item = {}, onOk, form, ...modalProps }) { + const formRef = useRef(); + + const handleOk = () => { + formRef.current + .validateFields() + .then(values => { + const data = { + ...values, + key: item.key, + }; + data.address = data.address.join(' '); + onOk(data); + }) + .catch(errorInfo => { + console.log(errorInfo); + }); + }; + + return ( + +
    + + + + + + + + + + Male + + + Female + + + + + + + + + + + + + + + + +
    + ); +} + +export default UserModal; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.js new file mode 100644 index 00000000..62293580 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.js @@ -0,0 +1,33 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Page } from 'components'; +import styles from './index.module.less'; + +class UserDetail extends PureComponent { + render() { + const { userDetail } = this.props; + const { data } = userDetail; + const content = []; + for (let key in data) { + if ({}.hasOwnProperty.call(data, key)) { + content.push( +
    +
    {key}
    +
    {String(data[key])}
    +
    + ); + } + } + return ( + +
    {content}
    +
    + ); + } +} + +UserDetail.propTypes = { + userDetail: PropTypes.object, +}; + +export default UserDetail; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.module.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.module.less new file mode 100644 index 00000000..fa9c7d40 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/index.module.less @@ -0,0 +1,14 @@ +.content { + line-height: 2.4; + font-size: 13px; + + .item { + display: flex; + + & > div { + &:first-child { + width: 100px; + } + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/models/detail.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/models/detail.js new file mode 100644 index 00000000..4b73d369 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/id/models/detail.js @@ -0,0 +1,50 @@ +const { pathToRegexp } = require('path-to-regexp'); +import api from 'api'; + +const { queryUser } = api; + +export default { + namespace: 'userDetail', + + state: { + data: {}, + }, + + subscriptions: { + setup({ dispatch, history }) { + history.listen(({ pathname }) => { + const match = pathToRegexp('/user/:id').exec(pathname); + if (match) { + dispatch({ type: 'query', payload: { id: match[1] } }); + } + }); + }, + }, + + effects: { + *query({ payload }, { call, put }) { + const data = yield call(queryUser, payload); + const { success, message, status, ...other } = data; + if (success) { + yield put({ + type: 'querySuccess', + payload: { + data: other, + }, + }); + } else { + throw data; + } + }, + }, + + reducers: { + querySuccess(state, { payload }) { + const { data } = payload; + return { + ...state, + data, + }; + }, + }, +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/index.tsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/index.tsx new file mode 100644 index 00000000..b76db303 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/index.tsx @@ -0,0 +1,148 @@ +import React, { useEffect } from 'react'; +import { Row, Col, Button, Popconfirm } from 'antd'; +import { Page } from '../../components'; +import { stringify } from 'qs'; +import List from './components/List'; +import Filter from './components/Filter'; +import Modal from './components/Modal'; +import { getStore } from './model'; +import { pathToRegexp } from 'path-to-regexp'; +import { t } from '../../utils/intl'; +import { parseSearch } from '../../utils'; + +function User({ location, history }) { + const st = getStore(); + + useEffect(() => { + if (pathToRegexp('/user').exec(location.pathname)) { + const payload = { page: 1, pageSize: 10, ...parseSearch(location.search) }; + st.query(payload); + } + }, [st.pagination.total]); + + const handleRefresh = newQuery => { + const { search, pathname } = location; + + const params = { + ...parseSearch(search), + ...newQuery, + }; + + history.push({ + pathname, + search: stringify(params, { arrayFormat: 'repeat' }), + }); + + st.query(params); + }; + + const handleDeleteItems = () => { + st.multiDelete({ + ids: st.selectedRowKeys, + }); + + handleRefresh({ + page: + st.list.length === st.selectedRowKeys.length && st.pagination.current > 1 + ? st.pagination.current - 1 + : st.pagination.current, + }); + }; + + const modalProps = () => { + return { + item: st.modalType === 'create' ? {} : st.currentItem, + visible: st.modalVisible, + destroyOnClose: true, + maskClosable: false, + confirmLoading: false, + title: `${st.modalType === 'create' ? t`Create User` : t`Update User`}`, + centered: true, + onOk: data => { + st[st.modalType](data); + handleRefresh(); + }, + onCancel() { + st.hideModal(); + }, + }; + }; + + const listProps = () => { + return { + dataSource: st.list, + loading: false, + pagination: st.pagination, + onChange: page => { + const { search } = location; + const params = { + ...parseSearch(search), + page: page.current, + pageSize: page.pageSize, + }; + + handleRefresh(params); + }, + onDeleteItem: id => { + st.delete(id); + handleRefresh({ + page: st.list.length === 1 && st.pagination.current > 1 ? st.pagination.current - 1 : st.pagination.current, + }); + }, + onEditItem: item => { + st.showModal({ + modalType: 'update', + currentItem: item, + }); + }, + rowSelection: { + selectedRowKeys: st.selectedRowKeys, + onChange: keys => { + st.selectedRowKeys = keys; + }, + }, + }; + }; + + const filterProps = () => { + const { search } = location; + + return { + filter: { + ...parseSearch(search), + }, + onFilterChange: value => { + handleRefresh({ + ...value, + }); + }, + onAdd: () => { + st.showModal({ + modalType: 'create', + }); + }, + }; + }; + + return ( + + + {st.selectedRowKeys.length > 0 && ( + + + {`Selected ${st.selectedRowKeys.length} items `} + + + + + + )} + + + + ); +} + +export default User; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/model.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/model.ts new file mode 100644 index 00000000..44f8ac24 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/pages/user/model.ts @@ -0,0 +1,92 @@ +import api from '../../services'; +import { createStore } from 'inula'; + +const { queryUserList, createUser, removeUser, updateUser, removeUserList } = api; + +export const getStore = createStore({ + id: 'user', + + state: { + currentItem: { + id: 0, + }, + modalVisible: false, + modalType: 'create', + selectedRowKeys: [], + + list: [], + pagination: { + showSizeChanger: true, + showQuickJumper: true, + current: 1, + total: 0, + pageSize: 10, + }, + loading: false, + }, + + actions: { + async query(state, payload) { + state.loading = true; + const data = await queryUserList(payload); + state.loading = false; + if (data) { + state.list = data.data; + state.pagination.current = Number(payload.page) || 1; + state.pagination.pageSize = Number(payload.pageSize) || 10; + state.pagination.total = data.total; + } + }, + + async delete(state, payload) { + const data = await removeUser({ id: payload }); + if (data.success) { + state.selectedRowKeys = state.selectedRowKeys.filter(_ => _ !== payload); + } else { + throw data; + } + }, + + async multiDelete(state, payload) { + const data = await removeUserList(payload); + if (data.success) { + state.selectedRowKeys = []; + } else { + throw data; + } + }, + + async create(state, payload) { + const data = await createUser(payload); + if (data.success) { + this.hideModal(); + } else { + throw data; + } + }, + + async update(state, payload) { + const newUser = { ...payload, id: state.currentItem.id }; + const data = await updateUser(newUser); + if (data.success) { + this.hideModal(); + } else { + throw data; + } + }, + + showModal(state, payload) { + state.modalVisible = true; + if (state.modalType !== undefined) { + state.modalType = payload.modalType; + } + if (payload.currentItem !== undefined) { + state.currentItem = payload.currentItem; + } + }, + + hideModal(state) { + state.modalVisible = false; + }, + }, +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/plugins/onError.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/plugins/onError.js new file mode 100644 index 00000000..f9cb6260 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/plugins/onError.js @@ -0,0 +1,13 @@ +import { message } from 'antd'; + +export default { + onError(e, a) { + e.preventDefault(); + if (e.message) { + message.error(e.message); + } else { + /* eslint-disable */ + console.error(e); + } + }, +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/api.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/api.js new file mode 100644 index 00000000..1dddf50f --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/api.js @@ -0,0 +1,16 @@ +export default { + queryRouteList: '/routes', + + queryUserInfo: '/user', + + queryUser: '/user/:id', + queryUserList: '/users', + updateUser: 'Patch /user/:id', + createUser: 'POST /user', + removeUser: 'DELETE /user/:id', + removeUserList: 'POST /users/delete', + + queryPostList: '/posts', + + queryDashboard: '/dashboard', +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/index.js new file mode 100644 index 00000000..ef4c9bf1 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/services/index.js @@ -0,0 +1,38 @@ +import request from 'utils/request'; +import config from 'utils/config'; + +import api from './api'; + +const gen = params => { + let url = config.apiPrefix + params; + let method = 'GET'; + + const paramsArray = params.split(' '); + if (paramsArray.length === 2) { + method = paramsArray[0]; + url = config.apiPrefix + paramsArray[1]; + } + + return function (data) { + return request({ + url, + data, + method, + }); + }; +}; + +const APIFunction = {}; +for (const key in api) { + APIFunction[key] = gen(api[key]); +} + +APIFunction.queryWeather = params => { + params.key = 'i7sau1babuzwhycn'; + return request({ + url: `${config.apiPrefix}/weather/now.json`, + data: params, + }); +}; + +export default APIFunction; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/default.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/default.less new file mode 100644 index 00000000..78678a50 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/default.less @@ -0,0 +1,16 @@ +// 本文件是对 ant-design: +// https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less +// 相应变量值的覆盖 +// 注意:只需写出要覆盖的变量即可(不需要覆盖的变量不要写) + +@import '../../../node_modules/antd/lib/style/themes/default.less'; + +@border-radius-base: 3px; +@border-radius-sm: 2px; +@shadow-color: rgba(0, 0, 0, 0.05); +@shadow-1-down: 4px 4px 40px @shadow-color; +@border-color-split: #f4f4f4; +@border-color-base: #e5e5e5; +@font-size-base: 13px; +@text-color: #666; +@hover-color: #f9f9fc; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/index.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/index.less new file mode 100644 index 00000000..9b0df9b6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/index.less @@ -0,0 +1,124 @@ +@import './vars.less'; + +body { + height: 100%; + overflow-y: hidden; + background-color: #f8f8f8; +} + +::-webkit-scrollbar-thumb { + background-color: #e6e6e6; +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.margin-right { + margin-right: 16px; +} + +:global { + .ant-breadcrumb { + & > span { + &:last-child { + color: #999; + font-weight: normal; + } + } + } + + .ant-breadcrumb-link { + .anticon + span { + margin-left: 4px; + } + } + + .ant-table { + .ant-table-thead > tr > th { + text-align: center; + } + + .ant-table-tbody > tr > td { + text-align: center; + } + + &.ant-table-small { + .ant-table-thead > tr > th { + background: #f7f7f7; + } + + .ant-table-body > table { + padding: 0; + } + } + } + + .ant-table-pagination { + float: none !important; + display: table; + margin: 16px auto !important; + } + + .ant-popover-inner { + border: none; + border-radius: 0; + box-shadow: 0 0 20px rgba(100, 100, 100, 0.2); + } + + .ant-form-item-control { + vertical-align: middle; + } + + .ant-modal-mask { + background-color: rgba(55, 55, 55, 0.2); + } + + .ant-modal-content { + box-shadow: none; + } + + .ant-select-dropdown-menu-item { + padding: 12px 16px !important; + } + + a:focus { + text-decoration: none; + } + + .ant-table-layout-fixed table { + table-layout: auto; + } +} +@media (min-width: 1600px) { + :global { + .ant-col-xl-48 { + width: 20%; + } + + .ant-col-xl-96 { + width: 40%; + } + } +} +@media (max-width: 767px) { + :global { + .ant-pagination-item, + .ant-pagination-next, + .ant-pagination-options, + .ant-pagination-prev { + margin-bottom: 8px; + } + + .ant-card { + .ant-card-head { + padding: 0 12px; + } + + .ant-card-body { + padding: 12px; + } + } + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/mixin.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/mixin.less new file mode 100644 index 00000000..dc01f6b6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/mixin.less @@ -0,0 +1,35 @@ +@import './default.less'; + +@dark-half: #494949; +@purple: #d897eb; +@shadow-1: 4px 4px 20px 0 rgba(0, 0, 0, 0.01); +@shadow-2: 4px 4px 40px 0 rgba(0, 0, 0, 0.05); +@transition-ease-in: all 0.3s ease-out; +@transition-ease-out: all 0.3s ease-out; +@ease-in: ease-in; + +.text-overflow { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.text-gradient { + background-image: -webkit-gradient( + linear, + 37.219838% 34.532506%, + 36.425669% 93.178216%, + from(#29cdff), + to(#0a60ff), + color-stop(0.37, #148eff) + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.background-hover { + transition: @transition-ease-in; + &:hover { + background-color: @hover-color; + } +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/vars.less b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/vars.less new file mode 100644 index 00000000..b8bb2bc6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/themes/vars.less @@ -0,0 +1,2 @@ +@import './default.less'; +@import './mixin.less'; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/city.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/city.js new file mode 100644 index 00000000..78aa870d --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/city.js @@ -0,0 +1,4051 @@ +/* + ## Address 字典数据 + 字典数据来源 http://www.atatech.org/articles/30028?rnd=254259856 + 国标 省(市)级行政区划码表 + 华北 北京市 天津市 河北省 山西省 内蒙古自治区 + 东北 辽宁省 吉林省 黑龙江省 + 华东 上海市 江苏省 浙江省 安徽省 福建省 江西省 山东省 + 华南 广东省 广西壮族自治区 海南省 + 华中 河南省 湖北省 湖南省 + 西南 重庆市 四川省 贵州省 云南省 西藏自治区 + 西北 陕西省 甘肃省 青海省 宁夏回族自治区 新疆维吾尔自治区 + 港澳台 香港特别行政区 澳门特别行政区 台湾省 + + **排序** + + ```js + var map = {} + _.each(_.keys(REGIONS),function(id){ + map[id] = REGIONS[ID] + }) + JSON.stringify(map) + ``` +*/ +let DICT = { + 110000: '北京', + 110100: '北京市', + 110101: '东城区', + 110102: '西城区', + 110105: '朝阳区', + 110106: '丰台区', + 110107: '石景山区', + 110108: '海淀区', + 110109: '门头沟区', + 110111: '房山区', + 110112: '通州区', + 110113: '顺义区', + 110114: '昌平区', + 110115: '大兴区', + 110116: '怀柔区', + 110117: '平谷区', + 110228: '密云县', + 110229: '延庆县', + 110230: '其它区', + 120000: '天津', + 120100: '天津市', + 120101: '和平区', + 120102: '河东区', + 120103: '河西区', + 120104: '南开区', + 120105: '河北区', + 120106: '红桥区', + 120110: '东丽区', + 120111: '西青区', + 120112: '津南区', + 120113: '北辰区', + 120114: '武清区', + 120115: '宝坻区', + 120116: '滨海新区', + 120221: '宁河县', + 120223: '静海县', + 120225: '蓟县', + 120226: '其它区', + 130000: '河北省', + 130100: '石家庄市', + 130102: '长安区', + 130103: '桥东区', + 130104: '桥西区', + 130105: '新华区', + 130107: '井陉矿区', + 130108: '裕华区', + 130121: '井陉县', + 130123: '正定县', + 130124: '栾城县', + 130125: '行唐县', + 130126: '灵寿县', + 130127: '高邑县', + 130128: '深泽县', + 130129: '赞皇县', + 130130: '无极县', + 130131: '平山县', + 130132: '元氏县', + 130133: '赵县', + 130181: '辛集市', + 130182: '藁城市', + 130183: '晋州市', + 130184: '新乐市', + 130185: '鹿泉市', + 130186: '其它区', + 130200: '唐山市', + 130202: '路南区', + 130203: '路北区', + 130204: '古冶区', + 130205: '开平区', + 130207: '丰南区', + 130208: '丰润区', + 130223: '滦县', + 130224: '滦南县', + 130225: '乐亭县', + 130227: '迁西县', + 130229: '玉田县', + 130230: '曹妃甸区', + 130281: '遵化市', + 130283: '迁安市', + 130284: '其它区', + 130300: '秦皇岛市', + 130302: '海港区', + 130303: '山海关区', + 130304: '北戴河区', + 130321: '青龙满族自治县', + 130322: '昌黎县', + 130323: '抚宁县', + 130324: '卢龙县', + 130398: '其它区', + 130400: '邯郸市', + 130402: '邯山区', + 130403: '丛台区', + 130404: '复兴区', + 130406: '峰峰矿区', + 130421: '邯郸县', + 130423: '临漳县', + 130424: '成安县', + 130425: '大名县', + 130426: '涉县', + 130427: '磁县', + 130428: '肥乡县', + 130429: '永年县', + 130430: '邱县', + 130431: '鸡泽县', + 130432: '广平县', + 130433: '馆陶县', + 130434: '魏县', + 130435: '曲周县', + 130481: '武安市', + 130482: '其它区', + 130500: '邢台市', + 130502: '桥东区', + 130503: '桥西区', + 130521: '邢台县', + 130522: '临城县', + 130523: '内丘县', + 130524: '柏乡县', + 130525: '隆尧县', + 130526: '任县', + 130527: '南和县', + 130528: '宁晋县', + 130529: '巨鹿县', + 130530: '新河县', + 130531: '广宗县', + 130532: '平乡县', + 130533: '威县', + 130534: '清河县', + 130535: '临西县', + 130581: '南宫市', + 130582: '沙河市', + 130583: '其它区', + 130600: '保定市', + 130602: '新市区', + 130603: '北市区', + 130604: '南市区', + 130621: '满城县', + 130622: '清苑县', + 130623: '涞水县', + 130624: '阜平县', + 130625: '徐水县', + 130626: '定兴县', + 130627: '唐县', + 130628: '高阳县', + 130629: '容城县', + 130630: '涞源县', + 130631: '望都县', + 130632: '安新县', + 130633: '易县', + 130634: '曲阳县', + 130635: '蠡县', + 130636: '顺平县', + 130637: '博野县', + 130638: '雄县', + 130681: '涿州市', + 130682: '定州市', + 130683: '安国市', + 130684: '高碑店市', + 130699: '其它区', + 130700: '张家口市', + 130702: '桥东区', + 130703: '桥西区', + 130705: '宣化区', + 130706: '下花园区', + 130721: '宣化县', + 130722: '张北县', + 130723: '康保县', + 130724: '沽源县', + 130725: '尚义县', + 130726: '蔚县', + 130727: '阳原县', + 130728: '怀安县', + 130729: '万全县', + 130730: '怀来县', + 130731: '涿鹿县', + 130732: '赤城县', + 130733: '崇礼县', + 130734: '其它区', + 130800: '承德市', + 130802: '双桥区', + 130803: '双滦区', + 130804: '鹰手营子矿区', + 130821: '承德县', + 130822: '兴隆县', + 130823: '平泉县', + 130824: '滦平县', + 130825: '隆化县', + 130826: '丰宁满族自治县', + 130827: '宽城满族自治县', + 130828: '围场满族蒙古族自治县', + 130829: '其它区', + 130900: '沧州市', + 130902: '新华区', + 130903: '运河区', + 130921: '沧县', + 130922: '青县', + 130923: '东光县', + 130924: '海兴县', + 130925: '盐山县', + 130926: '肃宁县', + 130927: '南皮县', + 130928: '吴桥县', + 130929: '献县', + 130930: '孟村回族自治县', + 130981: '泊头市', + 130982: '任丘市', + 130983: '黄骅市', + 130984: '河间市', + 130985: '其它区', + 131000: '廊坊市', + 131002: '安次区', + 131003: '广阳区', + 131022: '固安县', + 131023: '永清县', + 131024: '香河县', + 131025: '大城县', + 131026: '文安县', + 131028: '大厂回族自治县', + 131081: '霸州市', + 131082: '三河市', + 131083: '其它区', + 131100: '衡水市', + 131102: '桃城区', + 131121: '枣强县', + 131122: '武邑县', + 131123: '武强县', + 131124: '饶阳县', + 131125: '安平县', + 131126: '故城县', + 131127: '景县', + 131128: '阜城县', + 131181: '冀州市', + 131182: '深州市', + 131183: '其它区', + 140000: '山西省', + 140100: '太原市', + 140105: '小店区', + 140106: '迎泽区', + 140107: '杏花岭区', + 140108: '尖草坪区', + 140109: '万柏林区', + 140110: '晋源区', + 140121: '清徐县', + 140122: '阳曲县', + 140123: '娄烦县', + 140181: '古交市', + 140182: '其它区', + 140200: '大同市', + 140202: '城区', + 140203: '矿区', + 140211: '南郊区', + 140212: '新荣区', + 140221: '阳高县', + 140222: '天镇县', + 140223: '广灵县', + 140224: '灵丘县', + 140225: '浑源县', + 140226: '左云县', + 140227: '大同县', + 140228: '其它区', + 140300: '阳泉市', + 140302: '城区', + 140303: '矿区', + 140311: '郊区', + 140321: '平定县', + 140322: '盂县', + 140323: '其它区', + 140400: '长治市', + 140421: '长治县', + 140423: '襄垣县', + 140424: '屯留县', + 140425: '平顺县', + 140426: '黎城县', + 140427: '壶关县', + 140428: '长子县', + 140429: '武乡县', + 140430: '沁县', + 140431: '沁源县', + 140481: '潞城市', + 140482: '城区', + 140483: '郊区', + 140485: '其它区', + 140500: '晋城市', + 140502: '城区', + 140521: '沁水县', + 140522: '阳城县', + 140524: '陵川县', + 140525: '泽州县', + 140581: '高平市', + 140582: '其它区', + 140600: '朔州市', + 140602: '朔城区', + 140603: '平鲁区', + 140621: '山阴县', + 140622: '应县', + 140623: '右玉县', + 140624: '怀仁县', + 140625: '其它区', + 140700: '晋中市', + 140702: '榆次区', + 140721: '榆社县', + 140722: '左权县', + 140723: '和顺县', + 140724: '昔阳县', + 140725: '寿阳县', + 140726: '太谷县', + 140727: '祁县', + 140728: '平遥县', + 140729: '灵石县', + 140781: '介休市', + 140782: '其它区', + 140800: '运城市', + 140802: '盐湖区', + 140821: '临猗县', + 140822: '万荣县', + 140823: '闻喜县', + 140824: '稷山县', + 140825: '新绛县', + 140826: '绛县', + 140827: '垣曲县', + 140828: '夏县', + 140829: '平陆县', + 140830: '芮城县', + 140881: '永济市', + 140882: '河津市', + 140883: '其它区', + 140900: '忻州市', + 140902: '忻府区', + 140921: '定襄县', + 140922: '五台县', + 140923: '代县', + 140924: '繁峙县', + 140925: '宁武县', + 140926: '静乐县', + 140927: '神池县', + 140928: '五寨县', + 140929: '岢岚县', + 140930: '河曲县', + 140931: '保德县', + 140932: '偏关县', + 140981: '原平市', + 140982: '其它区', + 141000: '临汾市', + 141002: '尧都区', + 141021: '曲沃县', + 141022: '翼城县', + 141023: '襄汾县', + 141024: '洪洞县', + 141025: '古县', + 141026: '安泽县', + 141027: '浮山县', + 141028: '吉县', + 141029: '乡宁县', + 141030: '大宁县', + 141031: '隰县', + 141032: '永和县', + 141033: '蒲县', + 141034: '汾西县', + 141081: '侯马市', + 141082: '霍州市', + 141083: '其它区', + 141100: '吕梁市', + 141102: '离石区', + 141121: '文水县', + 141122: '交城县', + 141123: '兴县', + 141124: '临县', + 141125: '柳林县', + 141126: '石楼县', + 141127: '岚县', + 141128: '方山县', + 141129: '中阳县', + 141130: '交口县', + 141181: '孝义市', + 141182: '汾阳市', + 141183: '其它区', + 150000: '内蒙古自治区', + 150100: '呼和浩特市', + 150102: '新城区', + 150103: '回民区', + 150104: '玉泉区', + 150105: '赛罕区', + 150121: '土默特左旗', + 150122: '托克托县', + 150123: '和林格尔县', + 150124: '清水河县', + 150125: '武川县', + 150126: '其它区', + 150200: '包头市', + 150202: '东河区', + 150203: '昆都仑区', + 150204: '青山区', + 150205: '石拐区', + 150206: '白云鄂博矿区', + 150207: '九原区', + 150221: '土默特右旗', + 150222: '固阳县', + 150223: '达尔罕茂明安联合旗', + 150224: '其它区', + 150300: '乌海市', + 150302: '海勃湾区', + 150303: '海南区', + 150304: '乌达区', + 150305: '其它区', + 150400: '赤峰市', + 150402: '红山区', + 150403: '元宝山区', + 150404: '松山区', + 150421: '阿鲁科尔沁旗', + 150422: '巴林左旗', + 150423: '巴林右旗', + 150424: '林西县', + 150425: '克什克腾旗', + 150426: '翁牛特旗', + 150428: '喀喇沁旗', + 150429: '宁城县', + 150430: '敖汉旗', + 150431: '其它区', + 150500: '通辽市', + 150502: '科尔沁区', + 150521: '科尔沁左翼中旗', + 150522: '科尔沁左翼后旗', + 150523: '开鲁县', + 150524: '库伦旗', + 150525: '奈曼旗', + 150526: '扎鲁特旗', + 150581: '霍林郭勒市', + 150582: '其它区', + 150600: '鄂尔多斯市', + 150602: '东胜区', + 150621: '达拉特旗', + 150622: '准格尔旗', + 150623: '鄂托克前旗', + 150624: '鄂托克旗', + 150625: '杭锦旗', + 150626: '乌审旗', + 150627: '伊金霍洛旗', + 150628: '其它区', + 150700: '呼伦贝尔市', + 150702: '海拉尔区', + 150703: '扎赉诺尔区', + 150721: '阿荣旗', + 150722: '莫力达瓦达斡尔族自治旗', + 150723: '鄂伦春自治旗', + 150724: '鄂温克族自治旗', + 150725: '陈巴尔虎旗', + 150726: '新巴尔虎左旗', + 150727: '新巴尔虎右旗', + 150781: '满洲里市', + 150782: '牙克石市', + 150783: '扎兰屯市', + 150784: '额尔古纳市', + 150785: '根河市', + 150786: '其它区', + 150800: '巴彦淖尔市', + 150802: '临河区', + 150821: '五原县', + 150822: '磴口县', + 150823: '乌拉特前旗', + 150824: '乌拉特中旗', + 150825: '乌拉特后旗', + 150826: '杭锦后旗', + 150827: '其它区', + 150900: '乌兰察布市', + 150902: '集宁区', + 150921: '卓资县', + 150922: '化德县', + 150923: '商都县', + 150924: '兴和县', + 150925: '凉城县', + 150926: '察哈尔右翼前旗', + 150927: '察哈尔右翼中旗', + 150928: '察哈尔右翼后旗', + 150929: '四子王旗', + 150981: '丰镇市', + 150982: '其它区', + 152200: '兴安盟', + 152201: '乌兰浩特市', + 152202: '阿尔山市', + 152221: '科尔沁右翼前旗', + 152222: '科尔沁右翼中旗', + 152223: '扎赉特旗', + 152224: '突泉县', + 152225: '其它区', + 152500: '锡林郭勒盟', + 152501: '二连浩特市', + 152502: '锡林浩特市', + 152522: '阿巴嘎旗', + 152523: '苏尼特左旗', + 152524: '苏尼特右旗', + 152525: '东乌珠穆沁旗', + 152526: '西乌珠穆沁旗', + 152527: '太仆寺旗', + 152528: '镶黄旗', + 152529: '正镶白旗', + 152530: '正蓝旗', + 152531: '多伦县', + 152532: '其它区', + 152900: '阿拉善盟', + 152921: '阿拉善左旗', + 152922: '阿拉善右旗', + 152923: '额济纳旗', + 152924: '其它区', + 210000: '辽宁省', + 210100: '沈阳市', + 210102: '和平区', + 210103: '沈河区', + 210104: '大东区', + 210105: '皇姑区', + 210106: '铁西区', + 210111: '苏家屯区', + 210112: '东陵区', + 210113: '新城子区', + 210114: '于洪区', + 210122: '辽中县', + 210123: '康平县', + 210124: '法库县', + 210181: '新民市', + 210184: '沈北新区', + 210185: '其它区', + 210200: '大连市', + 210202: '中山区', + 210203: '西岗区', + 210204: '沙河口区', + 210211: '甘井子区', + 210212: '旅顺口区', + 210213: '金州区', + 210224: '长海县', + 210281: '瓦房店市', + 210282: '普兰店市', + 210283: '庄河市', + 210298: '其它区', + 210300: '鞍山市', + 210302: '铁东区', + 210303: '铁西区', + 210304: '立山区', + 210311: '千山区', + 210321: '台安县', + 210323: '岫岩满族自治县', + 210381: '海城市', + 210382: '其它区', + 210400: '抚顺市', + 210402: '新抚区', + 210403: '东洲区', + 210404: '望花区', + 210411: '顺城区', + 210421: '抚顺县', + 210422: '新宾满族自治县', + 210423: '清原满族自治县', + 210424: '其它区', + 210500: '本溪市', + 210502: '平山区', + 210503: '溪湖区', + 210504: '明山区', + 210505: '南芬区', + 210521: '本溪满族自治县', + 210522: '桓仁满族自治县', + 210523: '其它区', + 210600: '丹东市', + 210602: '元宝区', + 210603: '振兴区', + 210604: '振安区', + 210624: '宽甸满族自治县', + 210681: '东港市', + 210682: '凤城市', + 210683: '其它区', + 210700: '锦州市', + 210702: '古塔区', + 210703: '凌河区', + 210711: '太和区', + 210726: '黑山县', + 210727: '义县', + 210781: '凌海市', + 210782: '北镇市', + 210783: '其它区', + 210800: '营口市', + 210802: '站前区', + 210803: '西市区', + 210804: '鲅鱼圈区', + 210811: '老边区', + 210881: '盖州市', + 210882: '大石桥市', + 210883: '其它区', + 210900: '阜新市', + 210902: '海州区', + 210903: '新邱区', + 210904: '太平区', + 210905: '清河门区', + 210911: '细河区', + 210921: '阜新蒙古族自治县', + 210922: '彰武县', + 210923: '其它区', + 211000: '辽阳市', + 211002: '白塔区', + 211003: '文圣区', + 211004: '宏伟区', + 211005: '弓长岭区', + 211011: '太子河区', + 211021: '辽阳县', + 211081: '灯塔市', + 211082: '其它区', + 211100: '盘锦市', + 211102: '双台子区', + 211103: '兴隆台区', + 211121: '大洼县', + 211122: '盘山县', + 211123: '其它区', + 211200: '铁岭市', + 211202: '银州区', + 211204: '清河区', + 211221: '铁岭县', + 211223: '西丰县', + 211224: '昌图县', + 211281: '调兵山市', + 211282: '开原市', + 211283: '其它区', + 211300: '朝阳市', + 211302: '双塔区', + 211303: '龙城区', + 211321: '朝阳县', + 211322: '建平县', + 211324: '喀喇沁左翼蒙古族自治县', + 211381: '北票市', + 211382: '凌源市', + 211383: '其它区', + 211400: '葫芦岛市', + 211402: '连山区', + 211403: '龙港区', + 211404: '南票区', + 211421: '绥中县', + 211422: '建昌县', + 211481: '兴城市', + 211482: '其它区', + 220000: '吉林省', + 220100: '长春市', + 220102: '南关区', + 220103: '宽城区', + 220104: '朝阳区', + 220105: '二道区', + 220106: '绿园区', + 220112: '双阳区', + 220122: '农安县', + 220181: '九台市', + 220182: '榆树市', + 220183: '德惠市', + 220188: '其它区', + 220200: '吉林市', + 220202: '昌邑区', + 220203: '龙潭区', + 220204: '船营区', + 220211: '丰满区', + 220221: '永吉县', + 220281: '蛟河市', + 220282: '桦甸市', + 220283: '舒兰市', + 220284: '磐石市', + 220285: '其它区', + 220300: '四平市', + 220302: '铁西区', + 220303: '铁东区', + 220322: '梨树县', + 220323: '伊通满族自治县', + 220381: '公主岭市', + 220382: '双辽市', + 220383: '其它区', + 220400: '辽源市', + 220402: '龙山区', + 220403: '西安区', + 220421: '东丰县', + 220422: '东辽县', + 220423: '其它区', + 220500: '通化市', + 220502: '东昌区', + 220503: '二道江区', + 220521: '通化县', + 220523: '辉南县', + 220524: '柳河县', + 220581: '梅河口市', + 220582: '集安市', + 220583: '其它区', + 220600: '白山市', + 220602: '浑江区', + 220621: '抚松县', + 220622: '靖宇县', + 220623: '长白朝鲜族自治县', + 220625: '江源区', + 220681: '临江市', + 220682: '其它区', + 220700: '松原市', + 220702: '宁江区', + 220721: '前郭尔罗斯蒙古族自治县', + 220722: '长岭县', + 220723: '乾安县', + 220724: '扶余市', + 220725: '其它区', + 220800: '白城市', + 220802: '洮北区', + 220821: '镇赉县', + 220822: '通榆县', + 220881: '洮南市', + 220882: '大安市', + 220883: '其它区', + 222400: '延边朝鲜族自治州', + 222401: '延吉市', + 222402: '图们市', + 222403: '敦化市', + 222404: '珲春市', + 222405: '龙井市', + 222406: '和龙市', + 222424: '汪清县', + 222426: '安图县', + 222427: '其它区', + 230000: '黑龙江省', + 230100: '哈尔滨市', + 230102: '道里区', + 230103: '南岗区', + 230104: '道外区', + 230106: '香坊区', + 230108: '平房区', + 230109: '松北区', + 230111: '呼兰区', + 230123: '依兰县', + 230124: '方正县', + 230125: '宾县', + 230126: '巴彦县', + 230127: '木兰县', + 230128: '通河县', + 230129: '延寿县', + 230181: '阿城区', + 230182: '双城市', + 230183: '尚志市', + 230184: '五常市', + 230186: '其它区', + 230200: '齐齐哈尔市', + 230202: '龙沙区', + 230203: '建华区', + 230204: '铁锋区', + 230205: '昂昂溪区', + 230206: '富拉尔基区', + 230207: '碾子山区', + 230208: '梅里斯达斡尔族区', + 230221: '龙江县', + 230223: '依安县', + 230224: '泰来县', + 230225: '甘南县', + 230227: '富裕县', + 230229: '克山县', + 230230: '克东县', + 230231: '拜泉县', + 230281: '讷河市', + 230282: '其它区', + 230300: '鸡西市', + 230302: '鸡冠区', + 230303: '恒山区', + 230304: '滴道区', + 230305: '梨树区', + 230306: '城子河区', + 230307: '麻山区', + 230321: '鸡东县', + 230381: '虎林市', + 230382: '密山市', + 230383: '其它区', + 230400: '鹤岗市', + 230402: '向阳区', + 230403: '工农区', + 230404: '南山区', + 230405: '兴安区', + 230406: '东山区', + 230407: '兴山区', + 230421: '萝北县', + 230422: '绥滨县', + 230423: '其它区', + 230500: '双鸭山市', + 230502: '尖山区', + 230503: '岭东区', + 230505: '四方台区', + 230506: '宝山区', + 230521: '集贤县', + 230522: '友谊县', + 230523: '宝清县', + 230524: '饶河县', + 230525: '其它区', + 230600: '大庆市', + 230602: '萨尔图区', + 230603: '龙凤区', + 230604: '让胡路区', + 230605: '红岗区', + 230606: '大同区', + 230621: '肇州县', + 230622: '肇源县', + 230623: '林甸县', + 230624: '杜尔伯特蒙古族自治县', + 230625: '其它区', + 230700: '伊春市', + 230702: '伊春区', + 230703: '南岔区', + 230704: '友好区', + 230705: '西林区', + 230706: '翠峦区', + 230707: '新青区', + 230708: '美溪区', + 230709: '金山屯区', + 230710: '五营区', + 230711: '乌马河区', + 230712: '汤旺河区', + 230713: '带岭区', + 230714: '乌伊岭区', + 230715: '红星区', + 230716: '上甘岭区', + 230722: '嘉荫县', + 230781: '铁力市', + 230782: '其它区', + 230800: '佳木斯市', + 230803: '向阳区', + 230804: '前进区', + 230805: '东风区', + 230811: '郊区', + 230822: '桦南县', + 230826: '桦川县', + 230828: '汤原县', + 230833: '抚远县', + 230881: '同江市', + 230882: '富锦市', + 230883: '其它区', + 230900: '七台河市', + 230902: '新兴区', + 230903: '桃山区', + 230904: '茄子河区', + 230921: '勃利县', + 230922: '其它区', + 231000: '牡丹江市', + 231002: '东安区', + 231003: '阳明区', + 231004: '爱民区', + 231005: '西安区', + 231024: '东宁县', + 231025: '林口县', + 231081: '绥芬河市', + 231083: '海林市', + 231084: '宁安市', + 231085: '穆棱市', + 231086: '其它区', + 231100: '黑河市', + 231102: '爱辉区', + 231121: '嫩江县', + 231123: '逊克县', + 231124: '孙吴县', + 231181: '北安市', + 231182: '五大连池市', + 231183: '其它区', + 231200: '绥化市', + 231202: '北林区', + 231221: '望奎县', + 231222: '兰西县', + 231223: '青冈县', + 231224: '庆安县', + 231225: '明水县', + 231226: '绥棱县', + 231281: '安达市', + 231282: '肇东市', + 231283: '海伦市', + 231284: '其它区', + 232700: '大兴安岭地区', + 232702: '松岭区', + 232703: '新林区', + 232704: '呼中区', + 232721: '呼玛县', + 232722: '塔河县', + 232723: '漠河县', + 232724: '加格达奇区', + 232725: '其它区', + 310000: '上海', + 310100: '上海市', + 310101: '黄浦区', + 310104: '徐汇区', + 310105: '长宁区', + 310106: '静安区', + 310107: '普陀区', + 310108: '闸北区', + 310109: '虹口区', + 310110: '杨浦区', + 310112: '闵行区', + 310113: '宝山区', + 310114: '嘉定区', + 310115: '浦东新区', + 310116: '金山区', + 310117: '松江区', + 310118: '青浦区', + 310120: '奉贤区', + 310230: '崇明县', + 310231: '其它区', + 320000: '江苏省', + 320100: '南京市', + 320102: '玄武区', + 320104: '秦淮区', + 320105: '建邺区', + 320106: '鼓楼区', + 320111: '浦口区', + 320113: '栖霞区', + 320114: '雨花台区', + 320115: '江宁区', + 320116: '六合区', + 320124: '溧水区', + 320125: '高淳区', + 320126: '其它区', + 320200: '无锡市', + 320202: '崇安区', + 320203: '南长区', + 320204: '北塘区', + 320205: '锡山区', + 320206: '惠山区', + 320211: '滨湖区', + 320281: '江阴市', + 320282: '宜兴市', + 320297: '其它区', + 320300: '徐州市', + 320302: '鼓楼区', + 320303: '云龙区', + 320305: '贾汪区', + 320311: '泉山区', + 320321: '丰县', + 320322: '沛县', + 320323: '铜山区', + 320324: '睢宁县', + 320381: '新沂市', + 320382: '邳州市', + 320383: '其它区', + 320400: '常州市', + 320402: '天宁区', + 320404: '钟楼区', + 320405: '戚墅堰区', + 320411: '新北区', + 320412: '武进区', + 320481: '溧阳市', + 320482: '金坛市', + 320483: '其它区', + 320500: '苏州市', + 320505: '虎丘区', + 320506: '吴中区', + 320507: '相城区', + 320508: '姑苏区', + 320581: '常熟市', + 320582: '张家港市', + 320583: '昆山市', + 320584: '吴江区', + 320585: '太仓市', + 320596: '其它区', + 320600: '南通市', + 320602: '崇川区', + 320611: '港闸区', + 320612: '通州区', + 320621: '海安县', + 320623: '如东县', + 320681: '启东市', + 320682: '如皋市', + 320684: '海门市', + 320694: '其它区', + 320700: '连云港市', + 320703: '连云区', + 320705: '新浦区', + 320706: '海州区', + 320721: '赣榆县', + 320722: '东海县', + 320723: '灌云县', + 320724: '灌南县', + 320725: '其它区', + 320800: '淮安市', + 320802: '清河区', + 320803: '淮安区', + 320804: '淮阴区', + 320811: '清浦区', + 320826: '涟水县', + 320829: '洪泽县', + 320830: '盱眙县', + 320831: '金湖县', + 320832: '其它区', + 320900: '盐城市', + 320902: '亭湖区', + 320903: '盐都区', + 320921: '响水县', + 320922: '滨海县', + 320923: '阜宁县', + 320924: '射阳县', + 320925: '建湖县', + 320981: '东台市', + 320982: '大丰市', + 320983: '其它区', + 321000: '扬州市', + 321002: '广陵区', + 321003: '邗江区', + 321023: '宝应县', + 321081: '仪征市', + 321084: '高邮市', + 321088: '江都区', + 321093: '其它区', + 321100: '镇江市', + 321102: '京口区', + 321111: '润州区', + 321112: '丹徒区', + 321181: '丹阳市', + 321182: '扬中市', + 321183: '句容市', + 321184: '其它区', + 321200: '泰州市', + 321202: '海陵区', + 321203: '高港区', + 321281: '兴化市', + 321282: '靖江市', + 321283: '泰兴市', + 321284: '姜堰区', + 321285: '其它区', + 321300: '宿迁市', + 321302: '宿城区', + 321311: '宿豫区', + 321322: '沭阳县', + 321323: '泗阳县', + 321324: '泗洪县', + 321325: '其它区', + 330000: '浙江省', + 330100: '杭州市', + 330102: '上城区', + 330103: '下城区', + 330104: '江干区', + 330105: '拱墅区', + 330106: '西湖区', + 330108: '滨江区', + 330109: '萧山区', + 330110: '余杭区', + 330122: '桐庐县', + 330127: '淳安县', + 330182: '建德市', + 330183: '富阳市', + 330185: '临安市', + 330186: '其它区', + 330200: '宁波市', + 330203: '海曙区', + 330204: '江东区', + 330205: '江北区', + 330206: '北仑区', + 330211: '镇海区', + 330212: '鄞州区', + 330225: '象山县', + 330226: '宁海县', + 330281: '余姚市', + 330282: '慈溪市', + 330283: '奉化市', + 330284: '其它区', + 330300: '温州市', + 330302: '鹿城区', + 330303: '龙湾区', + 330304: '瓯海区', + 330322: '洞头县', + 330324: '永嘉县', + 330326: '平阳县', + 330327: '苍南县', + 330328: '文成县', + 330329: '泰顺县', + 330381: '瑞安市', + 330382: '乐清市', + 330383: '其它区', + 330400: '嘉兴市', + 330402: '南湖区', + 330411: '秀洲区', + 330421: '嘉善县', + 330424: '海盐县', + 330481: '海宁市', + 330482: '平湖市', + 330483: '桐乡市', + 330484: '其它区', + 330500: '湖州市', + 330502: '吴兴区', + 330503: '南浔区', + 330521: '德清县', + 330522: '长兴县', + 330523: '安吉县', + 330524: '其它区', + 330600: '绍兴市', + 330602: '越城区', + 330621: '绍兴县', + 330624: '新昌县', + 330681: '诸暨市', + 330682: '上虞市', + 330683: '嵊州市', + 330684: '其它区', + 330700: '金华市', + 330702: '婺城区', + 330703: '金东区', + 330723: '武义县', + 330726: '浦江县', + 330727: '磐安县', + 330781: '兰溪市', + 330782: '义乌市', + 330783: '东阳市', + 330784: '永康市', + 330785: '其它区', + 330800: '衢州市', + 330802: '柯城区', + 330803: '衢江区', + 330822: '常山县', + 330824: '开化县', + 330825: '龙游县', + 330881: '江山市', + 330882: '其它区', + 330900: '舟山市', + 330902: '定海区', + 330903: '普陀区', + 330921: '岱山县', + 330922: '嵊泗县', + 330923: '其它区', + 331000: '台州市', + 331002: '椒江区', + 331003: '黄岩区', + 331004: '路桥区', + 331021: '玉环县', + 331022: '三门县', + 331023: '天台县', + 331024: '仙居县', + 331081: '温岭市', + 331082: '临海市', + 331083: '其它区', + 331100: '丽水市', + 331102: '莲都区', + 331121: '青田县', + 331122: '缙云县', + 331123: '遂昌县', + 331124: '松阳县', + 331125: '云和县', + 331126: '庆元县', + 331127: '景宁畲族自治县', + 331181: '龙泉市', + 331182: '其它区', + 340000: '安徽省', + 340100: '合肥市', + 340102: '瑶海区', + 340103: '庐阳区', + 340104: '蜀山区', + 340111: '包河区', + 340121: '长丰县', + 340122: '肥东县', + 340123: '肥西县', + 340192: '其它区', + 340200: '芜湖市', + 340202: '镜湖区', + 340203: '弋江区', + 340207: '鸠江区', + 340208: '三山区', + 340221: '芜湖县', + 340222: '繁昌县', + 340223: '南陵县', + 340224: '其它区', + 340300: '蚌埠市', + 340302: '龙子湖区', + 340303: '蚌山区', + 340304: '禹会区', + 340311: '淮上区', + 340321: '怀远县', + 340322: '五河县', + 340323: '固镇县', + 340324: '其它区', + 340400: '淮南市', + 340402: '大通区', + 340403: '田家庵区', + 340404: '谢家集区', + 340405: '八公山区', + 340406: '潘集区', + 340421: '凤台县', + 340422: '其它区', + 340500: '马鞍山市', + 340503: '花山区', + 340504: '雨山区', + 340506: '博望区', + 340521: '当涂县', + 340522: '其它区', + 340600: '淮北市', + 340602: '杜集区', + 340603: '相山区', + 340604: '烈山区', + 340621: '濉溪县', + 340622: '其它区', + 340700: '铜陵市', + 340702: '铜官山区', + 340703: '狮子山区', + 340711: '郊区', + 340721: '铜陵县', + 340722: '其它区', + 340800: '安庆市', + 340802: '迎江区', + 340803: '大观区', + 340811: '宜秀区', + 340822: '怀宁县', + 340823: '枞阳县', + 340824: '潜山县', + 340825: '太湖县', + 340826: '宿松县', + 340827: '望江县', + 340828: '岳西县', + 340881: '桐城市', + 340882: '其它区', + 341000: '黄山市', + 341002: '屯溪区', + 341003: '黄山区', + 341004: '徽州区', + 341021: '歙县', + 341022: '休宁县', + 341023: '黟县', + 341024: '祁门县', + 341025: '其它区', + 341100: '滁州市', + 341102: '琅琊区', + 341103: '南谯区', + 341122: '来安县', + 341124: '全椒县', + 341125: '定远县', + 341126: '凤阳县', + 341181: '天长市', + 341182: '明光市', + 341183: '其它区', + 341200: '阜阳市', + 341202: '颍州区', + 341203: '颍东区', + 341204: '颍泉区', + 341221: '临泉县', + 341222: '太和县', + 341225: '阜南县', + 341226: '颍上县', + 341282: '界首市', + 341283: '其它区', + 341300: '宿州市', + 341302: '埇桥区', + 341321: '砀山县', + 341322: '萧县', + 341323: '灵璧县', + 341324: '泗县', + 341325: '其它区', + 341400: '巢湖市', + 341421: '庐江县', + 341422: '无为县', + 341423: '含山县', + 341424: '和县', + 341500: '六安市', + 341502: '金安区', + 341503: '裕安区', + 341521: '寿县', + 341522: '霍邱县', + 341523: '舒城县', + 341524: '金寨县', + 341525: '霍山县', + 341526: '其它区', + 341600: '亳州市', + 341602: '谯城区', + 341621: '涡阳县', + 341622: '蒙城县', + 341623: '利辛县', + 341624: '其它区', + 341700: '池州市', + 341702: '贵池区', + 341721: '东至县', + 341722: '石台县', + 341723: '青阳县', + 341724: '其它区', + 341800: '宣城市', + 341802: '宣州区', + 341821: '郎溪县', + 341822: '广德县', + 341823: '泾县', + 341824: '绩溪县', + 341825: '旌德县', + 341881: '宁国市', + 341882: '其它区', + 350000: '福建省', + 350100: '福州市', + 350102: '鼓楼区', + 350103: '台江区', + 350104: '仓山区', + 350105: '马尾区', + 350111: '晋安区', + 350121: '闽侯县', + 350122: '连江县', + 350123: '罗源县', + 350124: '闽清县', + 350125: '永泰县', + 350128: '平潭县', + 350181: '福清市', + 350182: '长乐市', + 350183: '其它区', + 350200: '厦门市', + 350203: '思明区', + 350205: '海沧区', + 350206: '湖里区', + 350211: '集美区', + 350212: '同安区', + 350213: '翔安区', + 350214: '其它区', + 350300: '莆田市', + 350302: '城厢区', + 350303: '涵江区', + 350304: '荔城区', + 350305: '秀屿区', + 350322: '仙游县', + 350323: '其它区', + 350400: '三明市', + 350402: '梅列区', + 350403: '三元区', + 350421: '明溪县', + 350423: '清流县', + 350424: '宁化县', + 350425: '大田县', + 350426: '尤溪县', + 350427: '沙县', + 350428: '将乐县', + 350429: '泰宁县', + 350430: '建宁县', + 350481: '永安市', + 350482: '其它区', + 350500: '泉州市', + 350502: '鲤城区', + 350503: '丰泽区', + 350504: '洛江区', + 350505: '泉港区', + 350521: '惠安县', + 350524: '安溪县', + 350525: '永春县', + 350526: '德化县', + 350527: '金门县', + 350581: '石狮市', + 350582: '晋江市', + 350583: '南安市', + 350584: '其它区', + 350600: '漳州市', + 350602: '芗城区', + 350603: '龙文区', + 350622: '云霄县', + 350623: '漳浦县', + 350624: '诏安县', + 350625: '长泰县', + 350626: '东山县', + 350627: '南靖县', + 350628: '平和县', + 350629: '华安县', + 350681: '龙海市', + 350682: '其它区', + 350700: '南平市', + 350702: '延平区', + 350721: '顺昌县', + 350722: '浦城县', + 350723: '光泽县', + 350724: '松溪县', + 350725: '政和县', + 350781: '邵武市', + 350782: '武夷山市', + 350783: '建瓯市', + 350784: '建阳市', + 350785: '其它区', + 350800: '龙岩市', + 350802: '新罗区', + 350821: '长汀县', + 350822: '永定县', + 350823: '上杭县', + 350824: '武平县', + 350825: '连城县', + 350881: '漳平市', + 350882: '其它区', + 350900: '宁德市', + 350902: '蕉城区', + 350921: '霞浦县', + 350922: '古田县', + 350923: '屏南县', + 350924: '寿宁县', + 350925: '周宁县', + 350926: '柘荣县', + 350981: '福安市', + 350982: '福鼎市', + 350983: '其它区', + 360000: '江西省', + 360100: '南昌市', + 360102: '东湖区', + 360103: '西湖区', + 360104: '青云谱区', + 360105: '湾里区', + 360111: '青山湖区', + 360121: '南昌县', + 360122: '新建县', + 360123: '安义县', + 360124: '进贤县', + 360128: '其它区', + 360200: '景德镇市', + 360202: '昌江区', + 360203: '珠山区', + 360222: '浮梁县', + 360281: '乐平市', + 360282: '其它区', + 360300: '萍乡市', + 360302: '安源区', + 360313: '湘东区', + 360321: '莲花县', + 360322: '上栗县', + 360323: '芦溪县', + 360324: '其它区', + 360400: '九江市', + 360402: '庐山区', + 360403: '浔阳区', + 360421: '九江县', + 360423: '武宁县', + 360424: '修水县', + 360425: '永修县', + 360426: '德安县', + 360427: '星子县', + 360428: '都昌县', + 360429: '湖口县', + 360430: '彭泽县', + 360481: '瑞昌市', + 360482: '其它区', + 360483: '共青城市', + 360500: '新余市', + 360502: '渝水区', + 360521: '分宜县', + 360522: '其它区', + 360600: '鹰潭市', + 360602: '月湖区', + 360622: '余江县', + 360681: '贵溪市', + 360682: '其它区', + 360700: '赣州市', + 360702: '章贡区', + 360721: '赣县', + 360722: '信丰县', + 360723: '大余县', + 360724: '上犹县', + 360725: '崇义县', + 360726: '安远县', + 360727: '龙南县', + 360728: '定南县', + 360729: '全南县', + 360730: '宁都县', + 360731: '于都县', + 360732: '兴国县', + 360733: '会昌县', + 360734: '寻乌县', + 360735: '石城县', + 360781: '瑞金市', + 360782: '南康市', + 360783: '其它区', + 360800: '吉安市', + 360802: '吉州区', + 360803: '青原区', + 360821: '吉安县', + 360822: '吉水县', + 360823: '峡江县', + 360824: '新干县', + 360825: '永丰县', + 360826: '泰和县', + 360827: '遂川县', + 360828: '万安县', + 360829: '安福县', + 360830: '永新县', + 360881: '井冈山市', + 360882: '其它区', + 360900: '宜春市', + 360902: '袁州区', + 360921: '奉新县', + 360922: '万载县', + 360923: '上高县', + 360924: '宜丰县', + 360925: '靖安县', + 360926: '铜鼓县', + 360981: '丰城市', + 360982: '樟树市', + 360983: '高安市', + 360984: '其它区', + 361000: '抚州市', + 361002: '临川区', + 361021: '南城县', + 361022: '黎川县', + 361023: '南丰县', + 361024: '崇仁县', + 361025: '乐安县', + 361026: '宜黄县', + 361027: '金溪县', + 361028: '资溪县', + 361029: '东乡县', + 361030: '广昌县', + 361031: '其它区', + 361100: '上饶市', + 361102: '信州区', + 361121: '上饶县', + 361122: '广丰县', + 361123: '玉山县', + 361124: '铅山县', + 361125: '横峰县', + 361126: '弋阳县', + 361127: '余干县', + 361128: '鄱阳县', + 361129: '万年县', + 361130: '婺源县', + 361181: '德兴市', + 361182: '其它区', + 370000: '山东省', + 370100: '济南市', + 370102: '历下区', + 370103: '市中区', + 370104: '槐荫区', + 370105: '天桥区', + 370112: '历城区', + 370113: '长清区', + 370124: '平阴县', + 370125: '济阳县', + 370126: '商河县', + 370181: '章丘市', + 370182: '其它区', + 370200: '青岛市', + 370202: '市南区', + 370203: '市北区', + 370211: '黄岛区', + 370212: '崂山区', + 370213: '李沧区', + 370214: '城阳区', + 370281: '胶州市', + 370282: '即墨市', + 370283: '平度市', + 370285: '莱西市', + 370286: '其它区', + 370300: '淄博市', + 370302: '淄川区', + 370303: '张店区', + 370304: '博山区', + 370305: '临淄区', + 370306: '周村区', + 370321: '桓台县', + 370322: '高青县', + 370323: '沂源县', + 370324: '其它区', + 370400: '枣庄市', + 370402: '市中区', + 370403: '薛城区', + 370404: '峄城区', + 370405: '台儿庄区', + 370406: '山亭区', + 370481: '滕州市', + 370482: '其它区', + 370500: '东营市', + 370502: '东营区', + 370503: '河口区', + 370521: '垦利县', + 370522: '利津县', + 370523: '广饶县', + 370591: '其它区', + 370600: '烟台市', + 370602: '芝罘区', + 370611: '福山区', + 370612: '牟平区', + 370613: '莱山区', + 370634: '长岛县', + 370681: '龙口市', + 370682: '莱阳市', + 370683: '莱州市', + 370684: '蓬莱市', + 370685: '招远市', + 370686: '栖霞市', + 370687: '海阳市', + 370688: '其它区', + 370700: '潍坊市', + 370702: '潍城区', + 370703: '寒亭区', + 370704: '坊子区', + 370705: '奎文区', + 370724: '临朐县', + 370725: '昌乐县', + 370781: '青州市', + 370782: '诸城市', + 370783: '寿光市', + 370784: '安丘市', + 370785: '高密市', + 370786: '昌邑市', + 370787: '其它区', + 370800: '济宁市', + 370802: '市中区', + 370811: '任城区', + 370826: '微山县', + 370827: '鱼台县', + 370828: '金乡县', + 370829: '嘉祥县', + 370830: '汶上县', + 370831: '泗水县', + 370832: '梁山县', + 370881: '曲阜市', + 370882: '兖州市', + 370883: '邹城市', + 370884: '其它区', + 370900: '泰安市', + 370902: '泰山区', + 370903: '岱岳区', + 370921: '宁阳县', + 370923: '东平县', + 370982: '新泰市', + 370983: '肥城市', + 370984: '其它区', + 371000: '威海市', + 371002: '环翠区', + 371081: '文登市', + 371082: '荣成市', + 371083: '乳山市', + 371084: '其它区', + 371100: '日照市', + 371102: '东港区', + 371103: '岚山区', + 371121: '五莲县', + 371122: '莒县', + 371123: '其它区', + 371200: '莱芜市', + 371202: '莱城区', + 371203: '钢城区', + 371204: '其它区', + 371300: '临沂市', + 371302: '兰山区', + 371311: '罗庄区', + 371312: '河东区', + 371321: '沂南县', + 371322: '郯城县', + 371323: '沂水县', + 371324: '苍山县', + 371325: '费县', + 371326: '平邑县', + 371327: '莒南县', + 371328: '蒙阴县', + 371329: '临沭县', + 371330: '其它区', + 371400: '德州市', + 371402: '德城区', + 371421: '陵县', + 371422: '宁津县', + 371423: '庆云县', + 371424: '临邑县', + 371425: '齐河县', + 371426: '平原县', + 371427: '夏津县', + 371428: '武城县', + 371481: '乐陵市', + 371482: '禹城市', + 371483: '其它区', + 371500: '聊城市', + 371502: '东昌府区', + 371521: '阳谷县', + 371522: '莘县', + 371523: '茌平县', + 371524: '东阿县', + 371525: '冠县', + 371526: '高唐县', + 371581: '临清市', + 371582: '其它区', + 371600: '滨州市', + 371602: '滨城区', + 371621: '惠民县', + 371622: '阳信县', + 371623: '无棣县', + 371624: '沾化县', + 371625: '博兴县', + 371626: '邹平县', + 371627: '其它区', + 371700: '菏泽市', + 371702: '牡丹区', + 371721: '曹县', + 371722: '单县', + 371723: '成武县', + 371724: '巨野县', + 371725: '郓城县', + 371726: '鄄城县', + 371727: '定陶县', + 371728: '东明县', + 371729: '其它区', + 410000: '河南省', + 410100: '郑州市', + 410102: '中原区', + 410103: '二七区', + 410104: '管城回族区', + 410105: '金水区', + 410106: '上街区', + 410108: '惠济区', + 410122: '中牟县', + 410181: '巩义市', + 410182: '荥阳市', + 410183: '新密市', + 410184: '新郑市', + 410185: '登封市', + 410188: '其它区', + 410200: '开封市', + 410202: '龙亭区', + 410203: '顺河回族区', + 410204: '鼓楼区', + 410205: '禹王台区', + 410211: '金明区', + 410221: '杞县', + 410222: '通许县', + 410223: '尉氏县', + 410224: '开封县', + 410225: '兰考县', + 410226: '其它区', + 410300: '洛阳市', + 410302: '老城区', + 410303: '西工区', + 410304: '瀍河回族区', + 410305: '涧西区', + 410306: '吉利区', + 410307: '洛龙区', + 410322: '孟津县', + 410323: '新安县', + 410324: '栾川县', + 410325: '嵩县', + 410326: '汝阳县', + 410327: '宜阳县', + 410328: '洛宁县', + 410329: '伊川县', + 410381: '偃师市', + 410400: '平顶山市', + 410402: '新华区', + 410403: '卫东区', + 410404: '石龙区', + 410411: '湛河区', + 410421: '宝丰县', + 410422: '叶县', + 410423: '鲁山县', + 410425: '郏县', + 410481: '舞钢市', + 410482: '汝州市', + 410483: '其它区', + 410500: '安阳市', + 410502: '文峰区', + 410503: '北关区', + 410505: '殷都区', + 410506: '龙安区', + 410522: '安阳县', + 410523: '汤阴县', + 410526: '滑县', + 410527: '内黄县', + 410581: '林州市', + 410582: '其它区', + 410600: '鹤壁市', + 410602: '鹤山区', + 410603: '山城区', + 410611: '淇滨区', + 410621: '浚县', + 410622: '淇县', + 410623: '其它区', + 410700: '新乡市', + 410702: '红旗区', + 410703: '卫滨区', + 410704: '凤泉区', + 410711: '牧野区', + 410721: '新乡县', + 410724: '获嘉县', + 410725: '原阳县', + 410726: '延津县', + 410727: '封丘县', + 410728: '长垣县', + 410781: '卫辉市', + 410782: '辉县市', + 410783: '其它区', + 410800: '焦作市', + 410802: '解放区', + 410803: '中站区', + 410804: '马村区', + 410811: '山阳区', + 410821: '修武县', + 410822: '博爱县', + 410823: '武陟县', + 410825: '温县', + 410881: '济源市', + 410882: '沁阳市', + 410883: '孟州市', + 410884: '其它区', + 410900: '濮阳市', + 410902: '华龙区', + 410922: '清丰县', + 410923: '南乐县', + 410926: '范县', + 410927: '台前县', + 410928: '濮阳县', + 410929: '其它区', + 411000: '许昌市', + 411002: '魏都区', + 411023: '许昌县', + 411024: '鄢陵县', + 411025: '襄城县', + 411081: '禹州市', + 411082: '长葛市', + 411083: '其它区', + 411100: '漯河市', + 411102: '源汇区', + 411103: '郾城区', + 411104: '召陵区', + 411121: '舞阳县', + 411122: '临颍县', + 411123: '其它区', + 411200: '三门峡市', + 411202: '湖滨区', + 411221: '渑池县', + 411222: '陕县', + 411224: '卢氏县', + 411281: '义马市', + 411282: '灵宝市', + 411283: '其它区', + 411300: '南阳市', + 411302: '宛城区', + 411303: '卧龙区', + 411321: '南召县', + 411322: '方城县', + 411323: '西峡县', + 411324: '镇平县', + 411325: '内乡县', + 411326: '淅川县', + 411327: '社旗县', + 411328: '唐河县', + 411329: '新野县', + 411330: '桐柏县', + 411381: '邓州市', + 411382: '其它区', + 411400: '商丘市', + 411402: '梁园区', + 411403: '睢阳区', + 411421: '民权县', + 411422: '睢县', + 411423: '宁陵县', + 411424: '柘城县', + 411425: '虞城县', + 411426: '夏邑县', + 411481: '永城市', + 411482: '其它区', + 411500: '信阳市', + 411502: '浉河区', + 411503: '平桥区', + 411521: '罗山县', + 411522: '光山县', + 411523: '新县', + 411524: '商城县', + 411525: '固始县', + 411526: '潢川县', + 411527: '淮滨县', + 411528: '息县', + 411529: '其它区', + 411600: '周口市', + 411602: '川汇区', + 411621: '扶沟县', + 411622: '西华县', + 411623: '商水县', + 411624: '沈丘县', + 411625: '郸城县', + 411626: '淮阳县', + 411627: '太康县', + 411628: '鹿邑县', + 411681: '项城市', + 411682: '其它区', + 411700: '驻马店市', + 411702: '驿城区', + 411721: '西平县', + 411722: '上蔡县', + 411723: '平舆县', + 411724: '正阳县', + 411725: '确山县', + 411726: '泌阳县', + 411727: '汝南县', + 411728: '遂平县', + 411729: '新蔡县', + 411730: '其它区', + 420000: '湖北省', + 420100: '武汉市', + 420102: '江岸区', + 420103: '江汉区', + 420104: '硚口区', + 420105: '汉阳区', + 420106: '武昌区', + 420107: '青山区', + 420111: '洪山区', + 420112: '东西湖区', + 420113: '汉南区', + 420114: '蔡甸区', + 420115: '江夏区', + 420116: '黄陂区', + 420117: '新洲区', + 420118: '其它区', + 420200: '黄石市', + 420202: '黄石港区', + 420203: '西塞山区', + 420204: '下陆区', + 420205: '铁山区', + 420222: '阳新县', + 420281: '大冶市', + 420282: '其它区', + 420300: '十堰市', + 420302: '茅箭区', + 420303: '张湾区', + 420321: '郧县', + 420322: '郧西县', + 420323: '竹山县', + 420324: '竹溪县', + 420325: '房县', + 420381: '丹江口市', + 420383: '其它区', + 420500: '宜昌市', + 420502: '西陵区', + 420503: '伍家岗区', + 420504: '点军区', + 420505: '猇亭区', + 420506: '夷陵区', + 420525: '远安县', + 420526: '兴山县', + 420527: '秭归县', + 420528: '长阳土家族自治县', + 420529: '五峰土家族自治县', + 420581: '宜都市', + 420582: '当阳市', + 420583: '枝江市', + 420584: '其它区', + 420600: '襄阳市', + 420602: '襄城区', + 420606: '樊城区', + 420607: '襄州区', + 420624: '南漳县', + 420625: '谷城县', + 420626: '保康县', + 420682: '老河口市', + 420683: '枣阳市', + 420684: '宜城市', + 420685: '其它区', + 420700: '鄂州市', + 420702: '梁子湖区', + 420703: '华容区', + 420704: '鄂城区', + 420705: '其它区', + 420800: '荆门市', + 420802: '东宝区', + 420804: '掇刀区', + 420821: '京山县', + 420822: '沙洋县', + 420881: '钟祥市', + 420882: '其它区', + 420900: '孝感市', + 420902: '孝南区', + 420921: '孝昌县', + 420922: '大悟县', + 420923: '云梦县', + 420981: '应城市', + 420982: '安陆市', + 420984: '汉川市', + 420985: '其它区', + 421000: '荆州市', + 421002: '沙市区', + 421003: '荆州区', + 421022: '公安县', + 421023: '监利县', + 421024: '江陵县', + 421081: '石首市', + 421083: '洪湖市', + 421087: '松滋市', + 421088: '其它区', + 421100: '黄冈市', + 421102: '黄州区', + 421121: '团风县', + 421122: '红安县', + 421123: '罗田县', + 421124: '英山县', + 421125: '浠水县', + 421126: '蕲春县', + 421127: '黄梅县', + 421181: '麻城市', + 421182: '武穴市', + 421183: '其它区', + 421200: '咸宁市', + 421202: '咸安区', + 421221: '嘉鱼县', + 421222: '通城县', + 421223: '崇阳县', + 421224: '通山县', + 421281: '赤壁市', + 421283: '其它区', + 421300: '随州市', + 421302: '曾都区', + 421321: '随县', + 421381: '广水市', + 421382: '其它区', + 422800: '恩施土家族苗族自治州', + 422801: '恩施市', + 422802: '利川市', + 422822: '建始县', + 422823: '巴东县', + 422825: '宣恩县', + 422826: '咸丰县', + 422827: '来凤县', + 422828: '鹤峰县', + 422829: '其它区', + 429004: '仙桃市', + 429005: '潜江市', + 429006: '天门市', + 429021: '神农架林区', + 430000: '湖南省', + 430100: '长沙市', + 430102: '芙蓉区', + 430103: '天心区', + 430104: '岳麓区', + 430105: '开福区', + 430111: '雨花区', + 430121: '长沙县', + 430122: '望城区', + 430124: '宁乡县', + 430181: '浏阳市', + 430182: '其它区', + 430200: '株洲市', + 430202: '荷塘区', + 430203: '芦淞区', + 430204: '石峰区', + 430211: '天元区', + 430221: '株洲县', + 430223: '攸县', + 430224: '茶陵县', + 430225: '炎陵县', + 430281: '醴陵市', + 430282: '其它区', + 430300: '湘潭市', + 430302: '雨湖区', + 430304: '岳塘区', + 430321: '湘潭县', + 430381: '湘乡市', + 430382: '韶山市', + 430383: '其它区', + 430400: '衡阳市', + 430405: '珠晖区', + 430406: '雁峰区', + 430407: '石鼓区', + 430408: '蒸湘区', + 430412: '南岳区', + 430421: '衡阳县', + 430422: '衡南县', + 430423: '衡山县', + 430424: '衡东县', + 430426: '祁东县', + 430481: '耒阳市', + 430482: '常宁市', + 430483: '其它区', + 430500: '邵阳市', + 430502: '双清区', + 430503: '大祥区', + 430511: '北塔区', + 430521: '邵东县', + 430522: '新邵县', + 430523: '邵阳县', + 430524: '隆回县', + 430525: '洞口县', + 430527: '绥宁县', + 430528: '新宁县', + 430529: '城步苗族自治县', + 430581: '武冈市', + 430582: '其它区', + 430600: '岳阳市', + 430602: '岳阳楼区', + 430603: '云溪区', + 430611: '君山区', + 430621: '岳阳县', + 430623: '华容县', + 430624: '湘阴县', + 430626: '平江县', + 430681: '汨罗市', + 430682: '临湘市', + 430683: '其它区', + 430700: '常德市', + 430702: '武陵区', + 430703: '鼎城区', + 430721: '安乡县', + 430722: '汉寿县', + 430723: '澧县', + 430724: '临澧县', + 430725: '桃源县', + 430726: '石门县', + 430781: '津市市', + 430782: '其它区', + 430800: '张家界市', + 430802: '永定区', + 430811: '武陵源区', + 430821: '慈利县', + 430822: '桑植县', + 430823: '其它区', + 430900: '益阳市', + 430902: '资阳区', + 430903: '赫山区', + 430921: '南县', + 430922: '桃江县', + 430923: '安化县', + 430981: '沅江市', + 430982: '其它区', + 431000: '郴州市', + 431002: '北湖区', + 431003: '苏仙区', + 431021: '桂阳县', + 431022: '宜章县', + 431023: '永兴县', + 431024: '嘉禾县', + 431025: '临武县', + 431026: '汝城县', + 431027: '桂东县', + 431028: '安仁县', + 431081: '资兴市', + 431082: '其它区', + 431100: '永州市', + 431102: '零陵区', + 431103: '冷水滩区', + 431121: '祁阳县', + 431122: '东安县', + 431123: '双牌县', + 431124: '道县', + 431125: '江永县', + 431126: '宁远县', + 431127: '蓝山县', + 431128: '新田县', + 431129: '江华瑶族自治县', + 431130: '其它区', + 431200: '怀化市', + 431202: '鹤城区', + 431221: '中方县', + 431222: '沅陵县', + 431223: '辰溪县', + 431224: '溆浦县', + 431225: '会同县', + 431226: '麻阳苗族自治县', + 431227: '新晃侗族自治县', + 431228: '芷江侗族自治县', + 431229: '靖州苗族侗族自治县', + 431230: '通道侗族自治县', + 431281: '洪江市', + 431282: '其它区', + 431300: '娄底市', + 431302: '娄星区', + 431321: '双峰县', + 431322: '新化县', + 431381: '冷水江市', + 431382: '涟源市', + 431383: '其它区', + 433100: '湘西土家族苗族自治州', + 433101: '吉首市', + 433122: '泸溪县', + 433123: '凤凰县', + 433124: '花垣县', + 433125: '保靖县', + 433126: '古丈县', + 433127: '永顺县', + 433130: '龙山县', + 433131: '其它区', + 440000: '广东省', + 440100: '广州市', + 440103: '荔湾区', + 440104: '越秀区', + 440105: '海珠区', + 440106: '天河区', + 440111: '白云区', + 440112: '黄埔区', + 440113: '番禺区', + 440114: '花都区', + 440115: '南沙区', + 440116: '萝岗区', + 440183: '增城市', + 440184: '从化市', + 440189: '其它区', + 440200: '韶关市', + 440203: '武江区', + 440204: '浈江区', + 440205: '曲江区', + 440222: '始兴县', + 440224: '仁化县', + 440229: '翁源县', + 440232: '乳源瑶族自治县', + 440233: '新丰县', + 440281: '乐昌市', + 440282: '南雄市', + 440283: '其它区', + 440300: '深圳市', + 440303: '罗湖区', + 440304: '福田区', + 440305: '南山区', + 440306: '宝安区', + 440307: '龙岗区', + 440308: '盐田区', + 440309: '其它区', + 440320: '光明新区', + 440321: '坪山新区', + 440322: '大鹏新区', + 440323: '龙华新区', + 440400: '珠海市', + 440402: '香洲区', + 440403: '斗门区', + 440404: '金湾区', + 440488: '其它区', + 440500: '汕头市', + 440507: '龙湖区', + 440511: '金平区', + 440512: '濠江区', + 440513: '潮阳区', + 440514: '潮南区', + 440515: '澄海区', + 440523: '南澳县', + 440524: '其它区', + 440600: '佛山市', + 440604: '禅城区', + 440605: '南海区', + 440606: '顺德区', + 440607: '三水区', + 440608: '高明区', + 440609: '其它区', + 440700: '江门市', + 440703: '蓬江区', + 440704: '江海区', + 440705: '新会区', + 440781: '台山市', + 440783: '开平市', + 440784: '鹤山市', + 440785: '恩平市', + 440786: '其它区', + 440800: '湛江市', + 440802: '赤坎区', + 440803: '霞山区', + 440804: '坡头区', + 440811: '麻章区', + 440823: '遂溪县', + 440825: '徐闻县', + 440881: '廉江市', + 440882: '雷州市', + 440883: '吴川市', + 440884: '其它区', + 440900: '茂名市', + 440902: '茂南区', + 440903: '茂港区', + 440923: '电白县', + 440981: '高州市', + 440982: '化州市', + 440983: '信宜市', + 440984: '其它区', + 441200: '肇庆市', + 441202: '端州区', + 441203: '鼎湖区', + 441223: '广宁县', + 441224: '怀集县', + 441225: '封开县', + 441226: '德庆县', + 441283: '高要市', + 441284: '四会市', + 441285: '其它区', + 441300: '惠州市', + 441302: '惠城区', + 441303: '惠阳区', + 441322: '博罗县', + 441323: '惠东县', + 441324: '龙门县', + 441325: '其它区', + 441400: '梅州市', + 441402: '梅江区', + 441421: '梅县', + 441422: '大埔县', + 441423: '丰顺县', + 441424: '五华县', + 441426: '平远县', + 441427: '蕉岭县', + 441481: '兴宁市', + 441482: '其它区', + 441500: '汕尾市', + 441502: '城区', + 441521: '海丰县', + 441523: '陆河县', + 441581: '陆丰市', + 441582: '其它区', + 441600: '河源市', + 441602: '源城区', + 441621: '紫金县', + 441622: '龙川县', + 441623: '连平县', + 441624: '和平县', + 441625: '东源县', + 441626: '其它区', + 441700: '阳江市', + 441702: '江城区', + 441721: '阳西县', + 441723: '阳东县', + 441781: '阳春市', + 441782: '其它区', + 441800: '清远市', + 441802: '清城区', + 441821: '佛冈县', + 441823: '阳山县', + 441825: '连山壮族瑶族自治县', + 441826: '连南瑶族自治县', + 441827: '清新区', + 441881: '英德市', + 441882: '连州市', + 441883: '其它区', + 441900: '东莞市', + 442000: '中山市', + 442101: '东沙群岛', + 445100: '潮州市', + 445102: '湘桥区', + 445121: '潮安区', + 445122: '饶平县', + 445186: '其它区', + 445200: '揭阳市', + 445202: '榕城区', + 445221: '揭东区', + 445222: '揭西县', + 445224: '惠来县', + 445281: '普宁市', + 445285: '其它区', + 445300: '云浮市', + 445302: '云城区', + 445321: '新兴县', + 445322: '郁南县', + 445323: '云安县', + 445381: '罗定市', + 445382: '其它区', + 450000: '广西壮族自治区', + 450100: '南宁市', + 450102: '兴宁区', + 450103: '青秀区', + 450105: '江南区', + 450107: '西乡塘区', + 450108: '良庆区', + 450109: '邕宁区', + 450122: '武鸣县', + 450123: '隆安县', + 450124: '马山县', + 450125: '上林县', + 450126: '宾阳县', + 450127: '横县', + 450128: '其它区', + 450200: '柳州市', + 450202: '城中区', + 450203: '鱼峰区', + 450204: '柳南区', + 450205: '柳北区', + 450221: '柳江县', + 450222: '柳城县', + 450223: '鹿寨县', + 450224: '融安县', + 450225: '融水苗族自治县', + 450226: '三江侗族自治县', + 450227: '其它区', + 450300: '桂林市', + 450302: '秀峰区', + 450303: '叠彩区', + 450304: '象山区', + 450305: '七星区', + 450311: '雁山区', + 450321: '阳朔县', + 450322: '临桂区', + 450323: '灵川县', + 450324: '全州县', + 450325: '兴安县', + 450326: '永福县', + 450327: '灌阳县', + 450328: '龙胜各族自治县', + 450329: '资源县', + 450330: '平乐县', + 450331: '荔浦县', + 450332: '恭城瑶族自治县', + 450333: '其它区', + 450400: '梧州市', + 450403: '万秀区', + 450405: '长洲区', + 450406: '龙圩区', + 450421: '苍梧县', + 450422: '藤县', + 450423: '蒙山县', + 450481: '岑溪市', + 450482: '其它区', + 450500: '北海市', + 450502: '海城区', + 450503: '银海区', + 450512: '铁山港区', + 450521: '合浦县', + 450522: '其它区', + 450600: '防城港市', + 450602: '港口区', + 450603: '防城区', + 450621: '上思县', + 450681: '东兴市', + 450682: '其它区', + 450700: '钦州市', + 450702: '钦南区', + 450703: '钦北区', + 450721: '灵山县', + 450722: '浦北县', + 450723: '其它区', + 450800: '贵港市', + 450802: '港北区', + 450803: '港南区', + 450804: '覃塘区', + 450821: '平南县', + 450881: '桂平市', + 450882: '其它区', + 450900: '玉林市', + 450902: '玉州区', + 450903: '福绵区', + 450921: '容县', + 450922: '陆川县', + 450923: '博白县', + 450924: '兴业县', + 450981: '北流市', + 450982: '其它区', + 451000: '百色市', + 451002: '右江区', + 451021: '田阳县', + 451022: '田东县', + 451023: '平果县', + 451024: '德保县', + 451025: '靖西县', + 451026: '那坡县', + 451027: '凌云县', + 451028: '乐业县', + 451029: '田林县', + 451030: '西林县', + 451031: '隆林各族自治县', + 451032: '其它区', + 451100: '贺州市', + 451102: '八步区', + 451119: '平桂管理区', + 451121: '昭平县', + 451122: '钟山县', + 451123: '富川瑶族自治县', + 451124: '其它区', + 451200: '河池市', + 451202: '金城江区', + 451221: '南丹县', + 451222: '天峨县', + 451223: '凤山县', + 451224: '东兰县', + 451225: '罗城仫佬族自治县', + 451226: '环江毛南族自治县', + 451227: '巴马瑶族自治县', + 451228: '都安瑶族自治县', + 451229: '大化瑶族自治县', + 451281: '宜州市', + 451282: '其它区', + 451300: '来宾市', + 451302: '兴宾区', + 451321: '忻城县', + 451322: '象州县', + 451323: '武宣县', + 451324: '金秀瑶族自治县', + 451381: '合山市', + 451382: '其它区', + 451400: '崇左市', + 451402: '江州区', + 451421: '扶绥县', + 451422: '宁明县', + 451423: '龙州县', + 451424: '大新县', + 451425: '天等县', + 451481: '凭祥市', + 451482: '其它区', + 460000: '海南省', + 460100: '海口市', + 460105: '秀英区', + 460106: '龙华区', + 460107: '琼山区', + 460108: '美兰区', + 460109: '其它区', + 460200: '三亚市', + 460300: '三沙市', + 460321: '西沙群岛', + 460322: '南沙群岛', + 460323: '中沙群岛的岛礁及其海域', + 469001: '五指山市', + 469002: '琼海市', + 469003: '儋州市', + 469005: '文昌市', + 469006: '万宁市', + 469007: '东方市', + 469025: '定安县', + 469026: '屯昌县', + 469027: '澄迈县', + 469028: '临高县', + 469030: '白沙黎族自治县', + 469031: '昌江黎族自治县', + 469033: '乐东黎族自治县', + 469034: '陵水黎族自治县', + 469035: '保亭黎族苗族自治县', + 469036: '琼中黎族苗族自治县', + 471005: '其它区', + 500000: '重庆', + 500100: '重庆市', + 500101: '万州区', + 500102: '涪陵区', + 500103: '渝中区', + 500104: '大渡口区', + 500105: '江北区', + 500106: '沙坪坝区', + 500107: '九龙坡区', + 500108: '南岸区', + 500109: '北碚区', + 500110: '万盛区', + 500111: '双桥区', + 500112: '渝北区', + 500113: '巴南区', + 500114: '黔江区', + 500115: '长寿区', + 500222: '綦江区', + 500223: '潼南县', + 500224: '铜梁县', + 500225: '大足区', + 500226: '荣昌县', + 500227: '璧山县', + 500228: '梁平县', + 500229: '城口县', + 500230: '丰都县', + 500231: '垫江县', + 500232: '武隆县', + 500233: '忠县', + 500234: '开县', + 500235: '云阳县', + 500236: '奉节县', + 500237: '巫山县', + 500238: '巫溪县', + 500240: '石柱土家族自治县', + 500241: '秀山土家族苗族自治县', + 500242: '酉阳土家族苗族自治县', + 500243: '彭水苗族土家族自治县', + 500381: '江津区', + 500382: '合川区', + 500383: '永川区', + 500384: '南川区', + 500385: '其它区', + 510000: '四川省', + 510100: '成都市', + 510104: '锦江区', + 510105: '青羊区', + 510106: '金牛区', + 510107: '武侯区', + 510108: '成华区', + 510112: '龙泉驿区', + 510113: '青白江区', + 510114: '新都区', + 510115: '温江区', + 510121: '金堂县', + 510122: '双流县', + 510124: '郫县', + 510129: '大邑县', + 510131: '蒲江县', + 510132: '新津县', + 510181: '都江堰市', + 510182: '彭州市', + 510183: '邛崃市', + 510184: '崇州市', + 510185: '其它区', + 510300: '自贡市', + 510302: '自流井区', + 510303: '贡井区', + 510304: '大安区', + 510311: '沿滩区', + 510321: '荣县', + 510322: '富顺县', + 510323: '其它区', + 510400: '攀枝花市', + 510402: '东区', + 510403: '西区', + 510411: '仁和区', + 510421: '米易县', + 510422: '盐边县', + 510423: '其它区', + 510500: '泸州市', + 510502: '江阳区', + 510503: '纳溪区', + 510504: '龙马潭区', + 510521: '泸县', + 510522: '合江县', + 510524: '叙永县', + 510525: '古蔺县', + 510526: '其它区', + 510600: '德阳市', + 510603: '旌阳区', + 510623: '中江县', + 510626: '罗江县', + 510681: '广汉市', + 510682: '什邡市', + 510683: '绵竹市', + 510684: '其它区', + 510700: '绵阳市', + 510703: '涪城区', + 510704: '游仙区', + 510722: '三台县', + 510723: '盐亭县', + 510724: '安县', + 510725: '梓潼县', + 510726: '北川羌族自治县', + 510727: '平武县', + 510781: '江油市', + 510782: '其它区', + 510800: '广元市', + 510802: '利州区', + 510811: '昭化区', + 510812: '朝天区', + 510821: '旺苍县', + 510822: '青川县', + 510823: '剑阁县', + 510824: '苍溪县', + 510825: '其它区', + 510900: '遂宁市', + 510903: '船山区', + 510904: '安居区', + 510921: '蓬溪县', + 510922: '射洪县', + 510923: '大英县', + 510924: '其它区', + 511000: '内江市', + 511002: '市中区', + 511011: '东兴区', + 511024: '威远县', + 511025: '资中县', + 511028: '隆昌县', + 511029: '其它区', + 511100: '乐山市', + 511102: '市中区', + 511111: '沙湾区', + 511112: '五通桥区', + 511113: '金口河区', + 511123: '犍为县', + 511124: '井研县', + 511126: '夹江县', + 511129: '沐川县', + 511132: '峨边彝族自治县', + 511133: '马边彝族自治县', + 511181: '峨眉山市', + 511182: '其它区', + 511300: '南充市', + 511302: '顺庆区', + 511303: '高坪区', + 511304: '嘉陵区', + 511321: '南部县', + 511322: '营山县', + 511323: '蓬安县', + 511324: '仪陇县', + 511325: '西充县', + 511381: '阆中市', + 511382: '其它区', + 511400: '眉山市', + 511402: '东坡区', + 511421: '仁寿县', + 511422: '彭山县', + 511423: '洪雅县', + 511424: '丹棱县', + 511425: '青神县', + 511426: '其它区', + 511500: '宜宾市', + 511502: '翠屏区', + 511521: '宜宾县', + 511522: '南溪区', + 511523: '江安县', + 511524: '长宁县', + 511525: '高县', + 511526: '珙县', + 511527: '筠连县', + 511528: '兴文县', + 511529: '屏山县', + 511530: '其它区', + 511600: '广安市', + 511602: '广安区', + 511603: '前锋区', + 511621: '岳池县', + 511622: '武胜县', + 511623: '邻水县', + 511681: '华蓥市', + 511683: '其它区', + 511700: '达州市', + 511702: '通川区', + 511721: '达川区', + 511722: '宣汉县', + 511723: '开江县', + 511724: '大竹县', + 511725: '渠县', + 511781: '万源市', + 511782: '其它区', + 511800: '雅安市', + 511802: '雨城区', + 511821: '名山区', + 511822: '荥经县', + 511823: '汉源县', + 511824: '石棉县', + 511825: '天全县', + 511826: '芦山县', + 511827: '宝兴县', + 511828: '其它区', + 511900: '巴中市', + 511902: '巴州区', + 511903: '恩阳区', + 511921: '通江县', + 511922: '南江县', + 511923: '平昌县', + 511924: '其它区', + 512000: '资阳市', + 512002: '雁江区', + 512021: '安岳县', + 512022: '乐至县', + 512081: '简阳市', + 512082: '其它区', + 513200: '阿坝藏族羌族自治州', + 513221: '汶川县', + 513222: '理县', + 513223: '茂县', + 513224: '松潘县', + 513225: '九寨沟县', + 513226: '金川县', + 513227: '小金县', + 513228: '黑水县', + 513229: '马尔康县', + 513230: '壤塘县', + 513231: '阿坝县', + 513232: '若尔盖县', + 513233: '红原县', + 513234: '其它区', + 513300: '甘孜藏族自治州', + 513321: '康定县', + 513322: '泸定县', + 513323: '丹巴县', + 513324: '九龙县', + 513325: '雅江县', + 513326: '道孚县', + 513327: '炉霍县', + 513328: '甘孜县', + 513329: '新龙县', + 513330: '德格县', + 513331: '白玉县', + 513332: '石渠县', + 513333: '色达县', + 513334: '理塘县', + 513335: '巴塘县', + 513336: '乡城县', + 513337: '稻城县', + 513338: '得荣县', + 513339: '其它区', + 513400: '凉山彝族自治州', + 513401: '西昌市', + 513422: '木里藏族自治县', + 513423: '盐源县', + 513424: '德昌县', + 513425: '会理县', + 513426: '会东县', + 513427: '宁南县', + 513428: '普格县', + 513429: '布拖县', + 513430: '金阳县', + 513431: '昭觉县', + 513432: '喜德县', + 513433: '冕宁县', + 513434: '越西县', + 513435: '甘洛县', + 513436: '美姑县', + 513437: '雷波县', + 513438: '其它区', + 520000: '贵州省', + 520100: '贵阳市', + 520102: '南明区', + 520103: '云岩区', + 520111: '花溪区', + 520112: '乌当区', + 520113: '白云区', + 520121: '开阳县', + 520122: '息烽县', + 520123: '修文县', + 520151: '观山湖区', + 520181: '清镇市', + 520182: '其它区', + 520200: '六盘水市', + 520201: '钟山区', + 520203: '六枝特区', + 520221: '水城县', + 520222: '盘县', + 520223: '其它区', + 520300: '遵义市', + 520302: '红花岗区', + 520303: '汇川区', + 520321: '遵义县', + 520322: '桐梓县', + 520323: '绥阳县', + 520324: '正安县', + 520325: '道真仡佬族苗族自治县', + 520326: '务川仡佬族苗族自治县', + 520327: '凤冈县', + 520328: '湄潭县', + 520329: '余庆县', + 520330: '习水县', + 520381: '赤水市', + 520382: '仁怀市', + 520383: '其它区', + 520400: '安顺市', + 520402: '西秀区', + 520421: '平坝县', + 520422: '普定县', + 520423: '镇宁布依族苗族自治县', + 520424: '关岭布依族苗族自治县', + 520425: '紫云苗族布依族自治县', + 520426: '其它区', + 522200: '铜仁市', + 522201: '碧江区', + 522222: '江口县', + 522223: '玉屏侗族自治县', + 522224: '石阡县', + 522225: '思南县', + 522226: '印江土家族苗族自治县', + 522227: '德江县', + 522228: '沿河土家族自治县', + 522229: '松桃苗族自治县', + 522230: '万山区', + 522231: '其它区', + 522300: '黔西南布依族苗族自治州', + 522301: '兴义市', + 522322: '兴仁县', + 522323: '普安县', + 522324: '晴隆县', + 522325: '贞丰县', + 522326: '望谟县', + 522327: '册亨县', + 522328: '安龙县', + 522329: '其它区', + 522400: '毕节市', + 522401: '七星关区', + 522422: '大方县', + 522423: '黔西县', + 522424: '金沙县', + 522425: '织金县', + 522426: '纳雍县', + 522427: '威宁彝族回族苗族自治县', + 522428: '赫章县', + 522429: '其它区', + 522600: '黔东南苗族侗族自治州', + 522601: '凯里市', + 522622: '黄平县', + 522623: '施秉县', + 522624: '三穗县', + 522625: '镇远县', + 522626: '岑巩县', + 522627: '天柱县', + 522628: '锦屏县', + 522629: '剑河县', + 522630: '台江县', + 522631: '黎平县', + 522632: '榕江县', + 522633: '从江县', + 522634: '雷山县', + 522635: '麻江县', + 522636: '丹寨县', + 522637: '其它区', + 522700: '黔南布依族苗族自治州', + 522701: '都匀市', + 522702: '福泉市', + 522722: '荔波县', + 522723: '贵定县', + 522725: '瓮安县', + 522726: '独山县', + 522727: '平塘县', + 522728: '罗甸县', + 522729: '长顺县', + 522730: '龙里县', + 522731: '惠水县', + 522732: '三都水族自治县', + 522733: '其它区', + 530000: '云南省', + 530100: '昆明市', + 530102: '五华区', + 530103: '盘龙区', + 530111: '官渡区', + 530112: '西山区', + 530113: '东川区', + 530121: '呈贡区', + 530122: '晋宁县', + 530124: '富民县', + 530125: '宜良县', + 530126: '石林彝族自治县', + 530127: '嵩明县', + 530128: '禄劝彝族苗族自治县', + 530129: '寻甸回族彝族自治县', + 530181: '安宁市', + 530182: '其它区', + 530300: '曲靖市', + 530302: '麒麟区', + 530321: '马龙县', + 530322: '陆良县', + 530323: '师宗县', + 530324: '罗平县', + 530325: '富源县', + 530326: '会泽县', + 530328: '沾益县', + 530381: '宣威市', + 530382: '其它区', + 530400: '玉溪市', + 530402: '红塔区', + 530421: '江川县', + 530422: '澄江县', + 530423: '通海县', + 530424: '华宁县', + 530425: '易门县', + 530426: '峨山彝族自治县', + 530427: '新平彝族傣族自治县', + 530428: '元江哈尼族彝族傣族自治县', + 530429: '其它区', + 530500: '保山市', + 530502: '隆阳区', + 530521: '施甸县', + 530522: '腾冲县', + 530523: '龙陵县', + 530524: '昌宁县', + 530525: '其它区', + 530600: '昭通市', + 530602: '昭阳区', + 530621: '鲁甸县', + 530622: '巧家县', + 530623: '盐津县', + 530624: '大关县', + 530625: '永善县', + 530626: '绥江县', + 530627: '镇雄县', + 530628: '彝良县', + 530629: '威信县', + 530630: '水富县', + 530631: '其它区', + 530700: '丽江市', + 530702: '古城区', + 530721: '玉龙纳西族自治县', + 530722: '永胜县', + 530723: '华坪县', + 530724: '宁蒗彝族自治县', + 530725: '其它区', + 530800: '普洱市', + 530802: '思茅区', + 530821: '宁洱哈尼族彝族自治县', + 530822: '墨江哈尼族自治县', + 530823: '景东彝族自治县', + 530824: '景谷傣族彝族自治县', + 530825: '镇沅彝族哈尼族拉祜族自治县', + 530826: '江城哈尼族彝族自治县', + 530827: '孟连傣族拉祜族佤族自治县', + 530828: '澜沧拉祜族自治县', + 530829: '西盟佤族自治县', + 530830: '其它区', + 530900: '临沧市', + 530902: '临翔区', + 530921: '凤庆县', + 530922: '云县', + 530923: '永德县', + 530924: '镇康县', + 530925: '双江拉祜族佤族布朗族傣族自治县', + 530926: '耿马傣族佤族自治县', + 530927: '沧源佤族自治县', + 530928: '其它区', + 532300: '楚雄彝族自治州', + 532301: '楚雄市', + 532322: '双柏县', + 532323: '牟定县', + 532324: '南华县', + 532325: '姚安县', + 532326: '大姚县', + 532327: '永仁县', + 532328: '元谋县', + 532329: '武定县', + 532331: '禄丰县', + 532332: '其它区', + 532500: '红河哈尼族彝族自治州', + 532501: '个旧市', + 532502: '开远市', + 532522: '蒙自市', + 532523: '屏边苗族自治县', + 532524: '建水县', + 532525: '石屏县', + 532526: '弥勒市', + 532527: '泸西县', + 532528: '元阳县', + 532529: '红河县', + 532530: '金平苗族瑶族傣族自治县', + 532531: '绿春县', + 532532: '河口瑶族自治县', + 532533: '其它区', + 532600: '文山壮族苗族自治州', + 532621: '文山市', + 532622: '砚山县', + 532623: '西畴县', + 532624: '麻栗坡县', + 532625: '马关县', + 532626: '丘北县', + 532627: '广南县', + 532628: '富宁县', + 532629: '其它区', + 532800: '西双版纳傣族自治州', + 532801: '景洪市', + 532822: '勐海县', + 532823: '勐腊县', + 532824: '其它区', + 532900: '大理白族自治州', + 532901: '大理市', + 532922: '漾濞彝族自治县', + 532923: '祥云县', + 532924: '宾川县', + 532925: '弥渡县', + 532926: '南涧彝族自治县', + 532927: '巍山彝族回族自治县', + 532928: '永平县', + 532929: '云龙县', + 532930: '洱源县', + 532931: '剑川县', + 532932: '鹤庆县', + 532933: '其它区', + 533100: '德宏傣族景颇族自治州', + 533102: '瑞丽市', + 533103: '芒市', + 533122: '梁河县', + 533123: '盈江县', + 533124: '陇川县', + 533125: '其它区', + 533300: '怒江傈僳族自治州', + 533321: '泸水县', + 533323: '福贡县', + 533324: '贡山独龙族怒族自治县', + 533325: '兰坪白族普米族自治县', + 533326: '其它区', + 533400: '迪庆藏族自治州', + 533421: '香格里拉县', + 533422: '德钦县', + 533423: '维西傈僳族自治县', + 533424: '其它区', + 540000: '西藏自治区', + 540100: '拉萨市', + 540102: '城关区', + 540121: '林周县', + 540122: '当雄县', + 540123: '尼木县', + 540124: '曲水县', + 540125: '堆龙德庆县', + 540126: '达孜县', + 540127: '墨竹工卡县', + 540128: '其它区', + 542100: '昌都地区', + 542121: '昌都县', + 542122: '江达县', + 542123: '贡觉县', + 542124: '类乌齐县', + 542125: '丁青县', + 542126: '察雅县', + 542127: '八宿县', + 542128: '左贡县', + 542129: '芒康县', + 542132: '洛隆县', + 542133: '边坝县', + 542134: '其它区', + 542200: '山南地区', + 542221: '乃东县', + 542222: '扎囊县', + 542223: '贡嘎县', + 542224: '桑日县', + 542225: '琼结县', + 542226: '曲松县', + 542227: '措美县', + 542228: '洛扎县', + 542229: '加查县', + 542231: '隆子县', + 542232: '错那县', + 542233: '浪卡子县', + 542234: '其它区', + 542300: '日喀则地区', + 542301: '日喀则市', + 542322: '南木林县', + 542323: '江孜县', + 542324: '定日县', + 542325: '萨迦县', + 542326: '拉孜县', + 542327: '昂仁县', + 542328: '谢通门县', + 542329: '白朗县', + 542330: '仁布县', + 542331: '康马县', + 542332: '定结县', + 542333: '仲巴县', + 542334: '亚东县', + 542335: '吉隆县', + 542336: '聂拉木县', + 542337: '萨嘎县', + 542338: '岗巴县', + 542339: '其它区', + 542400: '那曲地区', + 542421: '那曲县', + 542422: '嘉黎县', + 542423: '比如县', + 542424: '聂荣县', + 542425: '安多县', + 542426: '申扎县', + 542427: '索县', + 542428: '班戈县', + 542429: '巴青县', + 542430: '尼玛县', + 542431: '其它区', + 542432: '双湖县', + 542500: '阿里地区', + 542521: '普兰县', + 542522: '札达县', + 542523: '噶尔县', + 542524: '日土县', + 542525: '革吉县', + 542526: '改则县', + 542527: '措勤县', + 542528: '其它区', + 542600: '林芝地区', + 542621: '林芝县', + 542622: '工布江达县', + 542623: '米林县', + 542624: '墨脱县', + 542625: '波密县', + 542626: '察隅县', + 542627: '朗县', + 542628: '其它区', + 610000: '陕西省', + 610100: '西安市', + 610102: '新城区', + 610103: '碑林区', + 610104: '莲湖区', + 610111: '灞桥区', + 610112: '未央区', + 610113: '雁塔区', + 610114: '阎良区', + 610115: '临潼区', + 610116: '长安区', + 610122: '蓝田县', + 610124: '周至县', + 610125: '户县', + 610126: '高陵县', + 610127: '其它区', + 610200: '铜川市', + 610202: '王益区', + 610203: '印台区', + 610204: '耀州区', + 610222: '宜君县', + 610223: '其它区', + 610300: '宝鸡市', + 610302: '渭滨区', + 610303: '金台区', + 610304: '陈仓区', + 610322: '凤翔县', + 610323: '岐山县', + 610324: '扶风县', + 610326: '眉县', + 610327: '陇县', + 610328: '千阳县', + 610329: '麟游县', + 610330: '凤县', + 610331: '太白县', + 610332: '其它区', + 610400: '咸阳市', + 610402: '秦都区', + 610403: '杨陵区', + 610404: '渭城区', + 610422: '三原县', + 610423: '泾阳县', + 610424: '乾县', + 610425: '礼泉县', + 610426: '永寿县', + 610427: '彬县', + 610428: '长武县', + 610429: '旬邑县', + 610430: '淳化县', + 610431: '武功县', + 610481: '兴平市', + 610482: '其它区', + 610500: '渭南市', + 610502: '临渭区', + 610521: '华县', + 610522: '潼关县', + 610523: '大荔县', + 610524: '合阳县', + 610525: '澄城县', + 610526: '蒲城县', + 610527: '白水县', + 610528: '富平县', + 610581: '韩城市', + 610582: '华阴市', + 610583: '其它区', + 610600: '延安市', + 610602: '宝塔区', + 610621: '延长县', + 610622: '延川县', + 610623: '子长县', + 610624: '安塞县', + 610625: '志丹县', + 610626: '吴起县', + 610627: '甘泉县', + 610628: '富县', + 610629: '洛川县', + 610630: '宜川县', + 610631: '黄龙县', + 610632: '黄陵县', + 610633: '其它区', + 610700: '汉中市', + 610702: '汉台区', + 610721: '南郑县', + 610722: '城固县', + 610723: '洋县', + 610724: '西乡县', + 610725: '勉县', + 610726: '宁强县', + 610727: '略阳县', + 610728: '镇巴县', + 610729: '留坝县', + 610730: '佛坪县', + 610731: '其它区', + 610800: '榆林市', + 610802: '榆阳区', + 610821: '神木县', + 610822: '府谷县', + 610823: '横山县', + 610824: '靖边县', + 610825: '定边县', + 610826: '绥德县', + 610827: '米脂县', + 610828: '佳县', + 610829: '吴堡县', + 610830: '清涧县', + 610831: '子洲县', + 610832: '其它区', + 610900: '安康市', + 610902: '汉滨区', + 610921: '汉阴县', + 610922: '石泉县', + 610923: '宁陕县', + 610924: '紫阳县', + 610925: '岚皋县', + 610926: '平利县', + 610927: '镇坪县', + 610928: '旬阳县', + 610929: '白河县', + 610930: '其它区', + 611000: '商洛市', + 611002: '商州区', + 611021: '洛南县', + 611022: '丹凤县', + 611023: '商南县', + 611024: '山阳县', + 611025: '镇安县', + 611026: '柞水县', + 611027: '其它区', + 620000: '甘肃省', + 620100: '兰州市', + 620102: '城关区', + 620103: '七里河区', + 620104: '西固区', + 620105: '安宁区', + 620111: '红古区', + 620121: '永登县', + 620122: '皋兰县', + 620123: '榆中县', + 620124: '其它区', + 620200: '嘉峪关市', + 620300: '金昌市', + 620302: '金川区', + 620321: '永昌县', + 620322: '其它区', + 620400: '白银市', + 620402: '白银区', + 620403: '平川区', + 620421: '靖远县', + 620422: '会宁县', + 620423: '景泰县', + 620424: '其它区', + 620500: '天水市', + 620502: '秦州区', + 620503: '麦积区', + 620521: '清水县', + 620522: '秦安县', + 620523: '甘谷县', + 620524: '武山县', + 620525: '张家川回族自治县', + 620526: '其它区', + 620600: '武威市', + 620602: '凉州区', + 620621: '民勤县', + 620622: '古浪县', + 620623: '天祝藏族自治县', + 620624: '其它区', + 620700: '张掖市', + 620702: '甘州区', + 620721: '肃南裕固族自治县', + 620722: '民乐县', + 620723: '临泽县', + 620724: '高台县', + 620725: '山丹县', + 620726: '其它区', + 620800: '平凉市', + 620802: '崆峒区', + 620821: '泾川县', + 620822: '灵台县', + 620823: '崇信县', + 620824: '华亭县', + 620825: '庄浪县', + 620826: '静宁县', + 620827: '其它区', + 620900: '酒泉市', + 620902: '肃州区', + 620921: '金塔县', + 620922: '瓜州县', + 620923: '肃北蒙古族自治县', + 620924: '阿克塞哈萨克族自治县', + 620981: '玉门市', + 620982: '敦煌市', + 620983: '其它区', + 621000: '庆阳市', + 621002: '西峰区', + 621021: '庆城县', + 621022: '环县', + 621023: '华池县', + 621024: '合水县', + 621025: '正宁县', + 621026: '宁县', + 621027: '镇原县', + 621028: '其它区', + 621100: '定西市', + 621102: '安定区', + 621121: '通渭县', + 621122: '陇西县', + 621123: '渭源县', + 621124: '临洮县', + 621125: '漳县', + 621126: '岷县', + 621127: '其它区', + 621200: '陇南市', + 621202: '武都区', + 621221: '成县', + 621222: '文县', + 621223: '宕昌县', + 621224: '康县', + 621225: '西和县', + 621226: '礼县', + 621227: '徽县', + 621228: '两当县', + 621229: '其它区', + 622900: '临夏回族自治州', + 622901: '临夏市', + 622921: '临夏县', + 622922: '康乐县', + 622923: '永靖县', + 622924: '广河县', + 622925: '和政县', + 622926: '东乡族自治县', + 622927: '积石山保安族东乡族撒拉族自治县', + 622928: '其它区', + 623000: '甘南藏族自治州', + 623001: '合作市', + 623021: '临潭县', + 623022: '卓尼县', + 623023: '舟曲县', + 623024: '迭部县', + 623025: '玛曲县', + 623026: '碌曲县', + 623027: '夏河县', + 623028: '其它区', + 630000: '青海省', + 630100: '西宁市', + 630102: '城东区', + 630103: '城中区', + 630104: '城西区', + 630105: '城北区', + 630121: '大通回族土族自治县', + 630122: '湟中县', + 630123: '湟源县', + 630124: '其它区', + 632100: '海东市', + 632121: '平安县', + 632122: '民和回族土族自治县', + 632123: '乐都区', + 632126: '互助土族自治县', + 632127: '化隆回族自治县', + 632128: '循化撒拉族自治县', + 632129: '其它区', + 632200: '海北藏族自治州', + 632221: '门源回族自治县', + 632222: '祁连县', + 632223: '海晏县', + 632224: '刚察县', + 632225: '其它区', + 632300: '黄南藏族自治州', + 632321: '同仁县', + 632322: '尖扎县', + 632323: '泽库县', + 632324: '河南蒙古族自治县', + 632325: '其它区', + 632500: '海南藏族自治州', + 632521: '共和县', + 632522: '同德县', + 632523: '贵德县', + 632524: '兴海县', + 632525: '贵南县', + 632526: '其它区', + 632600: '果洛藏族自治州', + 632621: '玛沁县', + 632622: '班玛县', + 632623: '甘德县', + 632624: '达日县', + 632625: '久治县', + 632626: '玛多县', + 632627: '其它区', + 632700: '玉树藏族自治州', + 632721: '玉树市', + 632722: '杂多县', + 632723: '称多县', + 632724: '治多县', + 632725: '囊谦县', + 632726: '曲麻莱县', + 632727: '其它区', + 632800: '海西蒙古族藏族自治州', + 632801: '格尔木市', + 632802: '德令哈市', + 632821: '乌兰县', + 632822: '都兰县', + 632823: '天峻县', + 632824: '其它区', + 640000: '宁夏回族自治区', + 640100: '银川市', + 640104: '兴庆区', + 640105: '西夏区', + 640106: '金凤区', + 640121: '永宁县', + 640122: '贺兰县', + 640181: '灵武市', + 640182: '其它区', + 640200: '石嘴山市', + 640202: '大武口区', + 640205: '惠农区', + 640221: '平罗县', + 640222: '其它区', + 640300: '吴忠市', + 640302: '利通区', + 640303: '红寺堡区', + 640323: '盐池县', + 640324: '同心县', + 640381: '青铜峡市', + 640382: '其它区', + 640400: '固原市', + 640402: '原州区', + 640422: '西吉县', + 640423: '隆德县', + 640424: '泾源县', + 640425: '彭阳县', + 640426: '其它区', + 640500: '中卫市', + 640502: '沙坡头区', + 640521: '中宁县', + 640522: '海原县', + 640523: '其它区', + 650000: '新疆维吾尔自治区', + 650100: '乌鲁木齐市', + 650102: '天山区', + 650103: '沙依巴克区', + 650104: '新市区', + 650105: '水磨沟区', + 650106: '头屯河区', + 650107: '达坂城区', + 650109: '米东区', + 650121: '乌鲁木齐县', + 650122: '其它区', + 650200: '克拉玛依市', + 650202: '独山子区', + 650203: '克拉玛依区', + 650204: '白碱滩区', + 650205: '乌尔禾区', + 650206: '其它区', + 652100: '吐鲁番地区', + 652101: '吐鲁番市', + 652122: '鄯善县', + 652123: '托克逊县', + 652124: '其它区', + 652200: '哈密地区', + 652201: '哈密市', + 652222: '巴里坤哈萨克自治县', + 652223: '伊吾县', + 652224: '其它区', + 652300: '昌吉回族自治州', + 652301: '昌吉市', + 652302: '阜康市', + 652323: '呼图壁县', + 652324: '玛纳斯县', + 652325: '奇台县', + 652327: '吉木萨尔县', + 652328: '木垒哈萨克自治县', + 652329: '其它区', + 652700: '博尔塔拉蒙古自治州', + 652701: '博乐市', + 652702: '阿拉山口市', + 652722: '精河县', + 652723: '温泉县', + 652724: '其它区', + 652800: '巴音郭楞蒙古自治州', + 652801: '库尔勒市', + 652822: '轮台县', + 652823: '尉犁县', + 652824: '若羌县', + 652825: '且末县', + 652826: '焉耆回族自治县', + 652827: '和静县', + 652828: '和硕县', + 652829: '博湖县', + 652830: '其它区', + 652900: '阿克苏地区', + 652901: '阿克苏市', + 652922: '温宿县', + 652923: '库车县', + 652924: '沙雅县', + 652925: '新和县', + 652926: '拜城县', + 652927: '乌什县', + 652928: '阿瓦提县', + 652929: '柯坪县', + 652930: '其它区', + 653000: '克孜勒苏柯尔克孜自治州', + 653001: '阿图什市', + 653022: '阿克陶县', + 653023: '阿合奇县', + 653024: '乌恰县', + 653025: '其它区', + 653100: '喀什地区', + 653101: '喀什市', + 653121: '疏附县', + 653122: '疏勒县', + 653123: '英吉沙县', + 653124: '泽普县', + 653125: '莎车县', + 653126: '叶城县', + 653127: '麦盖提县', + 653128: '岳普湖县', + 653129: '伽师县', + 653130: '巴楚县', + 653131: '塔什库尔干塔吉克自治县', + 653132: '其它区', + 653200: '和田地区', + 653201: '和田市', + 653221: '和田县', + 653222: '墨玉县', + 653223: '皮山县', + 653224: '洛浦县', + 653225: '策勒县', + 653226: '于田县', + 653227: '民丰县', + 653228: '其它区', + 654000: '伊犁哈萨克自治州', + 654002: '伊宁市', + 654003: '奎屯市', + 654021: '伊宁县', + 654022: '察布查尔锡伯自治县', + 654023: '霍城县', + 654024: '巩留县', + 654025: '新源县', + 654026: '昭苏县', + 654027: '特克斯县', + 654028: '尼勒克县', + 654029: '其它区', + 654200: '塔城地区', + 654201: '塔城市', + 654202: '乌苏市', + 654221: '额敏县', + 654223: '沙湾县', + 654224: '托里县', + 654225: '裕民县', + 654226: '和布克赛尔蒙古自治县', + 654227: '其它区', + 654300: '阿勒泰地区', + 654301: '阿勒泰市', + 654321: '布尔津县', + 654322: '富蕴县', + 654323: '福海县', + 654324: '哈巴河县', + 654325: '青河县', + 654326: '吉木乃县', + 654327: '其它区', + 659001: '石河子市', + 659002: '阿拉尔市', + 659003: '图木舒克市', + 659004: '五家渠市', + 710000: '台湾', + 710100: '台北市', + 710101: '中正区', + 710102: '大同区', + 710103: '中山区', + 710104: '松山区', + 710105: '大安区', + 710106: '万华区', + 710107: '信义区', + 710108: '士林区', + 710109: '北投区', + 710110: '内湖区', + 710111: '南港区', + 710112: '文山区', + 710113: '其它区', + 710200: '高雄市', + 710201: '新兴区', + 710202: '前金区', + 710203: '芩雅区', + 710204: '盐埕区', + 710205: '鼓山区', + 710206: '旗津区', + 710207: '前镇区', + 710208: '三民区', + 710209: '左营区', + 710210: '楠梓区', + 710211: '小港区', + 710212: '其它区', + 710241: '苓雅区', + 710242: '仁武区', + 710243: '大社区', + 710244: '冈山区', + 710245: '路竹区', + 710246: '阿莲区', + 710247: '田寮区', + 710248: '燕巢区', + 710249: '桥头区', + 710250: '梓官区', + 710251: '弥陀区', + 710252: '永安区', + 710253: '湖内区', + 710254: '凤山区', + 710255: '大寮区', + 710256: '林园区', + 710257: '鸟松区', + 710258: '大树区', + 710259: '旗山区', + 710260: '美浓区', + 710261: '六龟区', + 710262: '内门区', + 710263: '杉林区', + 710264: '甲仙区', + 710265: '桃源区', + 710266: '那玛夏区', + 710267: '茂林区', + 710268: '茄萣区', + 710300: '台南市', + 710301: '中西区', + 710302: '东区', + 710303: '南区', + 710304: '北区', + 710305: '安平区', + 710306: '安南区', + 710307: '其它区', + 710339: '永康区', + 710340: '归仁区', + 710341: '新化区', + 710342: '左镇区', + 710343: '玉井区', + 710344: '楠西区', + 710345: '南化区', + 710346: '仁德区', + 710347: '关庙区', + 710348: '龙崎区', + 710349: '官田区', + 710350: '麻豆区', + 710351: '佳里区', + 710352: '西港区', + 710353: '七股区', + 710354: '将军区', + 710355: '学甲区', + 710356: '北门区', + 710357: '新营区', + 710358: '后壁区', + 710359: '白河区', + 710360: '东山区', + 710361: '六甲区', + 710362: '下营区', + 710363: '柳营区', + 710364: '盐水区', + 710365: '善化区', + 710366: '大内区', + 710367: '山上区', + 710368: '新市区', + 710369: '安定区', + 710400: '台中市', + 710401: '中区', + 710402: '东区', + 710403: '南区', + 710404: '西区', + 710405: '北区', + 710406: '北屯区', + 710407: '西屯区', + 710408: '南屯区', + 710409: '其它区', + 710431: '太平区', + 710432: '大里区', + 710433: '雾峰区', + 710434: '乌日区', + 710435: '丰原区', + 710436: '后里区', + 710437: '石冈区', + 710438: '东势区', + 710439: '和平区', + 710440: '新社区', + 710441: '潭子区', + 710442: '大雅区', + 710443: '神冈区', + 710444: '大肚区', + 710445: '沙鹿区', + 710446: '龙井区', + 710447: '梧栖区', + 710448: '清水区', + 710449: '大甲区', + 710450: '外埔区', + 710451: '大安区', + 710500: '金门县', + 710507: '金沙镇', + 710508: '金湖镇', + 710509: '金宁乡', + 710510: '金城镇', + 710511: '烈屿乡', + 710512: '乌坵乡', + 710600: '南投县', + 710614: '南投市', + 710615: '中寮乡', + 710616: '草屯镇', + 710617: '国姓乡', + 710618: '埔里镇', + 710619: '仁爱乡', + 710620: '名间乡', + 710621: '集集镇', + 710622: '水里乡', + 710623: '鱼池乡', + 710624: '信义乡', + 710625: '竹山镇', + 710626: '鹿谷乡', + 710700: '基隆市', + 710701: '仁爱区', + 710702: '信义区', + 710703: '中正区', + 710704: '中山区', + 710705: '安乐区', + 710706: '暖暖区', + 710707: '七堵区', + 710708: '其它区', + 710800: '新竹市', + 710801: '东区', + 710802: '北区', + 710803: '香山区', + 710804: '其它区', + 710900: '嘉义市', + 710901: '东区', + 710902: '西区', + 710903: '其它区', + 711100: '新北市', + 711130: '万里区', + 711131: '金山区', + 711132: '板桥区', + 711133: '汐止区', + 711134: '深坑区', + 711135: '石碇区', + 711136: '瑞芳区', + 711137: '平溪区', + 711138: '双溪区', + 711139: '贡寮区', + 711140: '新店区', + 711141: '坪林区', + 711142: '乌来区', + 711143: '永和区', + 711144: '中和区', + 711145: '土城区', + 711146: '三峡区', + 711147: '树林区', + 711148: '莺歌区', + 711149: '三重区', + 711150: '新庄区', + 711151: '泰山区', + 711152: '林口区', + 711153: '芦洲区', + 711154: '五股区', + 711155: '八里区', + 711156: '淡水区', + 711157: '三芝区', + 711158: '石门区', + 711200: '宜兰县', + 711214: '宜兰市', + 711215: '头城镇', + 711216: '礁溪乡', + 711217: '壮围乡', + 711218: '员山乡', + 711219: '罗东镇', + 711220: '三星乡', + 711221: '大同乡', + 711222: '五结乡', + 711223: '冬山乡', + 711224: '苏澳镇', + 711225: '南澳乡', + 711226: '钓鱼台', + 711300: '新竹县', + 711314: '竹北市', + 711315: '湖口乡', + 711316: '新丰乡', + 711317: '新埔镇', + 711318: '关西镇', + 711319: '芎林乡', + 711320: '宝山乡', + 711321: '竹东镇', + 711322: '五峰乡', + 711323: '横山乡', + 711324: '尖石乡', + 711325: '北埔乡', + 711326: '峨眉乡', + 711400: '桃园县', + 711414: '中坜市', + 711415: '平镇市', + 711416: '龙潭乡', + 711417: '杨梅市', + 711418: '新屋乡', + 711419: '观音乡', + 711420: '桃园市', + 711421: '龟山乡', + 711422: '八德市', + 711423: '大溪镇', + 711424: '复兴乡', + 711425: '大园乡', + 711426: '芦竹乡', + 711500: '苗栗县', + 711519: '竹南镇', + 711520: '头份镇', + 711521: '三湾乡', + 711522: '南庄乡', + 711523: '狮潭乡', + 711524: '后龙镇', + 711525: '通霄镇', + 711526: '苑里镇', + 711527: '苗栗市', + 711528: '造桥乡', + 711529: '头屋乡', + 711530: '公馆乡', + 711531: '大湖乡', + 711532: '泰安乡', + 711533: '铜锣乡', + 711534: '三义乡', + 711535: '西湖乡', + 711536: '卓兰镇', + 711700: '彰化县', + 711727: '彰化市', + 711728: '芬园乡', + 711729: '花坛乡', + 711730: '秀水乡', + 711731: '鹿港镇', + 711732: '福兴乡', + 711733: '线西乡', + 711734: '和美镇', + 711735: '伸港乡', + 711736: '员林镇', + 711737: '社头乡', + 711738: '永靖乡', + 711739: '埔心乡', + 711740: '溪湖镇', + 711741: '大村乡', + 711742: '埔盐乡', + 711743: '田中镇', + 711744: '北斗镇', + 711745: '田尾乡', + 711746: '埤头乡', + 711747: '溪州乡', + 711748: '竹塘乡', + 711749: '二林镇', + 711750: '大城乡', + 711751: '芳苑乡', + 711752: '二水乡', + 711900: '嘉义县', + 711919: '番路乡', + 711920: '梅山乡', + 711921: '竹崎乡', + 711922: '阿里山乡', + 711923: '中埔乡', + 711924: '大埔乡', + 711925: '水上乡', + 711926: '鹿草乡', + 711927: '太保市', + 711928: '朴子市', + 711929: '东石乡', + 711930: '六脚乡', + 711931: '新港乡', + 711932: '民雄乡', + 711933: '大林镇', + 711934: '溪口乡', + 711935: '义竹乡', + 711936: '布袋镇', + 712100: '云林县', + 712121: '斗南镇', + 712122: '大埤乡', + 712123: '虎尾镇', + 712124: '土库镇', + 712125: '褒忠乡', + 712126: '东势乡', + 712127: '台西乡', + 712128: '仑背乡', + 712129: '麦寮乡', + 712130: '斗六市', + 712131: '林内乡', + 712132: '古坑乡', + 712133: '莿桐乡', + 712134: '西螺镇', + 712135: '二仑乡', + 712136: '北港镇', + 712137: '水林乡', + 712138: '口湖乡', + 712139: '四湖乡', + 712140: '元长乡', + 712400: '屏东县', + 712434: '屏东市', + 712435: '三地门乡', + 712436: '雾台乡', + 712437: '玛家乡', + 712438: '九如乡', + 712439: '里港乡', + 712440: '高树乡', + 712441: '盐埔乡', + 712442: '长治乡', + 712443: '麟洛乡', + 712444: '竹田乡', + 712445: '内埔乡', + 712446: '万丹乡', + 712447: '潮州镇', + 712448: '泰武乡', + 712449: '来义乡', + 712450: '万峦乡', + 712451: '崁顶乡', + 712452: '新埤乡', + 712453: '南州乡', + 712454: '林边乡', + 712455: '东港镇', + 712456: '琉球乡', + 712457: '佳冬乡', + 712458: '新园乡', + 712459: '枋寮乡', + 712460: '枋山乡', + 712461: '春日乡', + 712462: '狮子乡', + 712463: '车城乡', + 712464: '牡丹乡', + 712465: '恒春镇', + 712466: '满州乡', + 712500: '台东县', + 712517: '台东市', + 712518: '绿岛乡', + 712519: '兰屿乡', + 712520: '延平乡', + 712521: '卑南乡', + 712522: '鹿野乡', + 712523: '关山镇', + 712524: '海端乡', + 712525: '池上乡', + 712526: '东河乡', + 712527: '成功镇', + 712528: '长滨乡', + 712529: '金峰乡', + 712530: '大武乡', + 712531: '达仁乡', + 712532: '太麻里乡', + 712600: '花莲县', + 712615: '花莲市', + 712616: '新城乡', + 712617: '太鲁阁', + 712618: '秀林乡', + 712619: '吉安乡', + 712620: '寿丰乡', + 712621: '凤林镇', + 712622: '光复乡', + 712623: '丰滨乡', + 712624: '瑞穗乡', + 712625: '万荣乡', + 712626: '玉里镇', + 712627: '卓溪乡', + 712628: '富里乡', + 712700: '澎湖县', + 712707: '马公市', + 712708: '西屿乡', + 712709: '望安乡', + 712710: '七美乡', + 712711: '白沙乡', + 712712: '湖西乡', + 712800: '连江县', + 712805: '南竿乡', + 712806: '北竿乡', + 712807: '莒光乡', + 712808: '东引乡', + 810000: '香港特别行政区', + 810100: '香港岛', + 810101: '中西区', + 810102: '湾仔', + 810103: '东区', + 810104: '南区', + 810200: '九龙', + 810201: '九龙城区', + 810202: '油尖旺区', + 810203: '深水埗区', + 810204: '黄大仙区', + 810205: '观塘区', + 810300: '新界', + 810301: '北区', + 810302: '大埔区', + 810303: '沙田区', + 810304: '西贡区', + 810305: '元朗区', + 810306: '屯门区', + 810307: '荃湾区', + 810308: '葵青区', + 810309: '离岛区', + 820000: '澳门特别行政区', + 820100: '澳门半岛', + 820200: '离岛', + 990000: '海外', + 990100: '海外', +}; + +const tree = list => { + let hashTable = Object.create(null); + list.forEach(aData => (hashTable[aData.id] = { ...aData, children: [] })); + let dataTree = []; + list.forEach(aData => { + if (aData.pid) { + if (hashTable[aData.pid]) hashTable[aData.pid].children.push(hashTable[aData.id]); + } else dataTree.push(hashTable[aData.id]); + }); + return dataTree; +}; + +let DICT_FIXED = (function () { + let fixed = []; + for (let id in DICT) { + if ({}.hasOwnProperty.call(DICT, id)) { + let pid; + const tmpObj = { id, value: DICT[id], label: DICT[id] }; + if (id.slice(2, 6) !== '0000') { + pid = id.slice(4, 6) === '00' ? `${id.slice(0, 2)}0000` : `${id.slice(0, 4)}00`; + } + if (pid) tmpObj.pid = pid; + fixed.push(tmpObj); + } + } + return tree(fixed); +})(); + +export default DICT_FIXED; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/config.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/config.js new file mode 100644 index 00000000..24f8d999 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/config.js @@ -0,0 +1,41 @@ +const config = { + siteName: 'inula Antd', + copyright: 'Ant Design Admin ©2020 zuiidea', + logoPath: '/logo.svg', + apiPrefix: '/api/v1', + fixedHeader: true, // sticky primary layout header + + /* Layout configuration, specify which layout to use for route. */ + layouts: [ + { + name: 'primary', + include: [/.*/], + exclude: [/(\/(en|zh))*\/login/], + }, + ], + + /* I18n configuration, `languages` and `defaultLanguage` are required currently. */ + i18n: { + /* Countrys flags: https://www.flaticon.com/packs/countrys-flags */ + languages: [ + { + key: 'pt-br', + title: 'Português', + flag: '/portugal.svg', + }, + { + key: 'en', + title: 'English', + flag: '/america.svg', + }, + { + key: 'zh', + title: '中文', + flag: '/china.svg', + }, + ], + defaultLanguage: 'en', + }, +}; + +export default config; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/constant.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/constant.js new file mode 100644 index 00000000..80b0b7ed --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/constant.js @@ -0,0 +1,7 @@ +export const ROLE_TYPE = { + ADMIN: 'admin', + DEFAULT: 'admin', + DEVELOPER: 'developer', +}; + +export const CANCEL_REQUEST_MESSAGE = 'cancel request'; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/iconMap.jsx b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/iconMap.jsx new file mode 100644 index 00000000..991c5c20 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/iconMap.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { + PayCircleOutlined, + ShoppingCartOutlined, + MessageOutlined, + TeamOutlined, + UserOutlined, + DashboardOutlined, + ApiOutlined, + CameraOutlined, + EditOutlined, + CodeOutlined, + LineOutlined, + BarChartOutlined, + AreaChartOutlined, +} from '@ant-design/icons'; + +export default { + 'pay-circle-o': , + 'shopping-cart': , + 'camera-o': , + 'line-chart': , + 'code-o': , + 'area-chart': , + 'bar-chart': , + message: , + team: , + dashboard: , + user: , + api: , + edit: , +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.js new file mode 100644 index 00000000..a676d9c6 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.js @@ -0,0 +1,168 @@ +import { cloneDeep } from 'lodash'; +import { pathToRegexp } from 'path-to-regexp'; +import moment from 'moment'; +import 'moment/locale/zh-cn'; +import store from 'store'; +import config from './config'; + +// export classnames from 'classnames' +// export config from './config' +// export request from './request' +// export { Color } from './theme' + +export const languages = config.i18n ? config.i18n.languages.map(item => item.key) : []; +export const defaultLanguage = config.i18n ? config.i18n.defaultLanguage : ''; + +/** + * Query objects that specify keys and values in an array where all values are objects. + * @param {array} array An array where all values are objects, like [{key:1},{key:2}]. + * @param {string} key The key of the object that needs to be queried. + * @param {string} value The value of the object that needs to be queried. + * @return {object|undefined} Return frist object when query success. + */ +export function queryArray(array, key, value) { + if (!Array.isArray(array)) { + return; + } + return array.find(_ => _[key] === value); +} + +/** + * Convert an array to a tree-structured array. + * @param {array} array The Array need to Converted. + * @param {string} id The alias of the unique ID of the object in the array. + * @param {string} parentId The alias of the parent ID of the object in the array. + * @param {string} children The alias of children of the object in the array. + * @return {array} Return a tree-structured array. + */ +export function arrayToTree(array, id = 'id', parentId = 'pid', children = 'children') { + const result = []; + const hash = {}; + const data = cloneDeep(array); + + data.forEach((item, index) => { + hash[data[index][id]] = data[index]; + }); + + data.forEach(item => { + const hashParent = hash[item[parentId]]; + if (hashParent) { + !hashParent[children] && (hashParent[children] = []); + hashParent[children].push(item); + } else { + result.push(item); + } + }); + return result; +} + +/** + * In an array object, traverse all parent IDs based on the value of an object. + * @param {array} array The Array need to Converted. + * @param {string} current Specify the value of the object that needs to be queried. + * @param {string} parentId The alias of the parent ID of the object in the array. + * @param {string} id The alias of the unique ID of the object in the array. + * @return {array} Return a key array. + */ +export function queryPathKeys(array, current, parentId, id = 'id') { + const result = [current]; + const hashMap = new Map(); + array.forEach(item => hashMap.set(item[id], item)); + + const getPath = current => { + const currentParentId = hashMap.get(current)[parentId]; + if (currentParentId) { + result.push(currentParentId); + getPath(currentParentId); + } + }; + + getPath(current); + return result; +} + +/** + * In an array of objects, specify an object that traverses the objects whose parent ID matches. + * @param {array} array The Array need to Converted. + * @param {string} current Specify the object that needs to be queried. + * @param {string} parentId The alias of the parent ID of the object in the array. + * @param {string} id The alias of the unique ID of the object in the array. + * @return {array} Return a key array. + */ +export function queryAncestors(array, current, parentId, id = 'id') { + const result = [current]; + const hashMap = new Map(); + array.forEach(item => hashMap.set(item[id], item)); + + const getPath = current => { + const currentParentId = hashMap.get(current[id])[parentId]; + if (currentParentId) { + result.push(hashMap.get(currentParentId)); + getPath(hashMap.get(currentParentId)); + } + }; + + getPath(current); + return result; +} + +/** + * Query which layout should be used for the current path based on the configuration. + * @param {layouts} layouts Layout configuration. + * @param {pathname} pathname Path name to be queried. + * @return {string} Return frist object when query success. + */ +export function queryLayout(layouts, pathname) { + let result = 'public'; + + const isMatch = regepx => { + return regepx instanceof RegExp ? regepx.test(pathname) : pathToRegexp(regepx).exec(pathname); + }; + + for (const item of layouts) { + let include = false; + let exclude = false; + if (item.include) { + for (const regepx of item.include) { + if (isMatch(regepx)) { + include = true; + break; + } + } + } + + if (include && item.exclude) { + for (const regepx of item.exclude) { + if (isMatch(regepx)) { + exclude = true; + break; + } + } + } + + if (include && !exclude) { + result = item.name; + break; + } + } + + return result; +} + +export function getLocale() { + return store.get('locale') || defaultLanguage; +} + +export function setLocale(language) { + if (getLocale() !== language) { + moment.locale(language === 'zh' ? 'zh-cn' : language); + store.set('locale', language); + window.location.reload(); + } +} + +export function parseSearch(search) { + const searchObj = {}; + search.replace(/([^?&=]+)=([^&]+)/g, (_, k, v) => (searchObj[k] = decodeURI(v))); + return searchObj; +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.test.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.test.js new file mode 100644 index 00000000..b13551eb --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/index.test.js @@ -0,0 +1,11 @@ +const { pathToRegexp } = require('path-to-regexp'); + +describe('test pathToRegexp', () => { + it('get right', () => { + expect(pathToRegexp('/user').exec('/zh/user')).toEqual(pathToRegexp('/user').exec('/user')); + expect(pathToRegexp('/user').exec('/user')).toEqual(pathToRegexp('/user').exec('/user')); + + expect(pathToRegexp('/user/:id').exec('/zh/user/1')).toEqual(pathToRegexp('/user/:id').exec('/user/1')); + expect(pathToRegexp('/user/:id').exec('/user/1')).toEqual(pathToRegexp('/user/:id').exec('/user/1')); + }); +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/intl.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/intl.js new file mode 100644 index 00000000..72f3396c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/intl.js @@ -0,0 +1,55 @@ +/** + * 国际化,支持按需加载 + */ + +import React from 'react'; +import { createIntl, createIntlCache, FormattedMessage } from 'react-intl'; +import zhCN from 'antd/lib/locale-provider/zh_CN'; +import enUS from 'antd/lib/locale-provider/en_US'; +import ptBR from 'antd/lib/locale-provider/pt_BR'; + +import enMessage from '@/locales/en/messages'; +import zhMessage from '@/locales/zh/messages'; +import ptMessage from '@/locales/pt-br/messages'; + +const _langResource = { + zh: { ...zhCN, ...zhMessage }, + en: { ...enUS, ...enMessage }, + 'pt-br': { ...ptBR, ...ptMessage }, +}; + +let _intl = null; + +export const getLangResource = lan => { + if (!_intl) { + _intl = createIntl({ locale: lan, messages: _langResource[lan] }, createIntlCache()); + } + + return _langResource; +}; + +export const loadLangResource = async lan => { + if (!_intl) { + _intl = createIntl({ locale: lan, messages: _langResource[lan] }, createIntlCache()); + } + + const messages = await import(`@/locales/${lan}/messages`); + + Object.keys(messages.default).forEach(key => { + _langResource[lan][key] = messages.default[key]; + }); + + Object.assign(_intl.messages, _langResource[lan]); +}; + +export const t = (key, values) => { + if (!key) { + return; + } + + return _intl.formatMessage({ id: key }, values) || key; +}; + +export const Trans = ({ children }) => { + return ; +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/request.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/request.js new file mode 100644 index 00000000..48e1139d --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/request.js @@ -0,0 +1,96 @@ +import axios from 'axios'; +import { cloneDeep } from 'lodash'; + +import { parse, compile } from 'path-to-regexp'; +import { message } from 'antd'; +import { CANCEL_REQUEST_MESSAGE } from 'utils/constant'; + +const { CancelToken } = axios; +window.cancelRequest = new Map(); + +export default function request(options) { + let { data, url } = options; + const cloneData = cloneDeep(data); + + try { + let domain = ''; + const urlMatch = url.match(/[a-zA-z]+:\/\/[^/]*/); + if (urlMatch) { + [domain] = urlMatch; + url = url.slice(domain.length); + } + + const match = parse(url); + url = compile(url)(data); + + for (const item of match) { + if (item instanceof Object && item.name in cloneData) { + delete cloneData[item.name]; + } + } + url = domain + url; + } catch (e) { + message.error(e.message); + } + + options.url = url; + options.cancelToken = new CancelToken(cancel => { + window.cancelRequest.set(Symbol(Date.now()), { + pathname: window.location.pathname, + cancel, + }); + }); + + options.params = options.data; + + return axios(options) + .then(response => { + const { statusText, status, data } = response; + + let result = {}; + if (typeof data === 'object') { + if (Array.isArray(data)) { + result.list = data; + } else { + result = data; + } + } else { + result.data = data; + } + + return Promise.resolve({ + success: true, + message: statusText, + statusCode: status, + ...result, + }); + }) + .catch(error => { + const { response, message } = error; + + if (String(message) === CANCEL_REQUEST_MESSAGE) { + return { + success: false, + }; + } + + let msg; + let statusCode; + + if (response && response instanceof Object) { + const { data, statusText } = response; + statusCode = response.status; + msg = data.message || statusText; + } else { + statusCode = 600; + msg = error.message || 'Network Error'; + } + + /* eslint-disable */ + return Promise.reject({ + success: false, + statusCode, + message: msg, + }); + }); +} diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/theme.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/theme.js new file mode 100644 index 00000000..18b7728c --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/src/admin/utils/theme.js @@ -0,0 +1,29 @@ +// module.exports = { +// Color: { +// green: '#64ea91', +// blue: '#8fc9fb', +// purple: '#d897eb', +// red: '#f69899', +// yellow: '#f8c82e', +// peach: '#f797d6', +// borderBase: '#e5e5e5', +// borderSplit: '#f4f4f4', +// grass: '#d6fbb5', +// sky: '#c1e0fc', +// }, +// } + +const Color = { + green: '#64ea91', + blue: '#8fc9fb', + purple: '#d897eb', + red: '#f69899', + yellow: '#f8c82e', + peach: '#f797d6', + borderBase: '#e5e5e5', + borderSplit: '#f4f4f4', + grass: '#d6fbb5', + sky: '#c1e0fc', +}; + +export default Color; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/vite.config.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/vite.config.js new file mode 100644 index 00000000..bdba1810 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/vite/vite.config.js @@ -0,0 +1,57 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; +import { viteMockServe } from 'vite-plugin-mock'; +// import requireTransform from 'vite-plugin-require-transform'; + +let alias = { + '@': path.resolve('src/admin'), + api: path.resolve('src/admin/services/'), + components: path.resolve('src/admin/components'), + config: path.resolve('src/admin/utils/config'), + themes: path.resolve('src/admin/themes'), + utils: path.resolve('src/admin/utils'), + react: 'inula', // 新增 + 'react-dom': 'inula', // 新增 + 'react/jsx-dev-runtime': 'inula/jsx-dev-runtime', +}; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + react({ + babel: { + plugins: [ + '@babel/plugin-transform-react-jsx', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-proposal-export-default-from', + ], + }, + }), + viteMockServe({ + mockPath: './mock', + }), + ], + define: { + global: 'window', + }, + resolve: { + alias, + }, + optimizeDeps: { + esbuildOptions: { + loader: { + '.js': 'jsx', + '.ts': 'tsx', + }, + }, + }, + css: { + preprocessorOptions: { + less: { + javascriptEnabled: true, + }, + }, + }, +}); diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.editorconfig b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.editorconfig new file mode 100644 index 00000000..5a51806d --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.env b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.env new file mode 100644 index 00000000..6c5aec02 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.env @@ -0,0 +1,4 @@ +PROJECT_NAME=my-app +COMPILE_MODE=webpack +BUILD_PATH=./webpack/webpack.dev.js +DEBUG=true \ No newline at end of file diff --git a/.eslintignore b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.eslintignore similarity index 100% rename from .eslintignore rename to packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.eslintignore diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.eslintrc.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.eslintrc.js new file mode 100644 index 00000000..273023f9 --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.eslintrc.js @@ -0,0 +1,54 @@ +module.exports = { + extends: ['@nce/eslint-config-nce'], + parser: 'babel-eslint', + env: { + amd: true, + es6: true, + browser: true, + node: false, + }, + globals: { + Prel: true, + __webpack_public_path__: true, + Axios: true, + }, + settings: { + 'import/resolver': { + webpack: { + config: 'webpack/webpack.basic.js', + }, + }, + }, + rules: { + indent: [ + 'error', + 4, + { + SwitchCase: 1, + flatTernaryExpressions: true, + }, + ], + 'no-unused-vars': 'off', // 允许变量声明后未使用 + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'no-underscore-dangle': ['off', 'always'], // 允许私有变量 _xxx的变量命名方式 + 'react/jsx-indent-props': [2, 4], // 验证JSX中的props缩进 + 'react/prop-types': 0, // 防止在React组件定义中丢失props验证 + 'react/jsx-indent': [ + // 解决react里面的缩进问题 + 'error', + 4, + ], + 'filenames/match-exported': 0, + 'react/jsx-one-expression-per-line': 0, // 关闭一个表达式必须换行设定 + 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }], // 允许在 .js 和 .jsx 文件中使用 jsx + 'consistent-return': 0, + 'comma-dangle': [2, 'never'], // 组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号 + 'react/button-has-type': 0, // button无需强制声明类型 + 'react/no-array-index-key': 0, // 允许使用数组index作为组件的key + 'global-require': 0, // 允许require语句不出现在顶层中 + 'no-nested-ternary': 0, // 允许嵌套三元表达式 + 'no-unused-expressions': 0, // 允许使用未执行的表达式。比如fn是一个函数,允许 fn && fn() + 'no-throw-literal': 0, // 允许throw抛出对象格式 + '@typescript-eslint/member-ordering': 0, // 禁用TypeScript声明规范 + }, +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.gitignore b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.gitignore new file mode 100644 index 00000000..30ca8b5e --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.gitignore @@ -0,0 +1,5 @@ +/node_modules +.idea +.vscode +package-lock.json +/build diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.horizon.ts b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.horizon.ts new file mode 100644 index 00000000..7f3bb9ea --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.horizon.ts @@ -0,0 +1,4 @@ +export default { + enableMock: true, + mockPath: './mock', +}; diff --git a/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierignore b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierignore new file mode 100644 index 00000000..a3360cac --- /dev/null +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierignore @@ -0,0 +1,4 @@ +node_modules/ +webpack/ +build/ +public/ diff --git a/.prettierrc.js b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierrc.js similarity index 53% rename from .prettierrc.js rename to packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierrc.js index da8f33e7..3b898b2a 100644 --- a/.prettierrc.js +++ b/packages/create-inula/lib/generators/InulaJS-antd/templates/webpack/.prettierrc.js @@ -1,20 +1,3 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * InulaJS is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -'use strict'; - module.exports = { printWidth: 120, // 一行120字符数,如果超过会进行换行 tabWidth: 2, // tab等2个空格 @@ -25,7 +8,7 @@ module.exports = { jsxSingleQuote: false, // 在JSX中使用双引号 trailingComma: 'es5', // 使用尾逗号(对象、数组等) bracketSpacing: true, // 对象的括号间增加空格 - bracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾 + jsxBracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾 arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号 vueIndentScriptAndStyle: false, // 不缩进Vue文件中的 + + diff --git a/packages/create-inula/lib/generators/Simple-app/templates/vite/package.json b/packages/create-inula/lib/generators/Simple-app/templates/vite/package.json new file mode 100644 index 00000000..5a774e56 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/vite/package.json @@ -0,0 +1,23 @@ +{ + "name": "vite", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "inula": "^0.0.44" + }, + "devDependencies": { + "@babel/core": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.18.6", + "@vitejs/plugin-react": "^3.1.0", + "@vitejs/plugin-react-refresh": "^1.3.6", + "babel-plugin-import": "^1.13.6" + } +} diff --git a/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.css b/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.css new file mode 100644 index 00000000..585241e5 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.css @@ -0,0 +1,54 @@ +* { + box-sizing: border-box; +} + +body, html { + margin: 0; + padding: 0; + font-family: 'Montserrat', sans-serif; + line-height: 1.6; + color: #fff; + background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%); + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.container { + text-align: center; +} + +.hero-title { + font-size: 3em; + margin-bottom: 20px; +} + +.hero-subtitle { + font-size: 1.5em; + margin-bottom: 50px; +} + +.content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.card { + background: rgba(255, 255, 255, 0.1); + border-radius: 5px; + padding: 20px; + width: 45%; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4px); +} + +.card h2, .card p { + color: #fff; +} + +.card a { + color: #fff; + text-decoration: underline; +} \ No newline at end of file diff --git a/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.jsx b/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.jsx new file mode 100644 index 00000000..63e6b877 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/vite/src/index.jsx @@ -0,0 +1,25 @@ +import inula from 'inula'; +import './index.css'; + +function App() { + return ( +
    +
    +

    欢迎来到 Inula 项目!

    +

    你已成功创建你的第一个 Inula 项目

    +
    +
    +
    +

    开始吧

    +

    编辑 src/index.jsx 并保存以重新加载。

    +
    +
    +

    了解更多

    +

    要了解 Inula,查看 Inula 文档

    +
    +
    +
    + ); +} + +inula.render(, document.getElementById('root')); diff --git a/packages/create-inula/lib/generators/Simple-app/templates/vite/vite.config.js b/packages/create-inula/lib/generators/Simple-app/templates/vite/vite.config.js new file mode 100644 index 00000000..aca9a402 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/vite/vite.config.js @@ -0,0 +1,14 @@ +import react from '@vitejs/plugin-react'; + +let alias = { + react: 'inula', // 新增 + 'react-dom': 'inula', // 新增 + 'react/jsx-dev-runtime': 'inula/jsx-dev-runtime', +}; + +export default { + plugins: [react()], + resolve: { + alias, + }, +}; diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/package.json b/packages/create-inula/lib/generators/Simple-app/templates/webpack/package.json new file mode 100644 index 00000000..4150b290 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/package.json @@ -0,0 +1,29 @@ +{ + "name": "inula-init", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "webpack serve --mode development", + "build": "webpack --mode production" + }, + "author": "", + "license": "ISC", + "dependencies": { + "inula": "^0.0.44" + }, + "devDependencies": { + "@babel/core": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.18.6", + "babel-loader": "^9.1.2", + "css-loader": "^6.7.3", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.0", + "style-loader": "^3.3.2", + "url-loader": "^4.1.1", + "webpack": "^5.77.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.13.2" + } +} diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.js b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.js new file mode 100644 index 00000000..afb65bca --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/App.js @@ -0,0 +1,27 @@ +import inula from 'inula'; +import './styles.css'; + +class App extends inula.Component { + render() { + return ( +
    +
    +

    欢迎来到 Inula 项目!

    +

    你已成功创建你的第一个 Inula 项目

    +
    +
    +
    +

    开始吧

    +

    编辑 src/App.js 并保存以重新加载。

    +
    +
    +

    了解更多

    +

    要了解 Inula,查看 Inula 文档

    +
    +
    +
    + ); + } +} + +export default App; diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.html b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.html new file mode 100644 index 00000000..8207fc09 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.html @@ -0,0 +1,11 @@ + + + + + React App + + +
    + + + diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.js b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.js new file mode 100644 index 00000000..fe8b6996 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/index.js @@ -0,0 +1,4 @@ +import inula from 'inula'; +import App from './App'; + +inula.render(, document.getElementById('root')); diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/styles.css b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/styles.css new file mode 100644 index 00000000..585241e5 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/src/styles.css @@ -0,0 +1,54 @@ +* { + box-sizing: border-box; +} + +body, html { + margin: 0; + padding: 0; + font-family: 'Montserrat', sans-serif; + line-height: 1.6; + color: #fff; + background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%); + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.container { + text-align: center; +} + +.hero-title { + font-size: 3em; + margin-bottom: 20px; +} + +.hero-subtitle { + font-size: 1.5em; + margin-bottom: 50px; +} + +.content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.card { + background: rgba(255, 255, 255, 0.1); + border-radius: 5px; + padding: 20px; + width: 45%; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4px); +} + +.card h2, .card p { + color: #fff; +} + +.card a { + color: #fff; + text-decoration: underline; +} \ No newline at end of file diff --git a/packages/create-inula/lib/generators/Simple-app/templates/webpack/webpack.config.js b/packages/create-inula/lib/generators/Simple-app/templates/webpack/webpack.config.js new file mode 100644 index 00000000..40bb50a8 --- /dev/null +++ b/packages/create-inula/lib/generators/Simple-app/templates/webpack/webpack.config.js @@ -0,0 +1,65 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + [ + '@babel/preset-react', + { + runtime: 'automatic', // 新增 + importSource: 'inula', // 新增 + }, + ], + ], + }, + }, + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'images/', + publicPath: 'images/', + }, + }, + ], + }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + use: ['file-loader'], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, 'src/index.html'), + filename: 'index.html', + }), + ], + devServer: { + static: path.join(__dirname, 'dist'), + compress: true, + port: 9000, + }, +}; diff --git a/packages/create-inula/lib/run.js b/packages/create-inula/lib/run.js new file mode 100644 index 00000000..e527f25b --- /dev/null +++ b/packages/create-inula/lib/run.js @@ -0,0 +1,68 @@ +const fs = require('fs'); +const path = require('path'); +const chalk = require('chalk'); +const mkdirp = require('mkdirp'); +const inquirer = require('inquirer'); +const yeoman = require('yeoman-environment'); + +const generatorType = fs + .readdirSync(`${__dirname}/generators`) + .filter(f => !f.startsWith('.')) + .map(f => { + return { + name: f, + value: f, + short: f, + }; + }); + +const runGenerator = async (generatorPath, { name = '', cwd = process.cwd(), args = {} }) => { + return new Promise(resolve => { + if (name) { + mkdirp.sync(name); + cwd = path.join(cwd, name); + } + + const Generator = require(generatorPath); + const env = yeoman.createEnv([], { + cwd, + }); + const generator = new Generator({ + name, + env, + resolved: require.resolve(generatorPath), + args, + }); + return generator.run(() => { + console.log('File Generate Done'); + resolve(true); + }); + }); +}; + +const run = async config => { + process.send && process.send({ type: 'prompt' }); + process.emit('message', { type: 'prompt' }); + + let { type } = config; + if (!type) { + const answers = await inquirer.prompt([ + { + name: 'type', + message: 'Please select the template', + type: 'list', + choices: generatorType, + }, + ]); + type = answers.type; + } + const templatePath = `./generators/${type}`; + try { + return runGenerator(templatePath, config); + } catch (e) { + console.error(chalk.red(`> Generate failed`), e); + process.exit(1); + } +}; + +module.exports = run; diff --git a/packages/create-inula/package.json b/packages/create-inula/package.json new file mode 100644 index 00000000..f813621a --- /dev/null +++ b/packages/create-inula/package.json @@ -0,0 +1,21 @@ +{ + "name": "create-inula", + "version": "1.0.0", + "description": "", + "main": "index.js", + "bin": { + "create-inula": "bin/cli.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "chalk": "4.0.0", + "glob": "^10.3.3", + "yargs-parser": "^21.1.1", + "yeoman-environment": "^3.15.0", + "yeoman-generator": "^5.8.0" + }, + "devDependencies": { + "lodash": "^4.17.21" + } +} diff --git a/packages/inula/.editorconfig b/packages/inula/.editorconfig new file mode 100644 index 00000000..5a51806d --- /dev/null +++ b/packages/inula/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + diff --git a/packages/inula/.gitignore b/packages/inula/.gitignore new file mode 100644 index 00000000..aa674023 --- /dev/null +++ b/packages/inula/.gitignore @@ -0,0 +1,6 @@ +node_modules +.idea +.vscode +package-lock.json +libs/**/dist +scripts/*.ejs diff --git a/License b/packages/inula/License similarity index 100% rename from License rename to packages/inula/License diff --git a/README.md b/packages/inula/README.md similarity index 100% rename from README.md rename to packages/inula/README.md diff --git a/babel.config.js b/packages/inula/babel.config.js similarity index 100% rename from babel.config.js rename to packages/inula/babel.config.js diff --git a/packages/inula/build/inula/@types/dom/DOMExternal.d.ts b/packages/inula/build/inula/@types/dom/DOMExternal.d.ts new file mode 100644 index 00000000..5d9dab18 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMExternal.d.ts @@ -0,0 +1,8 @@ +import { asyncUpdates } from '../renderer/Renderer'; +import { createPortal } from '../renderer/components/CreatePortal'; +import type { Container } from './DOMOperator'; +import { Callback } from '../renderer/UpdateHandler'; +declare function executeRender(children: any, container: Container, callback?: Callback): Element | Text; +declare function findDOMNode(domOrEle?: Element): null | Element | Text; +declare function destroy(container: Container): boolean; +export { createPortal, asyncUpdates as unstable_batchedUpdates, findDOMNode, executeRender as render, destroy as unmountComponentAtNode, }; diff --git a/packages/inula/build/inula/@types/dom/DOMInternalKeys.d.ts b/packages/inula/build/inula/@types/dom/DOMInternalKeys.d.ts new file mode 100644 index 00000000..871b95e1 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMInternalKeys.d.ts @@ -0,0 +1,12 @@ +/** + * 文件整体功能:给dom节点赋 VNode 的结构体和事件初始化标记 + */ +import type { VNode } from '../renderer/Types'; +import type { Container, Props } from './DOMOperator'; +export declare function getDom(vNode: VNode): Element | Text | null; +export declare function saveVNode(vNode: VNode, dom: Element | Text | Container): void; +export declare function getVNode(dom: Node | Container): VNode | null; +export declare function getNearestVNode(dom: Node): null | VNode; +export declare function getVNodeProps(dom: Element | Text): Props | null; +export declare function updateVNodeProps(dom: Element | Text, props: Props): void; +export declare function getNonDelegatedListenerMap(dom: Element | Text): Map; diff --git a/packages/inula/build/inula/@types/dom/DOMOperator.d.ts b/packages/inula/build/inula/@types/dom/DOMOperator.d.ts new file mode 100644 index 00000000..cf74e475 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMOperator.d.ts @@ -0,0 +1,31 @@ +import type { VNode } from '../renderer/Types'; +export declare type Props = Record & { + autoFocus?: boolean; + children?: any; + dangerouslySetInnerHTML?: any; + disabled?: boolean; + hidden?: boolean; + style?: { + display?: string; + }; +}; +export declare type Container = (Element & { + _treeRoot?: VNode | null; +}) | (Document & { + _treeRoot?: VNode | null; +}); +export declare function getNSCtx(parentNS: string, type: string, dom?: Container): string; +export declare function prepareForSubmit(): void; +export declare function resetAfterSubmit(): void; +export declare function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element; +export declare function initDomProps(dom: Element, tagName: string, rawProps: Props): boolean; +export declare function getPropChangeList(dom: Element, type: string, lastRawProps: Props, nextRawProps: Props): Object; +export declare function isTextChild(type: string, props: Props): boolean; +export declare function newTextDom(text: string, processing: VNode): Text; +export declare function submitDomUpdate(tag: string, vNode: VNode): void; +export declare function clearText(dom: Element): void; +export declare function appendChildElement(parent: Element | Container, child: Element | Text): void; +export declare function insertDomBefore(parent: Element | Container, child: Element | Text, beforeChild: Element | Text): void; +export declare function removeChildDom(parent: Element | Container, child: Element | Text): void; +export declare function hideDom(tag: string, dom: Element | Text): void; +export declare function unHideDom(tag: string, dom: Element | Text, props?: Props): void; diff --git a/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/DOMPropertiesHandler.d.ts b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/DOMPropertiesHandler.d.ts new file mode 100644 index 00000000..eff6c32f --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/DOMPropertiesHandler.d.ts @@ -0,0 +1,2 @@ +export declare function setDomProps(dom: Element, props: Object, isNativeTag: boolean, isInit: boolean): void; +export declare function compareProps(oldProps: Object, newProps: Object): Object; diff --git a/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/StyleHandler.d.ts b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/StyleHandler.d.ts new file mode 100644 index 00000000..fc468d0d --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/StyleHandler.d.ts @@ -0,0 +1,10 @@ +/** + * 对一些没有写单位的样式进行适配,例如:width: 10 => width: 10px + * 对空值或布尔值进行适配,转为空字符串 + * 去掉多余空字符 + */ +export declare function adjustStyleValue(name: any, value: any): any; +/** + * 设置 DOM 节点的 style 属性 + */ +export declare function setStyles(dom: any, styles: any): void; diff --git a/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/UpdateCommonProp.d.ts b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/UpdateCommonProp.d.ts new file mode 100644 index 00000000..b1837b7d --- /dev/null +++ b/packages/inula/build/inula/@types/dom/DOMPropertiesHandler/UpdateCommonProp.d.ts @@ -0,0 +1,6 @@ +/** + * 给 dom 设置属性 + * attrName 指代码中属性设置的属性名称(如 class) + * 多数情况 attrName 仅用作初始 DOM 节点对象使用,而 property 更多用于页面交互 + */ +export declare function updateCommonProp(dom: Element, attrName: string, value: any, isNativeTag: boolean): void; diff --git a/packages/inula/build/inula/@types/dom/SelectionRangeHandler.d.ts b/packages/inula/build/inula/@types/dom/SelectionRangeHandler.d.ts new file mode 100644 index 00000000..5d08cc4b --- /dev/null +++ b/packages/inula/build/inula/@types/dom/SelectionRangeHandler.d.ts @@ -0,0 +1,16 @@ +export declare function hasSelectionProperties(dom: any): boolean; +export declare function getSelectionInfo(): { + focusedDom: import("./utils/Interface").InulaDom; + selectionRange: { + start: number; + end: number; + }; +}; +export interface SelectionData { + focusedDom: HTMLInputElement | HTMLTextAreaElement | void; + selectionRange?: { + start: number; + end: number; + }; +} +export declare function resetSelectionRange(preSelectionRangeData: SelectionData): void; diff --git a/packages/inula/build/inula/@types/dom/utils/Common.d.ts b/packages/inula/build/inula/@types/dom/utils/Common.d.ts new file mode 100644 index 00000000..12f251af --- /dev/null +++ b/packages/inula/build/inula/@types/dom/utils/Common.d.ts @@ -0,0 +1,17 @@ +import { InulaDom } from './Interface'; +import { Props } from '../DOMOperator'; +/** + * 获取当前聚焦的 input 或者 textarea 元素 + * @param doc 指定 document + */ +export declare function getFocusedDom(doc?: Document): InulaDom | null; +export declare function getIFrameFocusedDom(): InulaDom; +export declare function isElement(dom: any): boolean; +export declare function isText(dom: any): boolean; +export declare function isComment(dom: any): boolean; +export declare function isDocument(dom: any): boolean; +export declare function isDocumentFragment(dom: any): boolean; +export declare function getDomTag(dom: any): any; +export declare function isInputElement(dom: Element): dom is HTMLInputElement; +export declare function shouldAutoFocus(tagName: string, props: Props): boolean; +export declare function isNotNull(object: any): boolean; diff --git a/packages/inula/build/inula/@types/dom/utils/DomCreator.d.ts b/packages/inula/build/inula/@types/dom/utils/DomCreator.d.ts new file mode 100644 index 00000000..1b07d6b3 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/utils/DomCreator.d.ts @@ -0,0 +1,6 @@ +export declare const NSS: { + html: string; + math: string; + svg: string; +}; +export declare function createDom(tagName: string, parentNamespace: string, doc: Document): Element; diff --git a/packages/inula/build/inula/@types/dom/utils/Interface.d.ts b/packages/inula/build/inula/@types/dom/utils/Interface.d.ts new file mode 100644 index 00000000..d8344eef --- /dev/null +++ b/packages/inula/build/inula/@types/dom/utils/Interface.d.ts @@ -0,0 +1,7 @@ +export interface Props { + [propName: string]: any; +} +export interface InulaSelect extends HTMLSelectElement { + _multiple?: boolean; +} +export declare type InulaDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; diff --git a/packages/inula/build/inula/@types/dom/validators/PropertiesData.d.ts b/packages/inula/build/inula/@types/dom/validators/PropertiesData.d.ts new file mode 100644 index 00000000..b41a39e4 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/validators/PropertiesData.d.ts @@ -0,0 +1,13 @@ +export declare enum PROPERTY_TYPE { + BOOLEAN = 0, + STRING = 1, + SPECIAL = 2, + BOOLEAN_STR = 3 +} +export declare type PropDetails = { + propName: string; + type: PROPERTY_TYPE; + attrName: string; + attrNS: string | null; +}; +export declare function getPropDetails(name: string): PropDetails | null; diff --git a/packages/inula/build/inula/@types/dom/validators/ValidateProps.d.ts b/packages/inula/build/inula/@types/dom/validators/ValidateProps.d.ts new file mode 100644 index 00000000..eb9e80a0 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/validators/ValidateProps.d.ts @@ -0,0 +1,5 @@ +import { PropDetails } from './PropertiesData'; +export declare function isNativeElement(tagName: string, props: Record): boolean; +export declare function isEventProp(propName: any): boolean; +export declare function isInvalidValue(name: string, value: any, propDetails: PropDetails | null, isNativeTag: boolean): boolean; +export declare function validateProps(type: any, props: any): void; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/InputValueHandler.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/InputValueHandler.d.ts new file mode 100644 index 00000000..e58f3050 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/InputValueHandler.d.ts @@ -0,0 +1,9 @@ +import { Props } from '../utils/Interface'; +export declare function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props): { + value: any; + defaultValue: any; + defaultChecked: any; + checked: any; +}; +export declare function updateInputValue(dom: HTMLInputElement, props: Props): void; +export declare function setInitInputValue(dom: HTMLInputElement, props: Props): void; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/OptionValueHandler.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/OptionValueHandler.d.ts new file mode 100644 index 00000000..29e4e9a2 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/OptionValueHandler.d.ts @@ -0,0 +1,4 @@ +import { Props } from '../utils/Interface'; +export declare function getOptionPropsWithoutValue(dom: Element, props: Props): { + children: string; +}; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/SelectValueHandler.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/SelectValueHandler.d.ts new file mode 100644 index 00000000..6da92e4f --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/SelectValueHandler.d.ts @@ -0,0 +1,12 @@ +import { InulaSelect, Props } from '../utils/Interface'; +export declare function getSelectPropsWithoutValue(dom: InulaSelect, properties: Object): { + value: any; + constructor: Function; + toString(): string; + toLocaleString(): string; + valueOf(): Object; + hasOwnProperty(v: PropertyKey): boolean; + isPrototypeOf(v: Object): boolean; + propertyIsEnumerable(v: PropertyKey): boolean; +}; +export declare function updateSelectValue(dom: InulaSelect, props: Props, isInit?: boolean): void; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/TextareaValueHandler.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/TextareaValueHandler.d.ts new file mode 100644 index 00000000..3ffd8e43 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/TextareaValueHandler.d.ts @@ -0,0 +1,12 @@ +import { Props } from '../utils/Interface'; +export declare function getTextareaPropsWithoutValue(dom: HTMLTextAreaElement, properties: Object): { + value: any; + constructor: Function; + toString(): string; + toLocaleString(): string; + valueOf(): Object; + hasOwnProperty(v: PropertyKey): boolean; + isPrototypeOf(v: Object): boolean; + propertyIsEnumerable(v: PropertyKey): boolean; +}; +export declare function updateTextareaValue(dom: HTMLTextAreaElement, props: Props, isInit?: boolean): void; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/ValueChangeHandler.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/ValueChangeHandler.d.ts new file mode 100644 index 00000000..174371b9 --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/ValueChangeHandler.d.ts @@ -0,0 +1,7 @@ +/** + * value值发生变化时,执行value的getter、setter。 + * 事件触发时,判断currentVal 和 input 的真实值是否一致,从而判断是否实际变更, + * 只有发生变更了,事件处理才会生成一个change事件 + */ +export declare function watchValueChange(dom: any): void; +export declare function updateInputHandlerIfChanged(dom: any): boolean; diff --git a/packages/inula/build/inula/@types/dom/valueHandler/index.d.ts b/packages/inula/build/inula/@types/dom/valueHandler/index.d.ts new file mode 100644 index 00000000..a20d267b --- /dev/null +++ b/packages/inula/build/inula/@types/dom/valueHandler/index.d.ts @@ -0,0 +1,9 @@ +/** + *