commit 6f0672150659a0f3ac2d1585993e0e5154a6071d
Author: muzi <444136347@qq.com>
Date: Mon Jun 16 14:42:26 2025 +0800
first commit
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..ba8dc32
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+max_line_length = 120
+tab_width = 2
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..c09b865
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,36 @@
+/* eslint-env node */
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+require('@rushstack/eslint-patch/modern-module-resolution');
+
+module.exports = {
+ root: true,
+ extends: [
+ 'plugin:vue/vue3-recommended',
+ 'eslint:recommended',
+ '@vue/eslint-config-typescript',
+ '@vue/eslint-config-prettier',
+ 'alloy',
+ 'alloy/vue',
+ 'alloy/typescript',
+ './config/unplugin/.eslintrc-auto-import.json',
+ ],
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ ecmaVersion: 'latest',
+ parser: {
+ js: '@babel/eslint-parser',
+ jsx: '@babel/eslint-parser',
+ ts: '@typescript-eslint/parser',
+ tsx: '@typescript-eslint/parser',
+ },
+ },
+ rules: {
+ '@typescript-eslint/prefer-optional-chain': 'off',
+ 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+ 'prettier/prettier': ['error', { endOfLine: 'auto' }],
+ },
+ globals: {
+ defineOptions: 'readonly',
+ },
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f9f947d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+*.js.map
+*.js
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+config/unplugin/*.d.ts
+config/unplugin/.eslintrc*
+
+analyzer.html
\ No newline at end of file
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
new file mode 100644
index 0000000..0401edc
--- /dev/null
+++ b/.prettierrc.cjs
@@ -0,0 +1,41 @@
+module.exports = {
+ // 一行最多 120 字符
+ printWidth: 120,
+ // 使用 2 个空格缩进
+ tabWidth: 2,
+ // 不使用缩进符,而使用空格
+ useTabs: false,
+ // 行尾需要有分号
+ semi: true,
+ // 使用单引号
+ singleQuote: true,
+ // 对象的 key 仅在必要时用引号
+ quoteProps: 'as-needed',
+ // jsx 不使用单引号,而使用双引号
+ jsxSingleQuote: false,
+ // 末尾需要有逗号
+ trailingComma: 'all',
+ // 大括号内的首尾需要空格
+ bracketSpacing: true,
+ // jsx 标签的反尖括号需要换行
+ bracketSameLine: false,
+ // 箭头函数,只有一个参数的时候,也需要括号
+ arrowParens: 'always',
+ // 每个文件格式化的范围是文件的全部内容
+ rangeStart: 0,
+ rangeEnd: Infinity,
+ // 不需要写文件开头的 @prettier
+ requirePragma: false,
+ // 不需要自动在文件开头插入 @prettier
+ insertPragma: false,
+ // 使用默认的折行标准
+ proseWrap: 'preserve',
+ // 根据显示样式决定 html 要不要折行
+ htmlWhitespaceSensitivity: 'css',
+ // vue 文件中的 script 和 style 内不用缩进
+ vueIndentScriptAndStyle: false,
+ // 换行符使用 lf
+ endOfLine: 'lf',
+ // 格式化内嵌代码
+ embeddedLanguageFormatting: 'auto',
+};
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9d094c7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,189 @@
+## template-admin-ts
+
+基于 Arco Design Pro 的中后台管理模板
+
+- vite 4.x
+- vue 3.x
+- vue-router 4.x
+- pinia
+- vueuse
+- axios
+- dayjs
+- lodash
+- arco-design
+- less
+- eslint + prettier
+
+### 项目启动
+
+```shell
+# 新开仓库
+git init
+# 未安装 pnpm
+npm add -g pnpm
+# 项目运行
+pnpm i
+pnpm dev
+```
+
+### 项目部署
+
+- 本地预览
+
+```shell
+# 打包
+pnpm build --mode preview
+# 本地运行
+pnpm preview
+```
+
+- 预发布/测试环境
+
+```shell
+pnpm build --mode staging
+```
+
+- 生产环境
+
+```shell
+pnpm build --mode production
+```
+
+### git 提交规范
+
+项目未安装依赖限制 `git commit`, 但需在**代码评审**阶段检查
+
+##### 示例:
+
+```git
+✨ feat: 新功能
+```
+
+```git
+🐛 fix: 修复bug
+```
+
+更多功能:[提交规范](https://www.conventionalcommits.org/zh-hans/v1.0.0/),[`gitmoji`](https://gitmoji.dev/)
+
+##### 插件安装
+
+- `vscode`
+
+ - Commit Message Editor
+ - Gitmoji
+
+- `webstorm`
+
+ - Git Commit Template
+ - Gitmoji Plus: Commit Button
+
+### `vite` 插件
+
+#### `unocss`
+
+- [配置](https://github.com/unocss/unocss)
+- [文档](https://uno.antfu.me/)
+
+示例:
+
+```html
+
+```
+
+#### 自动导入 `api`
+
+- [配置](https://github.com/antfu/unplugin-auto-import)
+
+已安装 `vue`, `vue-router`, `pinia`, `@vueuse/core`, `dayjs`, `lodash-es`
+
+> `lodash-es` 为部分安装;若需更多功能函数,可新增在 `['cloneDeep']`, 以数组形式。
+
+示例:
+
+```vue
+
+```
+
+#### 自动导入组件
+
+##### `src/components` 目录下的组件
+
+| 模式 | 描述 |
+| ---------------- | --------------------- |
+| `Comp.vue` | - |
+| `Comp/Index.vue` | - |
+| `Comp/Comp.vue` | - |
+| `Comp/Index.js` | 支持默认导出\命名导出 |
+
+> 默认导出: `Comp.vue`, `Comp/Index.vue`, `Comp/Comp.vue`, `Comp/Index.js`
+
+```vue
+
+
+
+```
+
+> 命名导出: `Comp/Index.js`
+
+```js
+// Comp/Index.js
+export { CompA, CompB, CompC };
+```
+
+```vue
+
+
+
+
+
+```
+
+##### `src/views/**/components` 业务组件
+
+页面内的业务组件直接使用
+
+`src/views/pageA/components/Comp.vue`
+
+```vue
+
+
+
+```
+
+##### `src/components/_base` 基础组件
+
+> 需在 `index.ts` 中命名导出
+
+```ts
+export { default as Comp } from './comp/index.vue';
+```
+
+```vue
+
+
+
+```
+
+#### 自动导入 `svg` 图标
+
+- `src/assets/icon.svg`
+- `src/assets/iconA.svg`
+
+```vue
+
+
+
+
+```
+
+### `ui` 框架 [`ArcoVue`](https://arco.design/vue/docs/start)
+
+- 自动导入组件,无需再次导入
+- 图标库已配置,同组件使用
diff --git a/config/index.ts b/config/index.ts
new file mode 100644
index 0000000..58a9509
--- /dev/null
+++ b/config/index.ts
@@ -0,0 +1,19 @@
+import { configUnocss } from './plugins';
+import { configAutoImport, configComponents, configIcons } from './unplugin';
+import viteCompression from 'vite-plugin-compression';
+import progress from 'vite-plugin-progress';
+import defineOptions from 'unplugin-vue-define-options/vite';
+
+export * from './utils';
+
+export const pluginsConfig = [
+ configUnocss(),
+ configAutoImport(),
+ configComponents(),
+ configIcons(),
+ viteCompression(),
+ progress(),
+ defineOptions({
+ include: [/\.vue$/, /\.vue\?vue/],
+ }),
+];
diff --git a/config/plugins/index.ts b/config/plugins/index.ts
new file mode 100644
index 0000000..766f747
--- /dev/null
+++ b/config/plugins/index.ts
@@ -0,0 +1 @@
+export * from './unocss';
diff --git a/config/plugins/unocss.ts b/config/plugins/unocss.ts
new file mode 100644
index 0000000..8327a11
--- /dev/null
+++ b/config/plugins/unocss.ts
@@ -0,0 +1,61 @@
+/*
+ * @Author: 田鑫
+ * @Date: 2023-03-05 18:14:16
+ * @LastEditors: 田鑫
+ * @LastEditTime: 2023-03-05 19:23:48
+ * @Description:
+ */
+import Unocss from 'unocss/vite';
+import {
+ presetUno,
+ presetIcons,
+ presetAttributify,
+ transformerDirectives,
+ transformerCompileClass,
+ transformerVariantGroup,
+ transformerAttributifyJsx,
+} from 'unocss';
+import presetRemToPx from '@unocss/preset-rem-to-px';
+
+export const configUnocss = () =>
+ Unocss({
+ presets: [presetUno(), presetIcons(), presetAttributify(), presetRemToPx()],
+ transformers: [
+ transformerDirectives(),
+ transformerCompileClass(),
+ transformerVariantGroup(),
+ transformerAttributifyJsx(),
+ ],
+ shortcuts: [
+ // 垂直水平居中
+ ['flex-center', 'flex justify-center items-center'],
+ ],
+ rules: [
+ [/^mgl-(\d+)$/, ([, d]) => ({ 'margin-left': `${d}px !important` })],
+ [/^mgr-(\d+)$/, ([, d]) => ({ 'margin-right': `${d}px !important` })],
+ [/^mgt-(\d+)$/, ([, d]) => ({ 'margin-top': `${d}px !important` })],
+ [/^mgb-(\d+)$/, ([, d]) => ({ 'margin-bottom': `${d}px !important` })],
+ [/^pd-(\d+)$/, ([, d]) => ({ padding: `${d}px !important` })],
+ [/^flex-(\d+)$/, ([, d]) => ({ flex: `${d} !important` })],
+ [/^w(\d+)$/, ([, d]) => ({ width: `${d}% !important` })],
+ [/^w-(\d+)$/, ([, d]) => ({ width: `${d}px !important` })],
+ [/^h-(\d+)$/, ([, d]) => ({ height: `${d}px !important` })],
+ [/^ft-(\d+)$/, ([, d]) => ({ 'font-size': `${d}px !important` })],
+ [
+ 'box-container',
+ {
+ 'border-radius': '2px',
+ padding: '20px',
+ margin: '8px',
+ 'background-color': '#fff',
+ },
+ ],
+ [
+ 'justify-between',
+ {
+ 'justify-content': 'space-between',
+ },
+ ],
+ ['align-center', { 'text-align': 'center' }],
+ ],
+ });
diff --git a/config/unplugin/auto-import.ts b/config/unplugin/auto-import.ts
new file mode 100644
index 0000000..b4ad073
--- /dev/null
+++ b/config/unplugin/auto-import.ts
@@ -0,0 +1,41 @@
+/**
+ * 自动引入API
+ * */
+import AutoImport from 'unplugin-auto-import/vite';
+
+import { ArcoResolver } from 'unplugin-vue-components/resolvers';
+import IconsResolver from 'unplugin-icons/resolver';
+
+import { layoutsResolver } from '../utils';
+
+export function configAutoImport() {
+ return AutoImport({
+ imports: [
+ 'vue',
+ 'vue-router',
+ 'pinia',
+ '@vueuse/core',
+ {
+ dayjs: [['default', 'dayjs']],
+ 'lodash-es': ['cloneDeep', 'omit', 'pick'],
+ '@/hooks': ['useModal'],
+ },
+ ],
+ resolvers: [
+ ArcoResolver({
+ resolveIcons: {
+ enable: true,
+ },
+ }),
+ IconsResolver({
+ enabledCollections: [],
+ }),
+ layoutsResolver(),
+ ],
+ eslintrc: {
+ enabled: true,
+ filepath: './config/unplugin/.eslintrc-auto-import.json',
+ },
+ dts: './config/unplugin/auto-imports.d.ts',
+ });
+}
diff --git a/config/unplugin/component.ts b/config/unplugin/component.ts
new file mode 100644
index 0000000..9840799
--- /dev/null
+++ b/config/unplugin/component.ts
@@ -0,0 +1,57 @@
+/**
+ * 自动引入组件
+ * */
+import { kebabCase } from 'unplugin-vue-components';
+import Components from 'unplugin-vue-components/vite';
+
+import { ArcoResolver } from 'unplugin-vue-components/resolvers';
+import IconsResolver from 'unplugin-icons/resolver';
+
+import { getSep, getPath, setResolve, layoutsResolver } from '../utils';
+
+export function configComponents() {
+ return Components({
+ dirs: ['src/components'],
+ extensions: ['vue'],
+ resolvers: [
+ ArcoResolver({
+ resolveIcons: {
+ enable: true,
+ },
+ sideEffect: true,
+ }),
+ IconsResolver({
+ prefix: false,
+ customCollections: ['i'],
+ enabledCollections: [],
+ }),
+ layoutsResolver(),
+ {
+ type: 'component',
+ resolve: (name) => {
+ const [prefix, folder] = kebabCase(name).split('-');
+
+ // 命名导出组件
+ if (prefix === 'eos') {
+ return {
+ name: name.slice(3),
+ from: getPath(`${getSep('src')}/components`, { folder }),
+ };
+ }
+ // 默认导出组件
+ if (prefix === 'eo') {
+ return setResolve(name);
+ }
+ if (prefix === 'base') {
+ return {
+ name: name.slice(4),
+ from: `${getSep('src')}/components/_base`,
+ };
+ }
+ },
+ },
+ ],
+ directoryAsNamespace: true,
+ dts: './config/unplugin/components.d.ts',
+ });
+}
diff --git a/config/unplugin/icons.ts b/config/unplugin/icons.ts
new file mode 100644
index 0000000..2afaffa
--- /dev/null
+++ b/config/unplugin/icons.ts
@@ -0,0 +1,14 @@
+/**
+ * 自动引入 svg 图标
+ * */
+import Icons from 'unplugin-icons/vite';
+import { FileSystemIconLoader } from 'unplugin-icons/loaders';
+
+export function configIcons() {
+ return Icons({
+ compiler: 'vue3',
+ customCollections: {
+ i: FileSystemIconLoader('./src/assets', (svg) => svg.replace(/^