{"id":41885275,"url":"https://github.com/wxsub/vite-element-template","last_synced_at":"2026-01-25T13:27:16.339Z","repository":{"id":238609467,"uuid":"796979409","full_name":"wxsub/vite-element-template","owner":"wxsub","description":"Vite Element Admin is a lightweight , high-performance Admin front-end framework system , using TypeScript + ElementPlus + Tailwindcss + Vue3 and other popular popular front-end technology languages of the moment , the framework integrates Formkit Jsonized form generation components , file routing system , automation components , MockJs and so on.","archived":false,"fork":false,"pushed_at":"2026-01-04T09:04:04.000Z","size":5825,"stargazers_count":14,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2026-01-05T09:23:08.516Z","etag":null,"topics":["docker","element-plus","tailwind-css","typescript","vitejs","vue3","vue3-admin","vue3-typescript","vuejs","web-admin"],"latest_commit_sha":null,"homepage":"","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wxsub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-07T01:21:24.000Z","updated_at":"2026-01-04T09:04:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"1fa567f6-2f8c-4519-8fe4-746a60806dd4","html_url":"https://github.com/wxsub/vite-element-template","commit_stats":null,"previous_names":["wxsub/chat-website-vite"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wxsub/vite-element-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wxsub%2Fvite-element-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wxsub%2Fvite-element-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wxsub%2Fvite-element-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wxsub%2Fvite-element-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wxsub","download_url":"https://codeload.github.com/wxsub/vite-element-template/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wxsub%2Fvite-element-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28753417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T10:25:12.305Z","status":"ssl_error","status_checked_at":"2026-01-25T10:25:11.933Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","element-plus","tailwind-css","typescript","vitejs","vue3","vue3-admin","vue3-typescript","vuejs","web-admin"],"created_at":"2026-01-25T13:27:15.661Z","updated_at":"2026-01-25T13:27:16.328Z","avatar_url":"https://github.com/wxsub.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vite-element-template webapp\r\n\r\n**English** | [中文](./README.zh_CN.md)\r\n\r\nBased on vue3+vite+elementPlus a fast front-end back-end framework to build the project, the project integrates automated publishing solutions, elementUI dynamic component import, axios, mockjs and docker and so on.\r\n\r\n**Project node version no less than 18.0.0**\r\n\r\n## Project Structure\r\n\r\n```\r\n├── .github/                # GitHub workflows configuration\r\n├── deploy/                 # Deployment scripts and configurations\r\n├── mock/                   # Mock data for development\r\n├── public/                 # Static assets\r\n├── src/                    # Source code\r\n│   ├── Layouts/            # Layout components\r\n│   ├── assets/             # Project assets (styles, icons)\r\n│   ├── components/         # Reusable components\r\n│   ├── composable/         # Composable functions\r\n│   ├── config/             # Application configurations\r\n│   ├── middleware/         # Route middleware\r\n│   ├── pages/              # Page components (auto-routing)\r\n│   ├── store/              # Vuex/Pinia store\r\n│   ├── types/              # TypeScript type definitions\r\n│   ├── utils/              # Utility functions\r\n│   ├── App.vue             # Root component\r\n│   └── main.ts             # Application entry point\r\n├── .env.*                  # Environment variables for different environments\r\n├── index.html              # HTML template\r\n├── package.json            # Project dependencies and scripts\r\n├── tailwind.config.js      # Tailwind CSS configuration\r\n├── tsconfig.json           # TypeScript configuration\r\n└── vite.config.ts          # Vite configuration\r\n```\r\n\r\n```\r\nnpm install -g pnpm\r\n// check you pnpm pnpm -v\r\n\r\n// development startup\r\npnpm install\r\npnpm run dev\r\n\r\n// build\r\npnpm run build\r\n```\r\n\r\n## Docker way development startup\r\n1. [install docker](https://www.docker.com)\r\n2. Build and start the container\r\n```\r\ndocker-compose up --build -d\r\n```\r\n1. Enter the container startup service\r\n```\r\ndocker-compose exec webapp-page /bin/bash\r\n\r\npnpm install\r\npnpm run dev\r\n```\r\n\r\n[Demo Preview](https://wxsub.github.io/vite-element-template/)\r\n\r\n## Tailwindcss\r\nsystem built-in tailwindcss, tailwindcss is a very good css auxiliary script, with vue3 dynamic introduction of the design, so most of the css don't have to draw, special needs style processing, please check under src/assets/styles/ whether there are\r\nsimilar css module, if there is no corresponding css module, you may need to manually write in the style tag, of course, it is also a very small part of the recommended fast writing css reference\r\n[tailwindcss documentation](https://tailwindcss.com/docs/), with editor's automatic prompts, you will get amazing development story.\r\nOf course use your VSCode plugin with is a good choice [plugin - search and install tailwindcss]\r\n*vscode No smart tips？*\r\n```json\r\n{\r\n  \"editor.quickSuggestions\": {\r\n    \"strings\": true\r\n  }\r\n}\r\n```\r\n\r\n## Application Components and Combinatorial Approach（composable）\r\nIt is recommended that application components be placed in the src/components folder whenever possible to improve code maintainability.\r\n\r\nFor more frequently used components or js, you need to move the files into `src/composable`, the files in this directory will be automatically loaded by the system without the need to manually bring in your pages or components, you don't need to worry about scanning costs, because it is still on-demand, you just need to use the corresponding file name within the business to load your business components.\r\n\r\nFor ts or js files, please use export to throw your method, you can use your thrown method anywhere without introducing it. For example:\r\n\r\n```\r\n// src/composable/useAxios.ts\r\nimport service from '@/config/axios.config'\r\n\r\nexport function useAxios() {\r\n  return service\r\n}\r\n\r\n// src/store/modules/user.ts\r\nfunction login(loginData: object) {\r\n  return new Promise\u003cvoid\u003e(async (resolve, reject) =\u003e {\r\n    try {\r\n      const response: any = await useAxios().post(\"/login\", loginData)\r\n      if (response?.token) {\r\n        useStorage\u003cstring\u003e(\"XSRF-TOKEN\", response.token)\r\n        resolve(response)\r\n      } else reject(response)\r\n    } catch (e) {\r\n      console.log(e)\r\n      reject(e)\r\n    }\r\n  })\r\n}\r\n```\r\n\r\n## Pages file routing and Layout system\r\nThe system checks for any .vue files in the src/pages folder and generates a route structure based on the file name. This way, you routes no longer need to maintain an array when adding routes to your application. So you need to design your pages in a consistent and specific way. Route meta information needs to be written in the route tag using yaml syntax, as follows:\r\n```html\r\n\u003croute lang=\"yaml\"\u003e\r\n  meta:\r\n    layout: \"default\"\r\n    title: \"C\"\r\n\u003c/route\u003e\r\n\r\n// or\r\n\r\n\u003croute\u003e\r\n  {\r\n    meta: { title: \"A\", roles: [] }\r\n  }\r\n\u003c/route\u003e\r\n```\r\nFor the name field of the route, you don't need to add it intentionally, the system will automatically generate it, of course, even if you add it, the system will not use it.\r\n\r\nThis way of writing in the editor there may not be able to highlight, it is difficult to see, so if necessary, through the following steps to set your Vetur\r\nupdate setting\r\n```json\r\n\"vetur.grammar.customBlocks\": {\r\n  \"route\": \"json\"\r\n}\r\n```\r\nRun the command in vscode\r\n```\r\nVetur: Generate grammar from vetur.grammar.customBlocks\r\n```\r\nRestart VS Code to get syntax highlighting for custom blocks.\r\n\r\nThe Layout System uses a design that mixes layouts into routes, for unconventional layouts you have to design your layout in the Layouts directory of the system, of course when designing a layout you need to avoid Chinese filenames, because it still uses the filename as the key.\r\nOf course, you need to avoid Chinese filenames when designing layouts, because it still uses the filename as the key.\r\n```vue\r\n// Specify fullscreen as layout\r\n\u003croute\u003e\r\n  {\r\n    meta: { title: \"B\", roles: [], layout: \"fullscreen\" }\r\n  }\r\n\u003c/route\u003e\r\n```\r\nTips：The layout field points to all the .vue files in the Layouts folder (default default) you need to have the same name as the file, as this will cause the route generation to fail.\r\n\r\n### Explanation of meta legal attributes in route tags\r\n\r\n**Warning**：Based on the rules of unplugin-vue-router, custom fields are deposited in the meta attribute if possible, otherwise they will not be recognized\r\n\r\n1. **title**: Routing title\r\n2. **permission**: route permissions\r\n3. **layout**: the layout used by the current route, the name of the file in the `src/Layouts/*.vue` directory\r\n4. **icon**: the current route icon, for sidebar component rendering, your icon field rules refer to [`svg-icon`](#use svg icon) component `name` field rules\r\n5. **hidden**: hide the current route, the current route will not appear in the sidebar, but can be accessed through the link to the page\r\n\r\n**Tips**: All files (routes) in the `pages/redirect` directory will not be designed by the permissions system\r\n\r\n### Index routing\r\nAny index.vue (must be all lowercase) file generates an empty route (similar to an index.html file):\r\n1. src/pages/index.vue: generates the / route\r\n2. src/pages/users/index.vue: generates the /users route\r\n\r\n### Nested routes\r\nNested layouts can be created using Vue Router subroutes. Nested routes are automatically defined by defining files next to a .vue folder of the same name. If both and component are created, they will be rendered in the.\r\n` src/pages/users/index.vuesrc/pages/users.vuesrc/pages/users/index.vuesrc/pages/users.vue\u003cRouterView\u003e `\r\nIn other words, given the following folder structure:\r\n\r\n```\r\nsrc/pages/\r\n  ├── users/\r\n  │  ├── _id.vue\r\n  │  └── index.vue\r\n  └── users.vue\r\n```\r\n\r\nYou will get this array of routes：\r\n\r\n```\r\nconst routes = [\r\n  {\r\n    path: '/users',\r\n    component: () =\u003e import('src/pages/users.vue'),\r\n    children: [\r\n      { path: '', component: () =\u003e import('src/pages/users/index.vue') },\r\n    ],\r\n  },\r\n]\r\n```\r\nAnd omitting that src/pages/users.vue component will generate the following route：\r\n```\r\nconst routes = [\r\n  {\r\n    path: '/users',\r\n    // notice how there is no component here\r\n    children: [\r\n      { path: '', component: () =\u003e import('src/pages/users/index.vue') },\r\n    ],\r\n  },\r\n]\r\n```\r\n\r\n### Nested Routing without Nested Layouts\r\nSometimes you may want to add nesting to URLs in the form of slashes, but you don't want to affect the UI hierarchy. Consider the following folder structure:\r\n```\r\nsrc/pages/\r\n├── users/\r\n│   ├── _id.vue\r\n│   └── index.vue\r\n└── users.vue\r\n```\r\nTo add a new route, /users/create could add a new file, src/pages/users/create.vue but this would nest the create.vue component within the component users.vue. To avoid this, you can create a file src/pages/users.create .vue. When you generate the route, the file . will become: /\r\n```\r\nconst routes = [\r\n  {\r\n    path: '/users',\r\n    component: () =\u003e import('src/pages/users.vue'),\r\n    children: [\r\n      { path: '', component: () =\u003e import('src/pages/users/index.vue') },\r\n      { path: ':id', component: () =\u003e import('src/pages/users/_id.vue') },\r\n    ],\r\n  },\r\n  {\r\n    path: '/users/create',\r\n    component: () =\u003e import('src/pages/users.create.vue'),\r\n  },\r\n]\r\n```\r\n\r\n### Capture All / 404 Route Not Found\r\nTo create a capture all route, ... Add three dots ( ) before the parameter name, e.g., src/pages/[... .path].vue will create routes with the following paths: /:path(. *).\r\n\r\nThis will match any route. Note that this can also be executed within a folder, e.g., src/pages/articles/[... .path].vue will create routes with the following path: /articles/:path(. *).\r\n\r\n### Naming routes\r\nAll generated routes will include the name attribute if they contain the component attribute. This prevents accidentally directing users to the parent route. By default, names are generated using file paths, but you can override this behavior by passing a custom getRouteName() function. You can do TypeScript validation almost anywhere, so changing this setting should be easy.\r\n\r\n### Normal legal use\r\n```\r\n├── about\r\n│ └── index.vue\r\n├── about.vue # Access this page via /about\r\n├── blog # Access the page via /blog\r\n│ ├── _id.vue # Dynamic routing\r\n│ ├── index.vue\r\n│ └── today\r\n├── components.vue\r\n└── index.vue # Default page when accessing local IP and port numbers\r\n```\r\nWhile the system will scan all `.vue` files in the pages directory, it will of course filter the components directory so you don't have to be concerned about them being scanned into the routing system.\r\nHowever, it is generally not recommended to write your components files in the pages directory.\r\n\r\nYou should also note that you should avoid using special symbols to create new files or folders in the pages directory, especially “-”, as this will cause the routing recursion to fail and the program to terminate.\r\n\r\n### Capturing routing permissions\r\nThe required routing information and permission resources will all come from `/composable/usePermissions.ts`. You can export its `permission` method, and the logic related to route permission verification will be stored there.\r\n\r\n### Route folders for multiple routes\r\nYou need to find plugins -\u003e VueRouter in the configurator (vite.config.ts) and add routesFolder and pass an array to provide multiple routes folders, routesFolder contains by default: src/pages\r\n\r\n```\r\nVueRouter({\r\n  routesFolder: ['src/pages', 'src/admin/routes'],\r\n})\r\n```\r\nYou can also provide a path prefix for each folder, which will be used as is and cannot start with / but can contain any arguments you want, even without ending with /:\r\n```\r\nVueRouter({\r\n  routesFolder: [\r\n    'src/pages',\r\n    {\r\n      src: 'src/admin/routes',\r\n      // note there is always a trailing slash and never a leading one\r\n      path: 'admin/',\r\n      // src/admin/routes/dashboard.vue -\u003e /admin/dashboard\r\n    },\r\n    {\r\n      src: 'src/docs',\r\n      // you can add parameters\r\n      path: 'docs/:lang/',\r\n      // src/docs/introduction.vue -\u003e /docs/:lang/introduction\r\n    },\r\n    {\r\n      src: 'src/promos',\r\n      // you can omit the trailing slash\r\n      path: 'promos-',\r\n      // src/promos/black-friday.vue -\u003e /promos-black-friday\r\n    },\r\n  ],\r\n})\r\n```\r\nPlease note that the folders provided must be independent and that a routing folder cannot contain another specified routing folder. If you need further customization, try using the [DefinePage() method](https://uvr.esm.is/guide/extending-routes#definepage).\r\n\r\n## Use svg icon\r\nThe system uses svg icon, svg icons are put into `/src/assets/icons` by default (if you want to change the directory, please change it in `createSvgIconsPlugin` in `vite.config.ts`), when you use it, please use the svgIcon component (the system has registered it by default).\r\nFor example:\r\n```html\r\n\u003csvg-icon name=\"github\"\u003e\u003c/svg-icon\u003e\r\n\r\n// or\r\n\r\n\u003csvg-icon name=\"home-wxsub-dark\"\u003e\u003c/svg-icon\u003e  // [dir name]-[svg file name]\r\n```\r\n\r\n## Autoload Element Plus Icon Icon Library\r\nThe system integrates unplugin-icons, unplugin-auto-import plugin rules to automatically import any icon set from iconify. So you can use Element Plus Icon icon library in your page without importing this icon, here is the use case\r\n```\r\n// use \u003cel-icon\u003e\u003cPlus /\u003e\u003c/el-icon\u003e\r\n\u003cel-icon\u003e\u003ci-ep-plus /\u003e\u003c/el-icon\u003e\r\n\r\n// use \u003cel-icon\u003e\u003cArrowDown /\u003e\u003c/el-icon\u003e\r\n\u003cel-icon\u003e\u003ci-ep-arrowDown /\u003e\u003c/el-icon\u003e\r\n```\r\n**Note: **\r\n1. i-ep- is a fixed prefix (i is the prefix of the component name, ep stands for Element Plus icon library)\r\n2. Icon names should be converted to upper case and lower case (e.g. Plus → plus, ArrowDown → arrowDown).\r\n\r\n**Avoid wasted performance by importing duplicate icons into the system.\r\n\r\n## Global scss variables\r\nThe system introduces a scss variables file (src/assets/styles/variables.scss) for each component, you can design your own scss variables or methods to be used in any component style.\r\n```css\r\n.container {\r\n  width: vm(100);\r\n}\r\n\r\n// or\r\n\r\n.container {\r\n  font-size: 24px;\r\n  @include responsive('mobile') {\r\n    font-size: 16px;\r\n  }\r\n  @include responsive('tablet') {\r\n    font-size: 18px;\r\n  }\r\n}\r\n```\r\n\r\n## ElementPlus-based FormKit Component\r\nBased on ElementPlus form components for the combination of packaging , through the data flow method of the form data , to facilitate rapid development .\r\n\r\n### Component Attributes\r\n| Parameters | Description | Types | Defaults | \r\n| -------- | :----- | :----: | :----: |\r\n| model-value / v-model | Component Binding Data Source | Object | {}\r\n| config | Form configuration items, detailed config configuration parameters refer to `config Attributes` below | Array | []\r\n| disabled | Disable entire form | Boolean | false  \r\n| labelPosition | Label alignment rules, refer to [ElementPlus Form Attributes](https://element-plus.org/zh-CN/component/form.html#form-api) | String | top  \r\n| labelWidth | labelWidth of the title of the form item (this parameter is only valid when labelPosition is left or right, and will be ignored when labelPosition is top) | Number | 125 \r\n| columns | How many list items are displayed in each row | Number / String | 5  \r\n| size | The size of the components in the form (optional: '' / 'large' / 'default' / 'small') | String | mini \r\n| rows | See [ElementPlus Row API](https://element-plus.org/zh-CN/component/layout.html#row-api) | String | top\r\n\r\n**Note**: `label-width` will be invalidated when `columns` is set to the string `'auto'`, and the result will be `0px` when it is invalidated.\r\n\r\n### Config Attributes\r\n| Parameters | Description | Optional Values | Type | Case\r\n| -------- | :-----:  |  :----: |  :----:  | :----: |\r\n| label |  Form item name  |   -   |  String  |  -\r\n| type | Type of this form item | Configurable, see below for default Config type explain | String | -\r\n| disabled |  Whether the form item is disabled  |   true / false   |  boolean  |  -\r\n| keys |  form item key value (this item should correspond to the field of the form item returned by the backend, so as to facilitate direct interaction between the modified data and the backend)  |   -   |  String  |  -\r\n| span |  Number of columns occupied by the current item grid  |   24   |  number  |   24\r\n| labelWidth |  The length of the label, e.g. '50px'. This value is inherited by form-item as a direct child of Form. auto can be used.  |   -   |  string / number  |   ''\r\n| rules |  Form item validation rules, empty without validation  |   -   |  Array  |   -\r\n| options |  Operational items for components such as select, cascader, etc.  |   -   |  Array  |  ` options: [{ name: 'Open all day', id: 'ALL' }] `\r\n| request |  This form item requires a custom request for remote data loading  |   -   |  Promise  |  ` request: useAxios().get('/default/shop/category-tree') `\r\n| handle |  Processing remote data in conjunction with remote data loading  |   -   |  Function  |  ` handle: (response: any) =\u003e Array.isArray(response) ? response : [] `\r\n| props |  Parameters bound directly to the component  |   -   |  Object  |  `props: { placeholder: 'Pls input shop code', max: 10 }`\r\n| visible |  This form entry displays the fields that need to be associated  |   -   |  Object  |  ` visible: { key: \"showid\", value: 0 } `Indicates that the item is not displayed when the value of the field `showid` in the form is 0.\r\n| events |  Accepting component events  |   -   |  Object  |  -\r\n| hint |  Display prompt text below the current line  |   -   |  string  |  -\r\n\r\n### Config type explain\r\n| Keywords | Description | Remarks\r\n| -------- | :-----: | :----: |\r\n| input | input box | -\r\n| select | drop-down selection box | -\r\n| datePicker | DateTimePicker | [Documentation](https://element-plus.org/zh-CN/component/datetime-picker.html)\r\n| timePicker | timePicker | [Documentation](https://element-plus.org/zh-CN/component/time-picker.html)\r\n| cascader | Cascade Selector | [documentation](https://element-plus.org/zh-CN/component/cascader.html)\r\n| remoteSearchSelect | input with remote search | Use the initialValue field for parameter fallback.\r\n| address | addressSelect | Internal fetchAddressData method requires API modification.\r\n| checkbox | radio | Radio | SingleCheckBox | checkboxes -\r\n| radio | Radio | SingleCheckBox | -\r\n| inputNumber | number input box | -\r\n| upload | File Upload | uploadUrl needs to be modified in utils/upload.class.ts.\r\n| rate | rating | [documentation](https://element-plus.org/zh-CN/component/rate.html)\r\n\r\n### FormKit Slots\r\n| Slot Name | Description | Parameters\r\n| -------- | :-----: | :-----: |\r\n| prepend | Input box front content | -\r\n| append | form item post content | -\r\n| content | form level content | configs =\u003e config item\r\n| ${config.keys} | form item content component level content | row =\u003e current config item, value =\u003e component binding value\r\n\r\n### Exposes\r\n| Name | Description | Parameters | Type\r\n| -------- | :-----: | :-----: | :-----: |\r\n| validate | Validate form items now | openTips =\u003e If or not popup tips for failed validation | Promise\r\n| clearValidate | clearValidate | - | Function\r\n\r\n## SFTP Local File Upload Deployment Automation\r\n\r\nIf your company has not set up CI/CD on the front-end, local file upload automation deployment seems to be a good choice!\r\n**By default, SSH SFTP** is used for local file push, you need to modify `/sftpServeConfig` your server information in `/deploy/sftp.mjs`, and `/sftpServeConfig` your server information in `/sftpServeConfig`.\r\n``\r\n// Publish the development environment\r\npnpm run deploy:development\r\n\r\n// Publish the production environment\r\npnpm run deploy:production\r\n```\r\n\r\nIf the backend restricts uploads to the ftp protocol, you need to install it first\r\n```\r\npnpm add basic-ftp -D\r\n```\r\nThen change the configuration information in the `/deploy/ftp.mjs` file\r\n\r\n```\r\n// Execute\r\nnode deploy/ftp.mjs --mode development\r\n``` // Run node deploy/ftp.mjs --mode development.\r\n\r\n** --mode is the environment variable you need to put at the end of the command**.\r\n\r\nOf course you can also use scp for file uploads, change the `\u003cUser Name\u003e`, `\u003cServer Public IP\u003e`, `\u003cServer directory\u003e` in the scripts.scp command in package.json.\r\n``\r\npnpm run scp\r\n```\r\n\r\n## Mock Data\r\nThe system integrates mockjs, please create mock api specification under `mock` folder.\r\nFor more information about using mockjs, please go to [mockjs official website](https://github.com/nuysoft/Mock/wiki), or refer to the following example\r\n\r\n```javascript\r\nimport { MockMethod } from 'vite-plugin-mock'\r\nimport { v4 as uuidV4 } from 'uuid'\r\n\r\nexport default [\r\n  {\r\n    url: '/api/user/info',\r\n    method: 'get',\r\n    response: () =\u003e {\r\n      return {\r\n        code: 200,\r\n        message: '获取成功',\r\n        data: {\r\n          id: 1,\r\n          role: 28,\r\n          token: uuidV4(),\r\n          name: '张三',\r\n          email: 'zhangsan@example.com'\r\n        }\r\n      }\r\n    }\r\n  },\r\n  {\r\n    url: '/api/login',\r\n    method: 'post',\r\n    response: () =\u003e {\r\n      return {\r\n        code: 200,\r\n        message: '获取成功',\r\n        data: {\r\n          id: 1,\r\n          token: uuidV4(),\r\n          name: '张三',\r\n          email: 'zhangsan@example.com'\r\n        }\r\n      }\r\n    }\r\n  }\r\n] as MockMethod[]\r\n```\r\n\r\n## Contributors\r\n\u003ctable\u003e\r\n    \u003ctr\u003e\r\n        \u003ctd align=\"center\" style=\"word-wrap: break-word; width: 75.0; height: 75.0\"\u003e\r\n            \u003ca target=\"_blank\" href=\"https://github.com/zhizhi-sun\"\u003e\r\n                \u003cimg src=\"./public/Contributors/72124234.jfif\" width=\"50;\"  style=\"border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px\"/\u003e\r\n                \u003cbr /\u003e\r\n                \u003csub style=\"font-size:14px\"\u003e\u003cb\u003ezhizhi-sun\u003c/b\u003e\u003c/sub\u003e\r\n            \u003c/a\u003e\r\n        \u003c/td\u003e\r\n        \u003ctd align=\"center\" style=\"word-wrap: break-word; width: 75.0; height: 75.0\"\u003e\r\n            \u003ca target=\"_blank\" href=\"https://github.com/zhizhi-sun\"\u003e\r\n                \u003cimg src=\"./public/Contributors/TroyLemon.png\" width=\"50;\"  style=\"border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px\"/\u003e\r\n                \u003cbr /\u003e\r\n                \u003csub style=\"font-size:14px\"\u003e\u003cb\u003eTroyLemon\u003c/b\u003e\u003c/sub\u003e\r\n            \u003c/a\u003e\r\n        \u003c/td\u003e\r\n    \u003c/tr\u003e\r\n\u003c/table\u003e\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwxsub%2Fvite-element-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwxsub%2Fvite-element-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwxsub%2Fvite-element-template/lists"}