feat: 🚀 添加新的表格封装

This commit is contained in:
bunny 2024-09-04 14:59:06 +08:00
parent b2fed0a09b
commit 71ba1bb264
35 changed files with 1937 additions and 2389 deletions

View File

@ -1,9 +1,39 @@
// @ts-check // @see: https://www.prettier.cn
/** @type {import("prettier").Config} */
export default { export default {
bracketSpacing: true, // 超过最大值换行
singleQuote: false, printWidth: 200,
arrowParens: "avoid", // 缩进字节数
trailingComma: "none" tabWidth: 1,
// 使用制表符而不是空格缩进行
useTabs: true,
// 结尾不用分号(true有false没有)
semi: true,
// 使用单引号(true单引号false双引号)
singleQuote: true,
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
quoteProps: 'as-needed',
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
bracketSpacing: true,
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>"默认none
trailingComma: 'all',
// 在JSX中使用单引号而不是双引号
jsxSingleQuote: true,
// (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid省略括号 ,always不省略括号
arrowParens: 'avoid',
// 如果文件顶部已经有一个 doclock这个选项将新建一行注释并打上@format标记。
insertPragma: false,
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 默认值。因为使用了一些折行敏感型的渲染器如GitHub comment而按照markdown文本样式进行折行
proseWrap: 'preserve',
// 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值 "strict" - 空格被认为是敏感的 "ignore" - 空格被认为是不敏感的
htmlWhitespaceSensitivity: 'css',
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
endOfLine: 'auto',
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeStart: 0,
rangeEnd: Infinity,
vueIndentScriptAndStyle: false, // Vue文件脚本和样式标签缩进
}; };

View File

@ -1,20 +0,0 @@
FROM node:20-alpine as build-stage
WORKDIR /app
RUN corepack enable
RUN corepack prepare pnpm@latest --activate
RUN npm config set registry https://registry.npmmirror.com
COPY .npmrc package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,4 +1,4 @@
import { Plugin as importToCDN } from "vite-plugin-cdn-import"; import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
/** /**
* @description `cdn`使cdn模式 .env.production VITE_CDN true * @description `cdn`使cdn模式 .env.production VITE_CDN true
@ -6,55 +6,50 @@ import { Plugin as importToCDN } from "vite-plugin-cdn-import";
* 使jscss文件cdn * 使jscss文件cdn
*/ */
export const cdn = importToCDN({ export const cdn = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path当然也可写完整路径会替换prodUrl //prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path当然也可写完整路径会替换prodUrl
prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}", prodUrl: 'https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}',
modules: [ modules: [
{ {
name: "vue", name: 'vue',
var: "Vue", var: 'Vue',
path: "vue.global.prod.min.js" path: 'vue.global.prod.min.js',
}, },
{ {
name: "vue-router", name: 'vue-router',
var: "VueRouter", var: 'VueRouter',
path: "vue-router.global.min.js" path: 'vue-router.global.min.js',
}, },
{ // 项目中没有直接安装vue-demi但是pinia用到了所以需要在引入pinia前引入vue-demihttps://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77
name: "vue-i18n", {
var: "VueI18n", name: 'vue-demi',
path: "vue-i18n.runtime.global.prod.min.js" var: 'VueDemi',
}, path: 'index.iife.min.js',
// 项目中没有直接安装vue-demi但是pinia用到了所以需要在引入pinia前引入vue-demihttps://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77 },
{ {
name: "vue-demi", name: 'pinia',
var: "VueDemi", var: 'Pinia',
path: "index.iife.min.js" path: 'pinia.iife.min.js',
}, },
{ {
name: "pinia", name: 'element-plus',
var: "Pinia", var: 'ElementPlus',
path: "pinia.iife.min.js" path: 'index.full.min.js',
}, css: 'index.min.css',
{ },
name: "element-plus", {
var: "ElementPlus", name: 'axios',
path: "index.full.min.js", var: 'axios',
css: "index.min.css" path: 'axios.min.js',
}, },
{ {
name: "axios", name: 'dayjs',
var: "axios", var: 'dayjs',
path: "axios.min.js" path: 'dayjs.min.js',
}, },
{ {
name: "dayjs", name: 'echarts',
var: "dayjs", var: 'echarts',
path: "dayjs.min.js" path: 'echarts.min.js',
}, },
{ ],
name: "echarts",
var: "echarts",
path: "echarts.min.js"
}
]
}); });

View File

@ -4,31 +4,12 @@
* include里vite 使 node_modules/.vite * include里vite 使 node_modules/.vite
* 使 src/main.ts include vite node_modules/.vite * 使 src/main.ts include vite node_modules/.vite
*/ */
const include = [ const include = ['qs', 'mitt', 'dayjs', 'axios', 'pinia', 'vue-types', 'js-cookie', 'vue-tippy', 'pinyin-pro', 'sortablejs', '@vueuse/core', '@pureadmin/utils', 'responsive-storage'];
"qs",
"mitt",
"dayjs",
"axios",
"pinia",
"vue-i18n",
"vue-types",
"js-cookie",
"vue-tippy",
"pinyin-pro",
"sortablejs",
"@vueuse/core",
"@pureadmin/utils",
"responsive-storage"
];
/** /**
* *
* `@iconify-icons/` `exclude` 使 * `@iconify-icons/` `exclude` 使
*/ */
const exclude = [ const exclude = ['@iconify-icons/ep', '@iconify-icons/ri', '@pureadmin/theme/dist/browser-utils'];
"@iconify-icons/ep",
"@iconify-icons/ri",
"@pureadmin/theme/dist/browser-utils"
];
export { include, exclude }; export { include, exclude };

View File

@ -1,66 +1,54 @@
import { cdn } from "./cdn"; import { cdn } from './cdn';
import vue from "@vitejs/plugin-vue"; import vue from '@vitejs/plugin-vue';
import { pathResolve } from "./utils"; import { viteBuildInfo } from './info';
import { viteBuildInfo } from "./info"; import svgLoader from 'vite-svg-loader';
import svgLoader from "vite-svg-loader"; import type { PluginOption } from 'vite';
import type { PluginOption } from "vite"; import vueJsx from '@vitejs/plugin-vue-jsx';
import vueJsx from "@vitejs/plugin-vue-jsx"; import Inspector from 'vite-plugin-vue-inspector';
import Inspector from "vite-plugin-vue-inspector"; import { configCompressPlugin } from './compress';
import { configCompressPlugin } from "./compress"; import removeNoMatch from 'vite-plugin-router-warn';
import removeNoMatch from "vite-plugin-router-warn"; import { visualizer } from 'rollup-plugin-visualizer';
import { visualizer } from "rollup-plugin-visualizer"; import removeConsole from 'vite-plugin-remove-console';
import removeConsole from "vite-plugin-remove-console"; import { themePreprocessorPlugin } from '@pureadmin/theme';
import { themePreprocessorPlugin } from "@pureadmin/theme"; import { genScssMultipleScopeVars } from '../src/layout/theme';
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; import { vitePluginFakeServer } from 'vite-plugin-fake-server';
import { genScssMultipleScopeVars } from "../src/layout/theme";
import { vitePluginFakeServer } from "vite-plugin-fake-server";
export function getPluginsList( export function getPluginsList(VITE_CDN: boolean, VITE_COMPRESSION: ViteCompression, VITE_PORT: number): PluginOption[] {
VITE_CDN: boolean, const lifecycle = process.env.npm_lifecycle_event;
VITE_COMPRESSION: ViteCompression, return [
VITE_PORT: number vue(),
): PluginOption[] { // jsx、tsx语法支持
const lifecycle = process.env.npm_lifecycle_event; vueJsx(),
return [ // 按下Command(⌘)+Shift(⇧)然后点击页面元素会自动打开本地IDE并跳转到对应的代码位置
vue(), Inspector(),
// jsx、tsx语法支持 viteBuildInfo(VITE_PORT),
vueJsx(), /**
VueI18nPlugin({ * vue-router动态路由警告No match found for location with path
jitCompilation: false, * https://github.com/vuejs/router/issues/521 和 https://github.com/vuejs/router/issues/359
include: [pathResolve("../locales/**")] * vite-plugin-router-warn只在开发环境下启用vue-router文件并且只在服务启动或重启时运行一次
}), */
// 按下Command(⌘)+Shift(⇧)然后点击页面元素会自动打开本地IDE并跳转到对应的代码位置 removeNoMatch(),
Inspector(), // mock支持
viteBuildInfo(VITE_PORT), vitePluginFakeServer({
/** logger: false,
* vue-router动态路由警告No match found for location with path include: 'mock',
* https://github.com/vuejs/router/issues/521 和 https://github.com/vuejs/router/issues/359 infixName: false,
* vite-plugin-router-warn只在开发环境下启用vue-router文件并且只在服务启动或重启时运行一次 enableProd: true,
*/ }),
removeNoMatch(), // 自定义主题
// mock支持 themePreprocessorPlugin({
vitePluginFakeServer({ scss: {
logger: false, multipleScopeVars: genScssMultipleScopeVars(),
include: "mock", extract: true,
infixName: false, },
enableProd: true }),
}), // svg组件化支持
// 自定义主题 svgLoader(),
themePreprocessorPlugin({ VITE_CDN ? cdn : null,
scss: { configCompressPlugin(VITE_COMPRESSION),
multipleScopeVars: genScssMultipleScopeVars(), // 线上环境删除console
extract: true removeConsole({ external: ['src/assets/iconfont/iconfont.js'] }),
} // 打包分析
}), lifecycle === 'report' ? visualizer({ open: true, brotliSize: true, filename: 'report.html' }) : (null as any),
// svg组件化支持 ];
svgLoader(),
VITE_CDN ? cdn : null,
configCompressPlugin(VITE_COMPRESSION),
// 线上环境删除console
removeConsole({ external: ["src/assets/iconfont/iconfont.js"] }),
// 打包分析
lifecycle === "report"
? visualizer({ open: true, brotliSize: true, filename: "report.html" })
: (null as any)
];
} }

View File

@ -1,181 +1,176 @@
import js from "@eslint/js"; import js from '@eslint/js';
import pluginVue from "eslint-plugin-vue"; import pluginTypeScript from '@typescript-eslint/eslint-plugin';
import * as parserVue from "vue-eslint-parser"; import * as parserTypeScript from '@typescript-eslint/parser';
import configPrettier from "eslint-config-prettier"; import configPrettier from 'eslint-config-prettier';
import pluginPrettier from "eslint-plugin-prettier"; import { defineFlatConfig } from 'eslint-define-config';
import { defineFlatConfig } from "eslint-define-config"; import pluginPrettier from 'eslint-plugin-prettier';
import * as parserTypeScript from "@typescript-eslint/parser"; import pluginVue from 'eslint-plugin-vue';
import pluginTypeScript from "@typescript-eslint/eslint-plugin"; import * as parserVue from 'vue-eslint-parser';
export default defineFlatConfig([ export default defineFlatConfig([
{ {
...js.configs.recommended, ...js.configs.recommended,
ignores: [ ignores: ['**/.*', 'dist/*', '*.d.ts', 'public/*', 'src/assets/**', 'src/**/iconfont/**'],
"**/.*", languageOptions: {
"dist/*", globals: {
"*.d.ts", // index.d.ts
"public/*", RefType: 'readonly',
"src/assets/**", EmitType: 'readonly',
"src/**/iconfont/**" TargetContext: 'readonly',
], ComponentRef: 'readonly',
languageOptions: { ElRef: 'readonly',
globals: { ForDataType: 'readonly',
// index.d.ts AnyFunction: 'readonly',
RefType: "readonly", PropType: 'readonly',
EmitType: "readonly", Writable: 'readonly',
TargetContext: "readonly", Nullable: 'readonly',
ComponentRef: "readonly", NonNullable: 'readonly',
ElRef: "readonly", Recordable: 'readonly',
ForDataType: "readonly", ReadonlyRecordable: 'readonly',
AnyFunction: "readonly", Indexable: 'readonly',
PropType: "readonly", DeepPartial: 'readonly',
Writable: "readonly", Without: 'readonly',
Nullable: "readonly", Exclusive: 'readonly',
NonNullable: "readonly", TimeoutHandle: 'readonly',
Recordable: "readonly", IntervalHandle: 'readonly',
ReadonlyRecordable: "readonly", Effect: 'readonly',
Indexable: "readonly", ChangeEvent: 'readonly',
DeepPartial: "readonly", WheelEvent: 'readonly',
Without: "readonly", ImportMetaEnv: 'readonly',
Exclusive: "readonly", Fn: 'readonly',
TimeoutHandle: "readonly", PromiseFn: 'readonly',
IntervalHandle: "readonly", ComponentElRef: 'readonly',
Effect: "readonly", parseInt: 'readonly',
ChangeEvent: "readonly", parseFloat: 'readonly',
WheelEvent: "readonly", },
ImportMetaEnv: "readonly", },
Fn: "readonly", plugins: {
PromiseFn: "readonly", prettier: pluginPrettier,
ComponentElRef: "readonly", },
parseInt: "readonly", rules: {
parseFloat: "readonly" ...configPrettier.rules,
} ...pluginPrettier.configs.recommended.rules,
}, 'no-debugger': 'off',
plugins: {
prettier: pluginPrettier 'no-unused-vars': [
}, 'error',
rules: { {
...configPrettier.rules, argsIgnorePattern: '^_',
...pluginPrettier.configs.recommended.rules, varsIgnorePattern: '^_',
"no-debugger": "off", },
"no-unused-vars": [ ],
"error", 'prettier/prettier': [
{ 'error',
argsIgnorePattern: "^_", {
varsIgnorePattern: "^_" endOfLine: 'auto',
} },
], ],
"prettier/prettier": [ },
"error", },
{ {
endOfLine: "auto" files: ['**/*.?([cm])ts', '**/*.?([cm])tsx'],
} languageOptions: {
] parser: parserTypeScript,
} parserOptions: {
}, sourceType: 'module',
{ },
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], },
languageOptions: { plugins: {
parser: parserTypeScript, '@typescript-eslint': pluginTypeScript,
parserOptions: { },
sourceType: "module" rules: {
} ...pluginTypeScript.configs.strict.rules,
}, '@typescript-eslint/ban-types': 'off',
plugins: { '@typescript-eslint/no-redeclare': 'error',
"@typescript-eslint": pluginTypeScript '@typescript-eslint/ban-ts-comment': 'off',
}, '@typescript-eslint/no-explicit-any': 'off',
rules: { '@typescript-eslint/prefer-as-const': 'warn',
...pluginTypeScript.configs.strict.rules, '@typescript-eslint/no-empty-function': 'off',
"@typescript-eslint/ban-types": "off", '@typescript-eslint/no-non-null-assertion': 'off',
"@typescript-eslint/no-redeclare": "error", '@typescript-eslint/no-import-type-side-effects': 'error',
"@typescript-eslint/ban-ts-comment": "off", '@typescript-eslint/explicit-module-boundary-types': 'off',
"@typescript-eslint/no-explicit-any": "off", '@typescript-eslint/consistent-type-imports': [
"@typescript-eslint/prefer-as-const": "warn", 'error',
"@typescript-eslint/no-empty-function": "off", {
"@typescript-eslint/no-non-null-assertion": "off", disallowTypeAnnotations: false,
"@typescript-eslint/no-import-type-side-effects": "error", fixStyle: 'inline-type-imports',
"@typescript-eslint/explicit-module-boundary-types": "off", },
"@typescript-eslint/consistent-type-imports": [ ],
"error", '@typescript-eslint/prefer-literal-enum-member': ['error', { allowBitwiseExpressions: true }],
{ disallowTypeAnnotations: false, fixStyle: "inline-type-imports" } '@typescript-eslint/no-unused-vars': [
], 'error',
"@typescript-eslint/prefer-literal-enum-member": [ {
"error", argsIgnorePattern: '^_',
{ allowBitwiseExpressions: true } varsIgnorePattern: '^_',
], },
"@typescript-eslint/no-unused-vars": [ ],
"error", },
{ },
argsIgnorePattern: "^_", {
varsIgnorePattern: "^_" files: ['**/*.d.ts'],
} rules: {
] 'eslint-comments/no-unlimited-disable': 'off',
} 'import/no-duplicates': 'off',
}, 'unused-imports/no-unused-vars': 'off',
{ },
files: ["**/*.d.ts"], },
rules: { {
"eslint-comments/no-unlimited-disable": "off", files: ['**/*.?([cm])js'],
"import/no-duplicates": "off", rules: {
"unused-imports/no-unused-vars": "off" '@typescript-eslint/no-require-imports': 'off',
} '@typescript-eslint/no-var-requires': 'off',
}, },
{ },
files: ["**/*.?([cm])js"], {
rules: { files: ['**/*.vue'],
"@typescript-eslint/no-require-imports": "off", languageOptions: {
"@typescript-eslint/no-var-requires": "off" globals: {
} $: 'readonly',
}, $$: 'readonly',
{ $computed: 'readonly',
files: ["**/*.vue"], $customRef: 'readonly',
languageOptions: { $ref: 'readonly',
globals: { $shallowRef: 'readonly',
$: "readonly", $toRef: 'readonly',
$$: "readonly", },
$computed: "readonly", parser: parserVue,
$customRef: "readonly", parserOptions: {
$ref: "readonly", ecmaFeatures: {
$shallowRef: "readonly", jsx: true,
$toRef: "readonly" },
}, extraFileExtensions: ['.vue'],
parser: parserVue, parser: '@typescript-eslint/parser',
parserOptions: { sourceType: 'module',
ecmaFeatures: { },
jsx: true },
}, plugins: {
extraFileExtensions: [".vue"], vue: pluginVue,
parser: "@typescript-eslint/parser", },
sourceType: "module" processor: pluginVue.processors['.vue'],
} rules: {
}, ...pluginVue.configs.base.rules,
plugins: { ...pluginVue.configs['vue3-essential'].rules,
vue: pluginVue ...pluginVue.configs['vue3-recommended'].rules,
}, 'no-undef': 'off',
processor: pluginVue.processors[".vue"], 'no-unused-vars': 'off',
rules: { 'vue/no-v-html': 'off',
...pluginVue.configs.base.rules, 'vue/require-default-prop': 'off',
...pluginVue.configs["vue3-essential"].rules, 'vue/require-explicit-emits': 'off',
...pluginVue.configs["vue3-recommended"].rules, 'vue/no-useless-template-attributes': 'off',
"no-undef": "off", 'vue/multi-word-component-names': 'off',
"no-unused-vars": "off", 'vue/no-setup-props-reactivity-loss': 'off',
"vue/no-v-html": "off", 'vue/html-self-closing': [
"vue/require-default-prop": "off", 'error',
"vue/require-explicit-emits": "off", {
"vue/multi-word-component-names": "off", html: {
"vue/no-setup-props-reactivity-loss": "off", void: 'always',
"vue/html-self-closing": [ normal: 'always',
"error", component: 'always',
{ },
html: { svg: 'always',
void: "always", math: 'always',
normal: "always", },
component: "always" ],
}, },
svg: "always", },
math: "always"
}
]
}
}
]); ]);

View File

@ -1,10 +1,9 @@
// @ts-check
export default { export default {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"], '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [ '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
"prettier --write--parser json" 'package.json': ['prettier --write'],
], '*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'],
"package.json": ["prettier --write"], '*.{scss,less,styl,html}': ['stylelint --fix', 'prettier --write'],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"], '*.md': ['prettier --write'],
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
"*.md": ["prettier --write"]
}; };

View File

@ -1,210 +1,208 @@
{ {
"name": "bunny-admin-element", "name": "bunny-admin-element",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"keywords": [ "keywords": [
"bunny-admin-element", "bunny-admin-element",
"bunny-cli", "bunny-cli",
"element-plus", "element-plus",
"tailwindcss", "tailwindcss",
"typescript", "typescript",
"pinia", "pinia",
"vue3", "vue3",
"vite", "vite",
"esm" "esm"
], ],
"homepage": "https://gitee.com/BunnyBoss/bunny-admin-element-thin", "homepage": "https://gitee.com/BunnyBoss/bunny-admin-element-thin",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://gitee.com/BunnyBoss/bunny-admin-element-thin" "url": "https://gitee.com/BunnyBoss/bunny-admin-element-thin"
}, },
"bugs": { "bugs": {
"url": "https://gitee.com/BunnyBoss/bunny-admin-element-thin/issues" "url": "https://gitee.com/BunnyBoss/bunny-admin-element-thin/issues"
}, },
"license": "MIT", "license": "MIT",
"author": { "author": {
"name": "Bunny0212", "name": "Bunny0212",
"email": "1319900154@qq.com", "email": "1319900154@qq.com",
"url": "https://github.com/xiaoxian521" "url": "https://github.com/xiaoxian521"
}, },
"scripts": { "scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite", "dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
"serve": "pnpm vite", "serve": "pnpm vite",
"start": "vite", "start": "vite",
"build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build && generate-version-file", "build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build && generate-version-file",
"build:staging": "rimraf dist && vite build --mode staging", "build:staging": "rimraf dist && vite build --mode staging",
"report": "rimraf dist && vite build", "report": "rimraf dist && vite build",
"preview": "vite preview", "preview": "vite preview",
"preview:build": "pnpm build && vite preview", "preview:build": "pnpm build && vite preview",
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck", "typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
"svgo": "svgo -f . -r", "svgo": "svgo -f . -r",
"clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install", "clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix", "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"", "lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint", "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install", "prepare": "husky install",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"commit": "git pull && git add -A && git-cz && git push" "commit": "git pull && git add -A && git-cz && git push"
}, },
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@howdyjs/mouse-menu": "^2.1.3", "@howdyjs/mouse-menu": "^2.1.3",
"@infectoone/vue-ganttastic": "^2.3.2", "@infectoone/vue-ganttastic": "^2.3.2",
"@logicflow/core": "^1.2.27", "@logicflow/core": "^1.2.27",
"@logicflow/extension": "^1.2.27", "@logicflow/extension": "^1.2.27",
"@pureadmin/descriptions": "^1.2.1", "@pureadmin/descriptions": "^1.2.1",
"@pureadmin/table": "^3.1.2", "@pureadmin/table": "^3.1.2",
"@pureadmin/utils": "^2.4.7", "@pureadmin/utils": "^2.4.7",
"@vue-flow/background": "^1.3.0", "@vue-flow/background": "^1.3.0",
"@vue-flow/core": "^1.33.6", "@vue-flow/core": "^1.33.6",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"@vueuse/motion": "^2.1.0", "@vueuse/motion": "^2.1.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.6.8", "axios": "^1.6.8",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"cropperjs": "^1.6.2", "commitlint": "^19.4.1",
"dayjs": "^1.11.11", "cropperjs": "^1.6.2",
"echarts": "^5.5.0", "dayjs": "^1.11.11",
"el-table-infinite-scroll": "^3.0.3", "echarts": "^5.5.0",
"element-plus": "2.7.1", "el-table-infinite-scroll": "^3.0.3",
"intro.js": "^7.2.0", "element-plus": "2.7.1",
"js-cookie": "^3.0.5", "intro.js": "^7.2.0",
"jsbarcode": "^3.11.6", "js-cookie": "^3.0.5",
"localforage": "^1.10.0", "jsbarcode": "^3.11.6",
"mint-filter": "^4.0.3", "localforage": "^1.10.0",
"mitt": "^3.0.1", "mint-filter": "^4.0.3",
"mqtt": "4.3.7", "mitt": "^3.0.1",
"nprogress": "^0.2.0", "mqtt": "4.3.7",
"path": "^0.12.7", "nprogress": "^0.2.0",
"pinia": "^2.1.7", "path": "^0.12.7",
"pinia-plugin-persistedstate": "^3.2.1", "pinia": "^2.1.7",
"pinyin-pro": "^3.20.4", "pinia-plugin-persistedstate": "^3.2.1",
"plus-pro-components": "^0.1.1", "pinyin-pro": "^3.20.4",
"qrcode": "^1.5.3", "plus-pro-components": "^0.1.1",
"qs": "^6.12.1", "qrcode": "^1.5.3",
"responsive-storage": "^2.2.0", "qs": "^6.12.1",
"sortablejs": "^1.15.2", "responsive-storage": "^2.2.0",
"swiper": "^11.1.1", "sortablejs": "^1.15.2",
"terser": "^5.31.0", "swiper": "^11.1.1",
"typeit": "^8.8.3", "terser": "^5.31.0",
"v-contextmenu": "^3.2.0", "typeit": "^8.8.3",
"v3-infinite-loading": "^1.3.1", "v-contextmenu": "^3.2.0",
"version-rocket": "^1.7.1", "v3-infinite-loading": "^1.3.1",
"vite-plugin-vue-inspector": "^5.1.3", "version-rocket": "^1.7.1",
"vue": "^3.4.27", "vite-plugin-vue-inspector": "^5.1.3",
"vue-i18n": "^9.13.1", "vue": "^3.4.27",
"vue-json-pretty": "^2.4.0", "vue-json-pretty": "^2.4.0",
"vue-pdf-embed": "^2.0.3", "vue-pdf-embed": "^2.0.3",
"vue-router": "^4.3.2", "vue-router": "^4.3.2",
"vue-tippy": "^6.4.1", "vue-tippy": "^6.4.1",
"vue-types": "^5.1.2", "vue-types": "^5.1.2",
"vue-virtual-scroller": "2.0.0-beta.8", "vue-virtual-scroller": "2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.4.3", "vue-waterfall-plugin-next": "^2.4.3",
"vue3-danmaku": "^1.6.0", "vue3-danmaku": "^1.6.0",
"vue3-puzzle-vcode": "^1.1.7", "vue3-puzzle-vcode": "^1.1.7",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vxe-table": "^4.6.9", "vxe-table": "^4.6.18",
"wavesurfer.js": "^7.7.13", "wavesurfer.js": "^7.7.13",
"xgplayer": "^3.0.17", "xgplayer": "^3.0.17",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.3.0", "@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2", "@commitlint/config-conventional": "^19.2.2",
"@commitlint/types": "^19.0.3", "@commitlint/types": "^19.0.3",
"@eslint/js": "^9.2.0", "@eslint/js": "^9.2.0",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@iconify-icons/ep": "^1.2.12", "@iconify-icons/ep": "^1.2.12",
"@iconify-icons/ri": "^1.2.10", "@iconify-icons/ri": "^1.2.10",
"@iconify/vue": "^4.1.2", "@iconify/vue": "^4.1.2",
"@intlify/unplugin-vue-i18n": "^4.0.0", "@pureadmin/theme": "^3.2.0",
"@pureadmin/theme": "^3.2.0", "@types/dagre": "^0.7.52",
"@types/dagre": "^0.7.52", "@types/gradient-string": "^1.1.6",
"@types/gradient-string": "^1.1.6", "@types/intro.js": "^5.1.5",
"@types/intro.js": "^5.1.5", "@types/js-cookie": "^3.0.6",
"@types/js-cookie": "^3.0.6", "@types/node": "^20.12.11",
"@types/node": "^20.12.11", "@types/nprogress": "^0.2.3",
"@types/nprogress": "^0.2.3", "@types/qrcode": "^1.5.5",
"@types/qrcode": "^1.5.5", "@types/qs": "^6.9.15",
"@types/qs": "^6.9.15", "@types/sortablejs": "^1.15.8",
"@types/sortablejs": "^1.15.8", "@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/parser": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^3.1.0", "autoprefixer": "^10.4.19",
"autoprefixer": "^10.4.19", "boxen": "^7.1.1",
"boxen": "^7.1.1", "commitizen": "^4.2.4",
"commitizen": "^4.2.4", "cssnano": "^7.0.1",
"commitlint": "^17.0.1", "cz-git": "^1.3.2",
"cssnano": "^7.0.1", "dagre": "^0.8.5",
"cz-git": "^1.3.2", "eslint": "^9.2.0",
"dagre": "^0.8.5", "eslint-config-prettier": "^9.1.0",
"eslint": "^9.2.0", "eslint-define-config": "^2.1.0",
"eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3",
"eslint-define-config": "^2.1.0", "eslint-plugin-vue": "^9.25.0",
"eslint-plugin-prettier": "^5.1.3", "gradient-string": "^2.0.2",
"eslint-plugin-vue": "^9.25.0", "husky": "^8.0.1",
"gradient-string": "^2.0.2", "lint-staged": "^15.2.2",
"husky": "^8.0.1", "postcss": "^8.4.38",
"lint-staged": "^15.2.2", "postcss-html": "^1.7.0",
"postcss": "^8.4.38", "postcss-import": "^16.1.0",
"postcss-html": "^1.7.0", "postcss-scss": "^4.0.9",
"postcss-import": "^16.1.0", "prettier": "^3.2.5",
"postcss-scss": "^4.0.9", "rimraf": "^5.0.5",
"prettier": "^3.2.5", "rollup-plugin-visualizer": "^5.12.0",
"rimraf": "^5.0.5", "sass": "^1.77.0",
"rollup-plugin-visualizer": "^5.12.0", "stylelint": "^16.5.0",
"sass": "^1.77.0", "stylelint-config-recess-order": "^5.0.1",
"stylelint": "^16.5.0", "stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-recess-order": "^5.0.1", "stylelint-config-standard-scss": "^13.1.0",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-prettier": "^5.0.0",
"stylelint-config-standard-scss": "^13.1.0", "svgo": "^3.3.0",
"stylelint-prettier": "^5.0.0", "tailwindcss": "^3.4.3",
"svgo": "^3.3.0", "typescript": "^5.4.5",
"tailwindcss": "^3.4.3", "vite": "^5.2.11",
"typescript": "^5.4.5", "vite-plugin-cdn-import": "^0.3.5",
"vite": "^5.2.11", "vite-plugin-compression": "^0.5.1",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-fake-server": "^2.1.1",
"vite-plugin-compression": "^0.5.1", "vite-plugin-remove-console": "^2.2.0",
"vite-plugin-fake-server": "^2.1.1", "vite-plugin-router-warn": "^1.0.0",
"vite-plugin-remove-console": "^2.2.0", "vite-svg-loader": "^5.1.0",
"vite-plugin-router-warn": "^1.0.0", "vue-eslint-parser": "^9.4.2",
"vite-svg-loader": "^5.1.0", "vue-tsc": "^1.8.27"
"vue-eslint-parser": "^9.4.2", },
"vue-tsc": "^1.8.27" "engines": {
}, "node": "^18.18.0 || ^20.9.0 || >=21.1.0",
"engines": { "pnpm": ">=9"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0", },
"pnpm": ">=9" "pnpm": {
}, "allowedDeprecatedVersions": {
"pnpm": { "are-we-there-yet": "*",
"allowedDeprecatedVersions": { "sourcemap-codec": "*",
"are-we-there-yet": "*", "domexception": "*",
"sourcemap-codec": "*", "w3c-hr-time": "*",
"domexception": "*", "inflight": "*",
"w3c-hr-time": "*", "npmlog": "*",
"inflight": "*", "rimraf": "*",
"npmlog": "*", "stable": "*",
"rimraf": "*", "gauge": "*",
"stable": "*", "abab": "*",
"gauge": "*", "glob": "*"
"abab": "*", },
"glob": "*" "peerDependencyRules": {
}, "allowedVersions": {
"peerDependencyRules": { "eslint": "9"
"allowedVersions": { }
"eslint": "9" }
} },
} "config": {
}, "commitizen": {
"config": { "path": "node_modules/cz-git"
"commitizen": { }
"path": "node_modules/cz-git" }
}
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
// @ts-check // @ts-check
/** @type {import('postcss-load-config').Config} */ /** @type {import("postcss-load-config").Config} */
export default { export default {
plugins: { plugins: {
"postcss-import": {}, 'postcss-import': {},
"tailwindcss/nesting": {}, 'tailwindcss/nesting': {},
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}) ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
} },
}; };

View File

@ -1,48 +0,0 @@
<template>
<div class="main">
<pure-table
ref="tableRef"
:adaptiveConfig="{ offsetBottom: 108 }"
:columns="column"
:data="dataList"
:header-cell-style="cellHeaderStyle"
:loading="loading"
:size="size"
adaptive
align-whole="center"
border
row-key="id"
table-layout="auto"
/>
</div>
</template>
<script lang="ts" setup>
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
import PureTable from "@pureadmin/table";
import type { PropType } from "vue";
// *
defineProps({
//
dataList: {
type: Array<any>,
default: []
},
//
column: {
type: Array<any>,
default: []
},
loading: {
type: Boolean,
default: false
},
size: {
type: String as PropType<any>,
default: "default"
}
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,96 +1,80 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import { getCurrentInstance, nextTick, onMounted, PropType, ref, unref, watch } from 'vue';
getCurrentInstance, import { rendTipProps } from '@/components/TableBar/utils/tableConfig';
nextTick, import { cellHeaderStyle, getDropdownItemStyle, iconClass, topClass } from '@/components/TableBar/utils/tableStyle';
onMounted, import PureTable from '@pureadmin/table';
PropType, import { useRoute } from 'vue-router';
ref, import RefreshIcon from '@/assets/table-bar/refresh.svg?component';
unref, import CollapseIcon from '@/assets/table-bar/collapse.svg?component';
watch import SettingIcon from '@/assets/table-bar/settings.svg?component';
} from "vue"; import { cloneDeep, getKeyList, isBoolean, isFunction } from '@pureadmin/utils';
import { rendTipProps } from "@/components/TableBar/utils/tableConfig"; import DragIcon from '@/assets/table-bar/drag.svg?component';
import { import Sortable from 'sortablejs';
cellHeaderStyle, import { DeleteFilled, EditPen } from '@element-plus/icons-vue';
getDropdownItemStyle, import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
iconClass, import Refresh from '@iconify-icons/ep/refresh';
topClass import { FormInstance } from 'element-plus';
} from "@/components/TableBar/utils/tableStyle";
import PureTable from "@pureadmin/table";
import { useRoute } from "vue-router";
import { useI18n } from "vue-i18n";
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
import SettingIcon from "@/assets/table-bar/settings.svg?component";
import { cloneDeep, getKeyList, isBoolean, isFunction } from "@pureadmin/utils";
import DragIcon from "@/assets/table-bar/drag.svg?component";
import Sortable from "sortablejs";
import { $t } from "@/plugins/i18n";
import { DeleteFilled, EditPen } from "@element-plus/icons-vue";
import { useRenderIcon } from "@/components/CommonIcon/src/hooks";
import Refresh from "@iconify-icons/ep/refresh";
import { FormInstance } from "element-plus";
// * // *
const props = defineProps({ const props = defineProps({
// //
dataList: { type: Array<any>, default: [] }, dataList: { type: Array<any>, default: [] },
// //
column: { type: Array as PropType<any>, default: () => [] }, column: { type: Array as PropType<any>, default: () => [] },
// //
loading: { type: Boolean, default: false }, loading: { type: Boolean, default: false },
// //
tableQueryFormVisible: { type: Boolean, default: true }, tableQueryFormVisible: { type: Boolean, default: true },
// small | default | large // small | default | large
size: { type: String as PropType<any>, default: "default" }, size: { type: String as PropType<any>, default: 'default' },
// //
pagination: { type: Object, default: Object }, pagination: { type: Object, default: Object },
// //
handleSelectionChange: { handleSelectionChange: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, },
// //
handleSizeChange: { onPageSizeChange: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, },
// //
handleCurrentChange: { onPageCurrentChange: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, },
// //
form: { form: {
type: Object as PropType<any>, type: Object as PropType<any>,
default: Object default: Object,
}, },
// key // key
tableKey: { tableKey: {
type: [String, Number] as PropType<string | number>, type: [String, Number] as PropType<string | number>,
default: "0" default: '0',
}, },
// //
tableTitle: { type: String, default: "" }, tableTitle: { type: String, default: '' },
// //
tableEdit: { tableEdit: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, // }, //
tableDelete: { tableDelete: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, },
// //
onReFresh: { onReFresh: {
type: Function as PropType<Function>, type: Function as PropType<Function>,
default: () => {} default: () => {},
}, },
onSearch: { type: Function as PropType<any> }, onSearch: { type: Function as PropType<any> },
model: { type: Object as PropType<any> } model: { type: Object as PropType<any> },
}); });
const emit = defineEmits(["changeColumn"]); const emit = defineEmits(['changeColumn']);
const { t, locale } = useI18n();
const route = useRoute(); const route = useRoute();
// //
const checkAll = ref(true); const checkAll = ref(true);
@ -100,16 +84,10 @@ const isIndeterminate = ref(false);
// //
const dynamicColumns = ref(props.column); const dynamicColumns = ref(props.column);
// //
const filterColumns = cloneDeep(props.column).filter(column => const filterColumns = cloneDeep(props.column).filter(column => (isBoolean(column?.hide) ? !column.hide : !(isFunction(column?.hide) && column?.hide())));
isBoolean(column?.hide)
? !column.hide
: !(isFunction(column?.hide) && column?.hide())
);
// //
const checkedColumns = ref(getKeyList(cloneDeep(filterColumns), "label")); const checkedColumns = ref(getKeyList(cloneDeep(filterColumns), 'label'));
const checkColumnList = ref( const checkColumnList = ref(getKeyList(cloneDeep(dynamicColumns.value), 'label'));
getKeyList(cloneDeep(dynamicColumns.value), "label")
);
const instance = getCurrentInstance()!; const instance = getCurrentInstance()!;
const ruleFormRef = ref<FormInstance>(); const ruleFormRef = ref<FormInstance>();
@ -118,7 +96,7 @@ const ruleFormRef = ref<FormInstance>();
* @param value 修改样式大小 larger | default | small * @param value 修改样式大小 larger | default | small
*/ */
const handleTableSizeClick = (value: string) => { const handleTableSizeClick = (value: string) => {
size.value = value; size.value = value;
}; };
/** /**
@ -126,11 +104,9 @@ const handleTableSizeClick = (value: string) => {
* @param val 是否全部显示 * @param val 是否全部显示
*/ */
const handleCheckAllChange = (val: boolean) => { const handleCheckAllChange = (val: boolean) => {
checkedColumns.value = val ? checkColumnList.value : []; checkedColumns.value = val ? checkColumnList.value : [];
isIndeterminate.value = false; isIndeterminate.value = false;
dynamicColumns.value.map(column => dynamicColumns.value.map(column => (val ? (column.hide = false) : (column.hide = true)));
val ? (column.hide = false) : (column.hide = true)
);
}; };
/** /**
@ -138,11 +114,10 @@ const handleCheckAllChange = (val: boolean) => {
* @param value * @param value
*/ */
const handleCheckedColumnsChange = (value: string[]) => { const handleCheckedColumnsChange = (value: string[]) => {
checkedColumns.value = value; checkedColumns.value = value;
const checkedCount = value.length; const checkedCount = value.length;
checkAll.value = checkedCount === checkColumnList.value.length; checkAll.value = checkedCount === checkColumnList.value.length;
isIndeterminate.value = isIndeterminate.value = checkedCount > 0 && checkedCount < checkColumnList.value.length;
checkedCount > 0 && checkedCount < checkColumnList.value.length;
}; };
/** /**
@ -150,9 +125,7 @@ const handleCheckedColumnsChange = (value: string[]) => {
* @param label * @param label
*/ */
const handleCheckColumnListChange = (label: string) => { const handleCheckColumnListChange = (label: string) => {
dynamicColumns.value.filter(item => item.label === label)[0].hide = !( dynamicColumns.value.filter(item => item.label === label)[0].hide = !(event.target as any).checked;
event.target as any
).checked;
}; };
/** /**
@ -160,72 +133,67 @@ const handleCheckColumnListChange = (label: string) => {
* @param label * @param label
*/ */
const isFixedColumn = (label: string) => { const isFixedColumn = (label: string) => {
return dynamicColumns.value.filter(item => item.label === label)[0].fixed; return dynamicColumns.value.filter(item => item.label === label)[0].fixed;
}; };
/** /**
* * 重置自定义表格列字段 * * 重置自定义表格列字段
*/ */
const onReset = async () => { const onReset = async () => {
// //
const list = []; const list = [];
// true // true
checkAll.value = true; checkAll.value = true;
isIndeterminate.value = false; isIndeterminate.value = false;
// //
checkedColumns.value = getKeyList(cloneDeep(filterColumns), "label"); checkedColumns.value = getKeyList(cloneDeep(filterColumns), 'label');
// ? ref reactive // ? ref reactive
// ? Proxy 访使 // ? Proxy 访使
checkColumnList.value = []; checkColumnList.value = [];
await nextTick(() => { await nextTick(() => {
checkColumnList.value = getKeyList(filterColumns, "label"); checkColumnList.value = getKeyList(filterColumns, 'label');
}); });
// checkedColumns list // checkedColumns list
checkedColumns.value.forEach(item => { checkedColumns.value.forEach(item => {
dynamicColumns.value.forEach(column => { dynamicColumns.value.forEach(column => {
if (column.label == item) { if (column.label == item) {
list.push(column); list.push(column);
} }
}); });
}); });
emit("changeColumn", list); emit('changeColumn', list);
}; };
/** 列展示拖拽排序 */ /** 列展示拖拽排序 */
const rowDrop = (event: any) => { const rowDrop = (event: any) => {
nextTick(() => { nextTick(() => {
const wrapper: HTMLElement = ( const wrapper: HTMLElement = (instance?.proxy?.$refs[`GroupRef${unref(props.tableKey)}`] as any).$el.firstElementChild;
instance?.proxy?.$refs[`GroupRef${unref(props.tableKey)}`] as any Sortable.create(wrapper, {
).$el.firstElementChild; animation: 300,
Sortable.create(wrapper, { handle: '.drag-btn',
animation: 300, onEnd: ({ newIndex, oldIndex, item }) => {
handle: ".drag-btn", const targetThElem = item;
onEnd: ({ newIndex, oldIndex, item }) => { const wrapperElem = targetThElem.parentNode as HTMLElement;
const targetThElem = item; const oldColumn = dynamicColumns.value[oldIndex];
const wrapperElem = targetThElem.parentNode as HTMLElement; const newColumn = dynamicColumns.value[newIndex];
const oldColumn = dynamicColumns.value[oldIndex]; if (oldColumn?.fixed || newColumn?.fixed) {
const newColumn = dynamicColumns.value[newIndex]; // fixed
if (oldColumn?.fixed || newColumn?.fixed) { const oldThElem = wrapperElem.children[oldIndex] as HTMLElement;
// fixed if (newIndex > oldIndex) {
const oldThElem = wrapperElem.children[oldIndex] as HTMLElement; wrapperElem.insertBefore(targetThElem, oldThElem);
if (newIndex > oldIndex) { } else {
wrapperElem.insertBefore(targetThElem, oldThElem); wrapperElem.insertBefore(targetThElem, oldThElem ? oldThElem.nextElementSibling : oldThElem);
} else { }
wrapperElem.insertBefore( return;
targetThElem, }
oldThElem ? oldThElem.nextElementSibling : oldThElem const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0];
); dynamicColumns.value.splice(newIndex, 0, currentRow);
} emit('changeColumn', dynamicColumns.value);
return; },
} });
const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0]; }).then();
dynamicColumns.value.splice(newIndex, 0, currentRow);
emit("changeColumn", dynamicColumns.value);
}
});
}).then();
}; };
/** /**
@ -233,234 +201,141 @@ const rowDrop = (event: any) => {
* @param formEl * @param formEl
*/ */
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.resetFields(); formEl.resetFields();
props.onSearch(); props.onSearch();
}; };
onMounted(() => { onMounted(() => {
watch([() => props.column], () => { watch([() => props.column], () => {
dynamicColumns.value = props.column; dynamicColumns.value = props.column;
}); });
}); });
</script> </script>
<template> <template>
<div class="main"> <div class="main">
<!-- 表单设置外加插槽 --> <!-- 表单设置外加插槽 -->
<el-form <el-form v-show="tableQueryFormVisible" ref="ruleFormRef" :inline="true" :model="model" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto" @submit="onSearch">
v-show="tableQueryFormVisible" <slot name="tableForm" />
ref="ruleFormRef" <el-form-item>
:inline="true" <el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
:model="model" <el-button :icon="useRenderIcon(Refresh)" @click="resetForm(ruleFormRef)"> 重置</el-button>
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto" </el-form-item>
@submit="onSearch" </el-form>
>
<slot name="tableForm" />
<el-form-item>
<el-button
:icon="useRenderIcon('ri:search-line')"
:loading="loading"
type="primary"
@click="onSearch"
>
{{ $t("buttons.search") }}
</el-button>
<el-button
:icon="useRenderIcon(Refresh)"
@click="resetForm(ruleFormRef)"
>
{{ $t("buttons.rest") }}</el-button
>
</el-form-item>
</el-form>
<!-- 表格头部设置 --> <!-- 表格头部设置 -->
<div class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color"> <div class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
<div class="flex justify-between w-full h-[60px] p-4"> <div class="flex justify-between w-full h-[60px] p-4">
<!-- 自定义左边头部内容 --> <!-- 自定义左边头部内容 -->
<slot name="tableTitle"> <slot name="tableTitle">
<p class="font-bold truncate"> <p class="font-bold truncate">
{{ tableTitle ? tableTitle : t(route.meta.title) }} {{ tableTitle ? tableTitle : route.meta.title }}
</p> </p>
</slot> </slot>
<!-- 自定义表格操作内容 --> <!-- 自定义表格操作内容 -->
<slot name="tableOperation"> <slot name="tableOperation">
<div class="flex items-center justify-around"> <div class="flex items-center justify-around">
<!-- 插槽内容 --> <!-- 插槽内容 -->
<div class="mr-4"> <div class="mr-4">
<slot name="tableButtons" /> <slot name="tableButtons" />
</div> </div>
<!-- 表格刷新按钮 --> <!-- 表格刷新按钮 -->
<RefreshIcon <RefreshIcon v-tippy="rendTipProps('刷新')" :class="`w-[16px] ${iconClass()}} ${loading ? 'animate-spin' : ''}`" @click="onReFresh" />
v-tippy="rendTipProps('刷新')" <el-divider direction="vertical" />
:class="`w-[16px] ${iconClass()}} ${loading ? 'animate-spin' : ''}`"
@click="onReFresh"
/>
<el-divider direction="vertical" />
<!-- 选择表格大小 --> <!-- 选择表格大小 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<CollapseIcon :class="`w-[16px] ${iconClass()}`" /> <CollapseIcon :class="`w-[16px] ${iconClass()}`" />
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item :style="getDropdownItemStyle(size, 'large')" @click="handleTableSizeClick('large')"> </el-dropdown-item>
:style="getDropdownItemStyle(size, 'large')" <el-dropdown-item :style="getDropdownItemStyle(size, 'default')" @click="handleTableSizeClick('default')"> 默认 </el-dropdown-item>
@click="handleTableSizeClick('large')" <el-dropdown-item :style="getDropdownItemStyle(size, 'small')" @click="handleTableSizeClick('small')"> </el-dropdown-item>
> </el-dropdown-menu>
{{ $t("style.larger") }} </template>
</el-dropdown-item> </el-dropdown>
<el-dropdown-item <el-divider direction="vertical" />
:style="getDropdownItemStyle(size, 'default')"
@click="handleTableSizeClick('default')"
>
{{ t("style.default") }}
</el-dropdown-item>
<el-dropdown-item
:style="getDropdownItemStyle(size, 'small')"
@click="handleTableSizeClick('small')"
>
{{ t("style.small") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" />
<!-- 表格列设置 --> <!-- 表格列设置 -->
<el-popover <el-popover :popper-style="{ padding: 0 }" placement="bottom-start" trigger="click" width="200">
:popper-style="{ padding: 0 }" <template #reference>
placement="bottom-start" <SettingIcon v-tippy="rendTipProps('列设置')" :class="`w-[16px] ${iconClass()}`" />
trigger="click" </template>
width="200"
>
<template #reference>
<SettingIcon
v-tippy="rendTipProps('列设置')"
:class="`w-[16px] ${iconClass()}`"
/>
</template>
<div :class="topClass()"> <div :class="topClass()">
<el-checkbox <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" class="!-mr-1" label="列展示" @change="handleCheckAllChange" />
v-model="checkAll" <el-button link type="primary" @click="onReset"> 重置</el-button>
:indeterminate="isIndeterminate" </div>
class="!-mr-1"
label="列展示"
@change="handleCheckAllChange"
/>
<el-button link type="primary" @click="onReset">
{{ t("buttons.rest") }}</el-button
>
</div>
<div class="pt-[6px] pl-[11px]"> <div class="pt-[6px] pl-[11px]">
<el-scrollbar max-height="36vh"> <el-scrollbar max-height="36vh">
<el-checkbox-group <el-checkbox-group :ref="`GroupRef${unref(props.tableKey)}`" :modelValue="checkedColumns" @change="handleCheckedColumnsChange">
:ref="`GroupRef${unref(props.tableKey)}`" <el-space :alignment="'flex-start'" :size="0" direction="vertical">
:modelValue="checkedColumns" <div v-for="(item, index) in checkColumnList" :key="index" class="flex items-center">
@change="handleCheckedColumnsChange" <DragIcon :class="`drag-btn w-[16px] mr-2 ${isFixedColumn(item) ? '!cursor-no-drop' : '!cursor-grab'}`" @mouseenter.prevent="rowDrop" />
> <el-checkbox :key="index" :label="item" :value="item" @change="handleCheckColumnListChange(item)">
<el-space <span :title="item" class="inline-block w-[120px] truncate hover:text-text_color_primary">
:alignment="'flex-start'" {{ item }}
:size="0" </span>
direction="vertical" </el-checkbox>
> </div>
<div </el-space>
v-for="(item, index) in checkColumnList" </el-checkbox-group>
:key="index" </el-scrollbar>
class="flex items-center" </div>
> </el-popover>
<DragIcon </div>
:class="`drag-btn w-[16px] mr-2 ${isFixedColumn(item) ? '!cursor-no-drop' : '!cursor-grab'}`" </slot>
@mouseenter.prevent="rowDrop" </div>
/> <slot name="tableSelect" />
<el-checkbox <pure-table
:key="index" ref="tableRef"
:label="item" :adaptiveConfig="{ offsetBottom: 108 }"
:value="item" :columns="column"
@change="handleCheckColumnListChange(item)" :data="dataList"
> :header-cell-style="cellHeaderStyle"
<span :loading="loading"
:title="item" :on-page-current-change="onPageCurrentChange"
class="inline-block w-[120px] truncate hover:text-text_color_primary" :on-page-size-change="onPageSizeChange"
> :pagination="pagination"
{{ item }} :paginationSmall="size === 'small'"
</span> :size="size"
</el-checkbox> adaptive
</div> align-whole="center"
</el-space> border
</el-checkbox-group> row-key="id"
</el-scrollbar> stripe
</div> table-layout="fixed"
</el-popover> v-bind="$attrs"
</div> @selection-change="handleSelectionChange"
</slot> >
</div> <template v-for="item in column" :key="item.prop" v-slot:[item.slot]="scope" v-bind="item">
<slot name="tableSelect" /> <slot :name="item.slot" v-bind="scope" />
<pure-table <slot v-if="item.slot === 'operation'" name="operation">
ref="tableRef" <el-button :icon="EditPen" link type="warning" @click="tableEdit(scope)">修改</el-button>
:adaptiveConfig="{ offsetBottom: 108 }" <el-popconfirm title="是否确认删除" @confirm="tableDelete(scope)">
:columns="column" <template #reference>
:data="dataList" <el-button :icon="DeleteFilled" link type="danger">删除</el-button>
:header-cell-style="cellHeaderStyle" </template>
:loading="loading" </el-popconfirm>
:pagination="pagination" </slot>
:paginationSmall="size === 'small'" </template>
:size="size" </pure-table>
adaptive </div>
align-whole="center" </div>
border
row-key="id"
stripe
table-layout="fixed"
v-bind="$attrs"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
@selection-change="handleSelectionChange"
>
<template
v-for="item in column"
:key="item.prop"
v-slot:[item.slot]="scope"
v-bind="item"
>
<slot :name="item.slot" v-bind="scope" />
<slot v-if="item.slot === 'operation'" name="operation">
<el-button
:icon="EditPen"
link
type="warning"
@click="tableEdit(scope)"
>修改</el-button
>
<el-popconfirm
:title="t('table.popConfirmTitle')"
@confirm="tableDelete(scope)"
>
<template #reference>
<el-button :icon="DeleteFilled" link type="danger"
>删除</el-button
>
</template>
</el-popconfirm>
</slot>
</template>
</pure-table>
</div>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-dropdown-menu__item i) { :deep(.el-dropdown-menu__item i) {
margin: 0; margin: 0;
} }
.search-form { .search-form {
:deep(.el-form-item) { :deep(.el-form-item) {
margin-bottom: 12px; margin-bottom: 12px;
} }
} }
</style> </style>

View File

@ -1,75 +0,0 @@
<template>
<div class="main mt-2 p-2 bg-bg_color">
<pure-table
ref="tableRef"
:adaptiveConfig="{ offsetBottom: 108 }"
:columns="column"
:data="dataList"
:header-cell-style="cellHeaderStyle"
:loading="loading"
:pagination="pagination"
:paginationSmall="size === 'small'"
:size="size"
adaptive
align-whole="center"
border
row-key="id"
table-layout="auto"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
/>
</div>
</template>
<script lang="ts" setup>
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
import PureTable from "@pureadmin/table";
import type { PropType } from "vue";
// *
defineProps({
//
dataList: {
type: Array<any>,
default: []
},
//
column: {
type: Array<any>,
default: []
},
//
loading: {
type: Boolean,
default: false
},
// small | default | large
size: {
type: String as PropType<any>,
default: "default"
},
//
pagination: {
type: Object,
default: Object
},
//
handleSelectionChange: {
type: Function as PropType<Function>,
default: () => {}
},
//
handleSizeChange: {
type: Function as PropType<Function>,
default: () => {}
},
//
handleCurrentChange: {
type: Function as PropType<Function>,
default: () => {}
}
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,105 +0,0 @@
<template>
<div class="main">
<el-form
ref="formRef"
:inline="true"
:model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
>
<slot name="tableForm" />
</el-form>
<div class="mt-2 p-2 bg-bg_color">
<pure-table
ref="tableRef"
:adaptiveConfig="{ offsetBottom: 108 }"
:columns="column"
:data="dataList"
:header-cell-style="cellHeaderStyle"
:loading="loading"
:pagination="pagination"
:paginationSmall="size === 'small'"
:size="size"
adaptive
align-whole="center"
border
row-key="id"
table-layout="auto"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
import PureTable from "@pureadmin/table";
// *
defineProps({
//
dataList: {
type: Array<any>,
default: []
},
//
column: {
type: Array<any>,
default: []
},
//
loading: {
type: Boolean,
default: false
},
// small | default | large
size: {
type: String as PropType<any>,
default: "default"
},
//
pagination: {
type: Object,
default: Object
},
//
handleSelectionChange: {
type: Function as PropType<Function>,
default: () => {}
},
//
handleSizeChange: {
type: Function as PropType<Function>,
default: () => {}
},
//
handleCurrentChange: {
type: Function as PropType<Function>,
default: () => {}
},
//
form: {
type: Object as PropType<any>,
default: Object
}
});
</script>
<style lang="scss" scoped>
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
.main-content {
margin: 24px 24px 0 !important;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>

View File

@ -0,0 +1,204 @@
<script lang="ts" setup>
import { onMounted, PropType, ref } from 'vue';
import { rendTipProps } from '@/components/TableBar/utils/tableConfig';
import { getDropdownItemStyle, iconClass } from '@/components/TableBar/utils/tableStyle';
import { useRoute } from 'vue-router';
import RefreshIcon from '@/assets/table-bar/refresh.svg?component';
import CollapseIcon from '@/assets/table-bar/collapse.svg?component';
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
import Refresh from '@iconify-icons/ep/refresh';
import { FormInstance } from 'element-plus';
import { Pagination } from '../../../../types/pagination/pagination';
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
import { pageSizes } from '@/enum/baseConstant';
// *
const props = defineProps({
//
dataList: { type: Array<any>, default: [] },
//
column: { type: Array as PropType<any>, default: () => [] },
//
loading: { type: Boolean, default: false },
//
tableQueryFormVisible: { type: Boolean, default: true },
// medium | small | mini
size: { type: String as PropType<any>, default: 'medium' },
//
pagination: { type: Object as PropType<Pagination>, default: Object },
//
onPageChange: {
type: Function as PropType<any>,
},
//
form: {
type: Object as PropType<any>,
default: Object,
},
//
tableTitle: { type: String, default: '' },
//
onTableEdit: {
type: Function as PropType<Function>,
default: () => {},
}, //
onTableDelete: {
type: Function as PropType<Function>,
default: () => {},
},
//
onReFresh: {
type: Function as PropType<any>,
},
onSearch: { type: Function as PropType<any> },
});
const route = useRoute();
//
const size = ref(props.size);
const toolbarRef = ref<VxeToolbarInstance>();
const tableRef = ref<VxeTableInstance>();
const ruleFormRef = ref<FormInstance>();
/**
* * 修改表格样式大小
* @param value 修改样式大小 medium | small | mini
*/
const handleTableSizeClick = (value: string) => {
size.value = value;
};
/**
* 重置表单
* @param formEl
*/
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
props.onSearch();
};
onMounted(() => {
const $table = tableRef.value;
const $toolbarRef = toolbarRef.value;
if ($table && $toolbarRef) {
$table.connect($toolbarRef);
}
});
</script>
<template>
<div class="main">
<!-- 表单设置外加插槽 -->
<el-form v-show="tableQueryFormVisible" ref="ruleFormRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto" @submit="onSearch">
<slot name="tableForm" />
<el-form-item>
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(ruleFormRef)"> 重置</el-button>
</el-form-item>
</el-form>
<!-- 表格头部设置 -->
<div class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
<div class="flex justify-between w-full h-[60px] p-4">
<!-- 自定义左边头部内容 -->
<slot name="tableTitle">
<p class="font-bold truncate">
{{ tableTitle ? tableTitle : route.meta.title }}
</p>
</slot>
<!-- 自定义表格操作内容 -->
<slot name="tableOperation">
<div class="flex items-center justify-around">
<!-- 插槽内容 -->
<div class="mr-4">
<slot name="tableButtons" />
</div>
<!-- 表格刷新按钮 -->
<RefreshIcon v-tippy="rendTipProps('刷新')" :class="`w-[16px] ${iconClass()}} ${loading ? 'animate-spin' : ''}`" @click="onReFresh" />
<el-divider direction="vertical" />
<!-- 选择表格大小 -->
<el-dropdown trigger="click">
<CollapseIcon :class="`w-[16px] ${iconClass()}`" />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item :style="getDropdownItemStyle(size, 'medium')" @click="handleTableSizeClick('medium')"> </el-dropdown-item>
<el-dropdown-item :style="getDropdownItemStyle(size, 'small')" @click="handleTableSizeClick('small')"> 默认 </el-dropdown-item>
<el-dropdown-item :style="getDropdownItemStyle(size, 'mini')" @click="handleTableSizeClick('mini')"> </el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" />
<vxe-toolbar ref="toolbarRef" custom export print />
<el-divider direction="vertical" />
</div>
</slot>
</div>
<slot name="tableSelect" />
<!-- 表格 -->
<vxe-table
ref="tableRef"
:column-config="{ resizable: true }"
:custom-config="{}"
:data="dataList"
:export-config="{}"
:loading="loading"
:print-config="{}"
:row-config="{ isHover: true }"
:size="size"
:tooltip-config="{ enterable: true, showAll: true }"
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
align="center"
border
header-align="center"
round
show-overflow
stripe
>
<slot name="tableType">
<vxe-column title="序号" type="seq" width="70" />
</slot>
<vxe-column v-for="item in column" :key="item.prop" :field="item.prop" :title="item.label">
<template v-if="$slots[item.prop]" #default="scope">
<slot :name="item.prop" v-bind="scope" />
</template>
</vxe-column>
<template #empty>
<div>
<img alt="" src="https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif" />
<p>不用再看了没有更多数据了</p>
</div>
</template>
</vxe-table>
<vxe-pager
v-show="onPageChange !== undefined"
:background="true"
:current-page="pagination.currentPage"
:layouts="['Home', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'End', 'Sizes', 'FullJump', 'Total']"
:loading="loading"
:on-page-change="onPageChange"
:page-size="pagination.pageSize"
:page-sizes="pageSizes"
:pager-count="5"
:size="size"
:total="pagination.total"
border
/>
</div>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>

View File

@ -1,30 +1,28 @@
import { computed } from "vue"; import { computed } from 'vue';
import { useEpThemeStoreHook } from "@/store/epTheme"; import { useEpThemeStoreHook } from '@/store/modules/epTheme';
/** /**
* * * *
*/ */
export const cellHeaderStyle = () => ({ export const cellHeaderStyle = () => ({
background: "var(--el-fill-color-light)", background: 'var(--el-fill-color-light)',
color: "var(--el-text-color-primary)" color: 'var(--el-text-color-primary)',
}); });
// * icon 样式 // * icon 样式
export const iconClass = () => export const iconClass = () => 'text-black dark:text-white duration-100 hover:!text-primary cursor-pointer outline-none ';
"text-black dark:text-white duration-100 hover:!text-primary cursor-pointer outline-none ";
// * 顶部样式 // * 顶部样式
export const topClass = () => export const topClass = () => 'flex justify-between pt-[3px] px-[11px] border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]';
"flex justify-between pt-[3px] px-[11px] border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]";
/** /**
* * * *
*/ */
export const getDropdownItemStyle = computed(() => { export const getDropdownItemStyle = computed(() => {
return (size: string, s: string) => { return (size: string, s: string) => {
return { return {
background: s === size ? useEpThemeStoreHook().epThemeColor : "", background: s === size ? useEpThemeStoreHook().epThemeColor : '',
color: s === size ? "#fff" : "var(--el-text-color-primary)" color: s === size ? '#fff' : 'var(--el-text-color-primary)',
}; };
}; };
}); });

View File

@ -1,52 +1,52 @@
import axios from "axios"; import axios from 'axios';
import type { App } from "vue"; import type { App } from 'vue';
let config: object = {}; let config: object = {};
const { VITE_PUBLIC_PATH } = import.meta.env; const { VITE_PUBLIC_PATH } = import.meta.env;
const setConfig = (cfg?: unknown) => { const setConfig = (cfg?: unknown) => {
config = Object.assign(config, cfg); config = Object.assign(config, cfg);
}; };
const getConfig = (key?: string): PlatformConfigs => { const getConfig = (key?: string): PlatformConfigs => {
if (typeof key === "string") { if (typeof key === 'string') {
const arr = key.split("."); const arr = key.split('.');
if (arr && arr.length) { if (arr && arr.length) {
let data = config; let data = config;
arr.forEach(v => { arr.forEach(v => {
if (data && typeof data[v] !== "undefined") { if (data && typeof data[v] !== 'undefined') {
data = data[v]; data = data[v];
} else { } else {
data = null; data = null;
} }
}); });
return data; return data;
} }
} }
return config; return config;
}; };
/** 获取项目动态全局配置 */ /** 获取项目动态全局配置 */
export const getPlatformConfig = async (app: App): Promise<undefined> => { export const getPlatformConfig = async (app: App): Promise<undefined> => {
app.config.globalProperties.$config = getConfig(); app.config.globalProperties.$config = getConfig();
return axios({ return axios({
method: "get", method: 'get',
url: `${VITE_PUBLIC_PATH}platform-config.json` url: `${VITE_PUBLIC_PATH}platform-config.json`,
}) })
.then(({ data: config }) => { .then(({ data: config }) => {
let $config = app.config.globalProperties.$config; let $config = app.config.globalProperties.$config;
// 自动注入系统配置 // 自动注入系统配置
if (app && $config && typeof config === "object") { if (app && $config && typeof config === 'object') {
$config = Object.assign($config, config); $config = Object.assign($config, config);
app.config.globalProperties.$config = $config; app.config.globalProperties.$config = $config;
// 设置全局配置 // 设置全局配置
setConfig($config); setConfig($config);
} }
return $config; return $config;
}) })
.catch(() => { .catch(() => {
throw "请在public文件夹下添加platform-config.json配置文件"; throw '请在public文件夹下添加platform-config.json配置文件';
}); });
}; };
/** 本地响应式存储的命名空间 */ /** 本地响应式存储的命名空间 */

30
src/enum/baseConstant.ts Normal file
View File

@ -0,0 +1,30 @@
import type { Option } from '../../types/enum/options';
/**
* *
*/
export const isDefaultOptions: Option[] = [
{ value: true, label: '是' },
{ value: false, label: '否' },
];
/**
* *
*/
export const isDefaultVisibleOptions: Option[] = [
{ value: true, label: '显示' },
{ value: false, label: '不显示' },
];
/**
* *
*/
export const sexConstant: Option[] = [
{ value: 1, label: '男' },
{ value: 0, label: '女' },
];
/**
* *
*/
export const pageSizes: number[] = [10, 30, 50, 100, 150, 200, 300];

View File

@ -0,0 +1,29 @@
// ? 表单选项
import type { Option } from '../../types/enum/options';
/**
* *
*/
export const defaultStatus: Option[] = [
{ value: '', label: '' },
{ value: 1, label: '启用' },
{ value: 0, label: '禁用' },
];
/**
* *
*/
export const isErrorStatus: Option[] = [
{ value: '', label: '' },
{ value: false, label: '未出错' },
{ value: true, label: '错误' },
];
/**
* *
*/
export const statusConstant: Option[] = [
{ value: '', label: '' },
{ value: 1, label: '是' },
{ value: 0, label: '否' },
];

View File

@ -0,0 +1,10 @@
import type { Option } from '../../../types/enum/options';
/**
*
*/
export const faviconCategory: Option[] = [
{ value: '', label: '' },
{ value: 'web', label: 'web 前台' },
{ value: 'admin', label: 'admin 后台' },
];

View File

@ -0,0 +1,21 @@
import type { Option } from '../../../types/enum/options';
/**
* *
*/
export const feedback: Option[] = [
{ value: '', label: '' },
{ value: 1, label: '已处理' },
{ value: 0, label: '未处理' },
{ value: -1, label: '其它问题' },
];
/**
* *
*/
export const feedbackTypeOptions: Option[] = [
{ label: '优化建议', value: '优化建议' },
{ label: 'bug反馈', value: 'bug反馈' },
{ label: '新增功能建议', value: '新增功能建议' },
{ label: '其它', value: '其它' },
];

View File

@ -0,0 +1,25 @@
import type { Option } from '../../../types/enum/options';
/**
* *
*/
export const layoutConstant: Option[] = [
{ value: 'ltr', label: '从左到右' },
{ value: 'rtl', label: '从右到左' },
];
/**
* *
*/
export const articleModeConstant: Option[] = [
{ value: 'album', label: '相册模式' },
{ value: 'list', label: '列表模式' },
];
/**
* *
*/
export const userStatus: Option[] = [
{ value: 0, label: '启用' },
{ value: 1, label: '禁用' },
];

View File

@ -1,67 +1,67 @@
import App from "./App.vue"; import App from './App.vue';
import router from "./router"; import router from './router';
import { setupStore } from "@/store"; import { setupStore } from '@/store';
import { getPlatformConfig } from "./config"; import { getPlatformConfig } from './config';
import { MotionPlugin } from "@vueuse/motion"; import { MotionPlugin } from '@vueuse/motion';
// import { useEcharts } from "@/plugins/echarts"; // import { useEcharts } from "@/plugins/echarts";
import { createApp, type Directive } from "vue"; import { createApp, type Directive } from 'vue';
import { useElementPlus } from "@/plugins/elementPlus"; import { useElementPlus } from '@/plugins/elementPlus';
import { injectResponsiveStorage } from "@/utils/responsive"; import { injectResponsiveStorage } from '@/utils/responsive';
import Table from "@pureadmin/table"; import Table from '@pureadmin/table';
// import PureDescriptions from "@pureadmin/descriptions"; // import PureDescriptions from "@pureadmin/descriptions";
// 引入重置样式 // 引入重置样式
import "./style/reset.scss"; import './style/reset.scss';
// 导入公共样式 // 导入公共样式
import "./style/index.scss"; import './style/index.scss';
// 一定要在main.ts中导入tailwind.css防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题 // 一定要在main.ts中导入tailwind.css防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
import "./style/tailwind.css"; import './style/tailwind.css';
import "element-plus/dist/index.css"; import 'element-plus/dist/index.css';
// 导入字体图标 // 导入字体图标
import "./assets/iconfont/iconfont.js"; import './assets/iconfont/iconfont.js';
import "./assets/iconfont/iconfont.css"; import './assets/iconfont/iconfont.css';
// 自定义指令 // 自定义指令
import * as directives from "@/directives"; import * as directives from '@/directives';
// 全局注册@iconify/vue图标库 // 全局注册@iconify/vue图标库
import { import { FontIcon, IconifyIconOffline, IconifyIconOnline } from './components/CommonIcon';
FontIcon,
IconifyIconOffline,
IconifyIconOnline
} from "./components/CommonIcon";
// 全局注册按钮级别权限组件 // 全局注册按钮级别权限组件
import { Auth } from "@/components/Auth"; import { Auth } from '@/components/Auth';
import { Perms } from "@/components/Perms"; import { Perms } from '@/components/Perms';
// 全局注册vue-tippy // 全局注册vue-tippy
import "tippy.js/dist/tippy.css"; import 'tippy.js/dist/tippy.css';
import "tippy.js/themes/light.css"; import 'tippy.js/themes/light.css';
import VueTippy from "vue-tippy"; import VueTippy from 'vue-tippy';
import { useEcharts } from "@/plugins/echarts"; import { useEcharts } from '@/plugins/echarts';
// 完整导入 表格库
import VxeUITable from 'vxe-table';
import 'vxe-table/lib/style.css';
const app = createApp(App); const app = createApp(App);
Object.keys(directives).forEach(key => { Object.keys(directives).forEach(key => {
app.directive(key, (directives as { [key: string]: Directive })[key]); app.directive(key, (directives as { [key: string]: Directive })[key]);
}); });
app.component("IconifyIconOffline", IconifyIconOffline); app.component('IconifyIconOffline', IconifyIconOffline);
app.component("IconifyIconOnline", IconifyIconOnline); app.component('IconifyIconOnline', IconifyIconOnline);
app.component("FontIcon", FontIcon); app.component('FontIcon', FontIcon);
app.component("Auth", Auth); app.component('Auth', Auth);
app.component("Perms", Perms); app.component('Perms', Perms);
app.use(VueTippy); app.use(VueTippy);
getPlatformConfig(app).then(async config => { getPlatformConfig(app).then(async config => {
setupStore(app); setupStore(app);
app.use(router); app.use(router);
await router.isReady(); await router.isReady();
injectResponsiveStorage(app, config); injectResponsiveStorage(app, config);
app app
.use(MotionPlugin) .use(MotionPlugin)
.use(useElementPlus) .use(useElementPlus)
.use(Table) .use(Table)
// .use(PureDescriptions) .use(VxeUITable)
.use(useEcharts); // .use(PureDescriptions)
app.mount("#app"); .use(useEcharts);
app.mount('#app');
}); });

View File

@ -0,0 +1,9 @@
export const columns = [
{ prop: 'id', label: 'id' },
{ prop: 'name', label: 'name' },
{ prop: 'nickname', label: 'nickname' },
{ prop: 'role', label: 'role' },
{ prop: 'sex', label: 'sex' },
{ prop: 'age', label: 'age' },
{ prop: 'address', label: 'address' },
];

View File

@ -1,66 +1,264 @@
<script setup lang="ts"> <script lang="ts" setup>
import { initRouter } from "@/router/utils"; import { initRouter } from '@/router/utils';
import { storageLocal } from "@pureadmin/utils"; import { storageLocal } from '@pureadmin/utils';
import { type CSSProperties, ref, computed } from "vue"; import { computed, type CSSProperties, ref } from 'vue';
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from '@/store/modules/user';
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from '@/store/modules/permission';
import TablePlusVxeBar from '@/components/TableBar/src/TablePlusVxeBar.vue';
import { columns } from '@/views/permission/page/columns';
defineOptions({ defineOptions({
name: "PermissionPage" name: 'PermissionPage',
}); });
const elStyle = computed((): CSSProperties => { const elStyle = computed((): CSSProperties => {
return { return {
width: "85vw", width: '85vw',
justifyContent: "start" justifyContent: 'start',
}; };
}); });
const username = ref(useUserStoreHook()?.username); const username = ref(useUserStoreHook()?.username);
const options = [ const options = [
{ {
value: "admin", value: 'admin',
label: "管理员角色" label: '管理员角色',
}, },
{ {
value: "common", value: 'common',
label: "普通角色" label: '普通角色',
} },
]; ];
function onChange() { function onChange() {
useUserStoreHook() useUserStoreHook()
.loginByUsername({ username: username.value, password: "admin123" }) .loginByUsername({ username: username.value, password: 'admin123' })
.then(res => { .then(res => {
if (res.success) { if (res.success) {
storageLocal().removeItem("async-routes"); storageLocal().removeItem('async-routes');
usePermissionStoreHook().clearAllCachePage(); usePermissionStoreHook().clearAllCachePage();
initRouter(); initRouter();
} }
}); });
} }
const dataList = [
{
id: 10001,
name: 'Test1',
nickname: 'T1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'Shenzhen',
},
{
id: 10002,
name: 'Test2',
nickname: 'T2',
role: 'Test',
sex: 'Women',
age: 22,
address: 'Guangzhou',
},
{
id: 10003,
name: 'Test3',
nickname: 'T3',
role: 'PM',
sex: 'Man',
age: 32,
address: 'Shanghai',
},
{
id: 10004,
name: 'Test4',
nickname: 'T4',
role: 'Designer',
sex: 'Women',
age: 23,
address: 'test abc',
},
{
id: 10005,
name: 'Test5',
nickname: 'T5',
role: 'Develop',
sex: 'Women',
age: 30,
address: 'Shanghai',
},
{
id: 10006,
name: 'Test6',
nickname: 'T6',
role: 'Designer',
sex: 'Women',
age: 21,
address: 'Shenzhen',
},
{
id: 10007,
name: 'Test7',
nickname: 'T7',
role: 'Test',
sex: 'Man',
age: 29,
address: 'Shenzhen',
},
{
id: 10008,
name: 'Test8',
nickname: 'T8',
role: 'Develop',
sex: 'Man',
age: 35,
address: 'test abc',
},
{
id: 10009,
name: 'Test9',
nickname: 'T9',
role: 'Develop',
sex: 'Man',
age: 35,
address: 'Shenzhen',
},
{
id: 100010,
name: 'Test10',
nickname: 'T10',
role: 'Develop',
sex: 'Man',
age: 35,
address: 'Guangzhou',
},
{
id: 100011,
name: 'Test11',
nickname: 'T11',
role: 'Develop',
sex: 'Man',
age: 49,
address: 'Guangzhou',
},
{
id: 100012,
name: 'Test12',
nickname: 'T12',
role: 'Develop',
sex: 'Women',
age: 45,
address: 'Shanghai',
},
{
id: 100013,
name: 'Test13',
nickname: 'T13',
role: 'Test',
sex: 'Women',
age: 35,
address: 'Guangzhou',
},
{
id: 100014,
name: 'Test14',
nickname: 'T14',
role: 'Test',
sex: 'Man',
age: 29,
address: 'Shanghai',
},
{
id: 100015,
name: 'Test15',
nickname: 'T15',
role: 'Develop',
sex: 'Man',
age: 39,
address: 'Guangzhou',
},
{
id: 100016,
name: 'Test16',
nickname: 'T16',
role: 'Test',
sex: 'Women',
age: 35,
address: 'Guangzhou',
},
{
id: 100017,
name: 'Test17',
nickname: 'T17',
role: 'Test',
sex: 'Man',
age: 39,
address: 'Shanghai',
},
{
id: 100018,
name: 'Test18',
nickname: 'T18',
role: 'Develop',
sex: 'Man',
age: 44,
address: 'Guangzhou',
},
{
id: 100019,
name: 'Test19',
nickname: 'T19',
role: 'Develop',
sex: 'Man',
age: 39,
address: 'Guangzhou',
},
{
id: 100020,
name: 'Test20',
nickname: 'T20',
role: 'Test',
sex: 'Women',
age: 35,
address: 'Guangzhou',
},
{
id: 100021,
name: 'Test21',
nickname: 'T21',
role: 'Test',
sex: 'Man',
age: 39,
address: 'Shanghai',
},
{
id: 100022,
name: 'Test22',
nickname: 'T22',
role: 'Develop',
sex: 'Man',
age: 44,
address: 'Guangzhou',
},
];
</script> </script>
<template> <template>
<div> <div>
<p class="mb-2"> <p class="mb-2">模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单</p>
模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单 <el-card :style="elStyle" shadow="never">
</p> <template #header>
<el-card shadow="never" :style="elStyle"> <div class="card-header">
<template #header> <span>当前角色{{ username }}</span>
<div class="card-header"> </div>
<span>当前角色{{ username }}</span> </template>
</div> <el-select v-model="username" class="!w-[160px]" @change="onChange">
</template> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
<el-select v-model="username" class="!w-[160px]" @change="onChange"> </el-select>
<el-option </el-card>
v-for="item in options"
:key="item.value" <TablePlusVxeBar :column="columns" :data-list="dataList" :loading="false" />
:label="item.label" </div>
:value="item.value"
/>
</el-select>
</el-card>
</div>
</template> </template>

48
types/directives.d.ts vendored
View File

@ -1,28 +1,28 @@
import type { Directive } from "vue"; import type { Directive } from 'vue';
import type { CopyEl, OptimizeOptions, RippleOptions } from "@/directives"; import type { CopyEl, OptimizeOptions, RippleOptions } from '@/directives';
declare module "vue" { declare module 'vue' {
export interface ComponentCustomProperties { export interface ComponentCustomProperties {
/** `Loading` 动画加载指令具体看https://element-plus.org/zh-CN/component/loading.html#%E6%8C%87%E4%BB%A4 */ /** `Loading` 动画加载指令具体看https://element-plus.org/zh-CN/component/loading.html#%E6%8C%87%E4%BB%A4 */
vLoading: Directive<Element, boolean>; vLoading: Directive<Element, boolean>;
/** 按钮权限指令(根据路由`meta`中的`auths`字段进行判断)*/ /** 按钮权限指令(根据路由`meta`中的`auths`字段进行判断)*/
vAuth: Directive<HTMLElement, string | Array<string>>; vAuth: Directive<HTMLElement, string | Array<string>>;
/** 文本复制指令(默认双击复制) */ /** 文本复制指令(默认双击复制) */
vCopy: Directive<CopyEl, string>; vCopy: Directive<CopyEl, string>;
/** 长按指令 */ /** 长按指令 */
vLongpress: Directive<HTMLElement, Function>; vLongpress: Directive<HTMLElement, Function>;
/** 防抖、节流指令 */ /** 防抖、节流指令 */
vOptimize: Directive<HTMLElement, OptimizeOptions>; vOptimize: Directive<HTMLElement, OptimizeOptions>;
/** 按钮权限指令(根据登录接口返回的`permissions`字段进行判断)*/ /** 按钮权限指令(根据登录接口返回的`permissions`字段进行判断)*/
vPerms: Directive<HTMLElement, string | Array<string>>; vPerms: Directive<HTMLElement, string | Array<string>>;
/** /**
* `v-ripple` * `v-ripple`
* 1. `v-ripple``ripple` * 1. `v-ripple``ripple`
* 2. `v-ripple="{ class: 'text-red' }"``ripple``tailwindcss``color` * 2. `v-ripple="{ class: 'text-red' }"``ripple``tailwindcss``color`
* 3. `v-ripple.center` * 3. `v-ripple.center`
*/ */
vRipple: Directive<HTMLElement, RippleOptions>; vRipple: Directive<HTMLElement, RippleOptions>;
} }
} }
export {}; export {};

12
types/enum/options.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// 默认option选项
export interface Option {
value: string | number | boolean | undefined;
label: string | undefined;
}
// 属性结构
export interface TreeData {
value: string;
label: string;
children?: TreeData[];
}

347
types/global.d.ts vendored
View File

@ -1,193 +1,186 @@
import type { ECharts } from "echarts"; import type { ECharts } from 'echarts';
import type { TableColumns } from "@pureadmin/table"; import type { TableColumns } from '@pureadmin/table';
/** /**
* `.vue` `.ts` `.tsx` 使 * `.vue` `.ts` `.tsx` 使
*/ */
declare global { declare global {
/** /**
* `node``pnpm` * `node``pnpm`
*/ */
const __APP_INFO__: { const __APP_INFO__: {
pkg: { pkg: {
name: string; name: string;
version: string; version: string;
engines: { engines: {
node: string; node: string;
pnpm: string; pnpm: string;
}; };
dependencies: Recordable<string>; dependencies: Recordable<string>;
devDependencies: Recordable<string>; devDependencies: Recordable<string>;
}; };
lastBuildTime: string; lastBuildTime: string;
}; };
/** /**
* Window * Window
*/ */
interface Window { interface Window {
// Global vue app instance // Global vue app instance
__APP__: App<Element>; __APP__: App<Element>;
webkitCancelAnimationFrame: (handle: number) => void; webkitCancelAnimationFrame: (handle: number) => void;
mozCancelAnimationFrame: (handle: number) => void; mozCancelAnimationFrame: (handle: number) => void;
oCancelAnimationFrame: (handle: number) => void; oCancelAnimationFrame: (handle: number) => void;
msCancelAnimationFrame: (handle: number) => void; msCancelAnimationFrame: (handle: number) => void;
webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number; webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number;
mozRequestAnimationFrame: (callback: FrameRequestCallback) => number; mozRequestAnimationFrame: (callback: FrameRequestCallback) => number;
oRequestAnimationFrame: (callback: FrameRequestCallback) => number; oRequestAnimationFrame: (callback: FrameRequestCallback) => number;
msRequestAnimationFrame: (callback: FrameRequestCallback) => number; msRequestAnimationFrame: (callback: FrameRequestCallback) => number;
} }
/** /**
* Document * Document
*/ */
interface Document { interface Document {
webkitFullscreenElement?: Element; webkitFullscreenElement?: Element;
mozFullScreenElement?: Element; mozFullScreenElement?: Element;
msFullscreenElement?: Element; msFullscreenElement?: Element;
} }
/** /**
* *
*/ */
type ViteCompression = type ViteCompression = 'none' | 'gzip' | 'brotli' | 'both' | 'gzip-clear' | 'brotli-clear' | 'both-clear';
| "none"
| "gzip"
| "brotli"
| "both"
| "gzip-clear"
| "brotli-clear"
| "both-clear";
/** /**
* *
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE}
*/ */
interface ViteEnv { interface ViteEnv {
VITE_PORT: number; VITE_PORT: number;
VITE_PUBLIC_PATH: string; VITE_PUBLIC_PATH: string;
VITE_ROUTER_HISTORY: string; VITE_ROUTER_HISTORY: string;
VITE_CDN: boolean; VITE_CDN: boolean;
VITE_HIDE_HOME: string; VITE_HIDE_HOME: string;
VITE_COMPRESSION: ViteCompression; VITE_COMPRESSION: ViteCompression;
} }
/** /**
* `@pureadmin/table` `TableColumns` 便 * `@pureadmin/table` `TableColumns` 便
*/ */
interface TableColumnList extends Array<TableColumns> {} interface TableColumnList extends Array<TableColumns> {}
/** /**
* `public/platform-config.json` * `public/platform-config.json`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json}
*/ */
interface PlatformConfigs { interface PlatformConfigs {
Version?: string; Version?: string;
Title?: string; Title?: string;
FixedHeader?: boolean; FixedHeader?: boolean;
HiddenSideBar?: boolean; HiddenSideBar?: boolean;
MultiTagsCache?: boolean; MultiTagsCache?: boolean;
MaxTagsLevel?: number; MaxTagsLevel?: number;
KeepAlive?: boolean; KeepAlive?: boolean;
Locale?: string; Locale?: string;
Layout?: string; Layout?: string;
Theme?: string; Theme?: string;
DarkMode?: boolean; DarkMode?: boolean;
OverallStyle?: string; OverallStyle?: string;
Grey?: boolean; Grey?: boolean;
Weak?: boolean; Weak?: boolean;
HideTabs?: boolean; HideTabs?: boolean;
HideFooter?: boolean; HideFooter?: boolean;
Stretch?: boolean | number; Stretch?: boolean | number;
SidebarStatus?: boolean; SidebarStatus?: boolean;
EpThemeColor?: string; EpThemeColor?: string;
ShowLogo?: boolean; ShowLogo?: boolean;
ShowModel?: string; ShowModel?: string;
MenuArrowIconNoTransition?: boolean; MenuArrowIconNoTransition?: boolean;
CachingAsyncRoutes?: boolean; CachingAsyncRoutes?: boolean;
TooltipEffect?: Effect; TooltipEffect?: Effect;
ResponsiveStorageNameSpace?: string; ResponsiveStorageNameSpace?: string;
MenuSearchHistory?: number; MenuSearchHistory?: number;
} }
/** /**
* `PlatformConfigs` * `PlatformConfigs`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json}
*/ */
interface StorageConfigs { interface StorageConfigs {
version?: string; version?: string;
title?: string; title?: string;
fixedHeader?: boolean; fixedHeader?: boolean;
hiddenSideBar?: boolean; hiddenSideBar?: boolean;
multiTagsCache?: boolean; multiTagsCache?: boolean;
keepAlive?: boolean; keepAlive?: boolean;
locale?: string; locale?: string;
layout?: string; layout?: string;
theme?: string; theme?: string;
darkMode?: boolean; darkMode?: boolean;
grey?: boolean; grey?: boolean;
weak?: boolean; weak?: boolean;
hideTabs?: boolean; hideTabs?: boolean;
hideFooter?: boolean; hideFooter?: boolean;
sidebarStatus?: boolean; sidebarStatus?: boolean;
epThemeColor?: string; epThemeColor?: string;
themeColor?: string; themeColor?: string;
overallStyle?: string; overallStyle?: string;
showLogo?: boolean; showLogo?: boolean;
showModel?: string; showModel?: string;
menuSearchHistory?: number; menuSearchHistory?: number;
username?: string; username?: string;
} }
/** /**
* `responsive-storage` `storage` * `responsive-storage` `storage`
*/ */
interface ResponsiveStorage { interface ResponsiveStorage {
locale: { locale: {
locale?: string; locale?: string;
}; };
layout: { layout: {
layout?: string; layout?: string;
theme?: string; theme?: string;
darkMode?: boolean; darkMode?: boolean;
sidebarStatus?: boolean; sidebarStatus?: boolean;
epThemeColor?: string; epThemeColor?: string;
themeColor?: string; themeColor?: string;
overallStyle?: string; overallStyle?: string;
}; };
configure: { configure: {
grey?: boolean; grey?: boolean;
weak?: boolean; weak?: boolean;
hideTabs?: boolean; hideTabs?: boolean;
hideFooter?: boolean; hideFooter?: boolean;
showLogo?: boolean; showLogo?: boolean;
showModel?: string; showModel?: string;
multiTagsCache?: boolean; multiTagsCache?: boolean;
stretch?: boolean | number; stretch?: boolean | number;
}; };
tags?: Array<any>; tags?: Array<any>;
} }
/** /**
* 访 * 访
*/ */
interface GlobalPropertiesApi { interface GlobalPropertiesApi {
$echarts: ECharts; $echarts: ECharts;
$storage: ResponsiveStorage; $storage: ResponsiveStorage;
$config: PlatformConfigs; $config: PlatformConfigs;
} }
/** /**
* `Element` * `Element`
*/ */
interface Element { interface Element {
// v-ripple 作用于 src/directives/ripple/index.ts 文件 // v-ripple 作用于 src/directives/ripple/index.ts 文件
_ripple?: { _ripple?: {
enabled?: boolean; enabled?: boolean;
centered?: boolean; centered?: boolean;
class?: string; class?: string;
circle?: boolean; circle?: boolean;
touched?: boolean; touched?: boolean;
}; };
} }
} }

29
types/index.d.ts vendored
View File

@ -4,15 +4,14 @@ type RefType<T> = T | null;
type EmitType = (event: string, ...args: any[]) => void; type EmitType = (event: string, ...args: any[]) => void;
type TargetContext = "_self" | "_blank"; type TargetContext = '_self' | '_blank';
type ComponentRef<T extends HTMLElement = HTMLDivElement> = type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
ComponentElRef<T> | null;
type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>; type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
type ForDataType<T> = { type ForDataType<T> = {
[P in T]?: ForDataType<T[P]>; [P in T]?: ForDataType<T[P]>;
}; };
type AnyFunction<T> = (...args: any[]) => T; type AnyFunction<T> = (...args: any[]) => T;
@ -20,7 +19,7 @@ type AnyFunction<T> = (...args: any[]) => T;
type PropType<T> = VuePropType<T>; type PropType<T> = VuePropType<T>;
type Writable<T> = { type Writable<T> = {
-readonly [P in keyof T]: T[P]; -readonly [P in keyof T]: T[P];
}; };
type Nullable<T> = T | null; type Nullable<T> = T | null;
@ -30,15 +29,15 @@ type NonNullable<T> = T extends null | undefined ? never : T;
type Recordable<T = any> = Record<string, T>; type Recordable<T = any> = Record<string, T>;
type ReadonlyRecordable<T = any> = { type ReadonlyRecordable<T = any> = {
readonly [key: string]: T; readonly [key: string]: T;
}; };
type Indexable<T = any> = { type Indexable<T = any> = {
[key: string]: T; [key: string]: T;
}; };
type DeepPartial<T> = { type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>; [P in keyof T]?: DeepPartial<T[P]>;
}; };
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never }; type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
@ -49,30 +48,30 @@ type TimeoutHandle = ReturnType<typeof setTimeout>;
type IntervalHandle = ReturnType<typeof setInterval>; type IntervalHandle = ReturnType<typeof setInterval>;
type Effect = "light" | "dark"; type Effect = 'light' | 'dark';
interface ChangeEvent extends Event { interface ChangeEvent extends Event {
target: HTMLInputElement; target: HTMLInputElement;
} }
interface WheelEvent { interface WheelEvent {
path?: EventTarget[]; path?: EventTarget[];
} }
interface ImportMetaEnv extends ViteEnv { interface ImportMetaEnv extends ViteEnv {
__: unknown; __: unknown;
} }
interface Fn<T = any, R = T> { interface Fn<T = any, R = T> {
(...arg: T[]): R; (...arg: T[]): R;
} }
interface PromiseFn<T = any, R = T> { interface PromiseFn<T = any, R = T> {
(...arg: T[]): Promise<R>; (...arg: T[]): Promise<R>;
} }
interface ComponentElRef<T extends HTMLElement = HTMLDivElement> { interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
$el: T; $el: T;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars

7
types/pagination/pagination.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
// 分页参数接口
export interface Pagination {
currentPage: number;
pageSize: number;
total: number;
pageSizes: number[];
}

194
types/router.d.ts vendored
View File

@ -1,108 +1,108 @@
// 全局路由类型声明 // 全局路由类型声明
import type { RouteComponent, RouteLocationNormalized } from "vue-router"; import type { RouteComponent, RouteLocationNormalized } from 'vue-router';
import type { FunctionalComponent } from "vue"; import type { FunctionalComponent } from 'vue';
declare global { declare global {
interface ToRouteType extends RouteLocationNormalized { interface ToRouteType extends RouteLocationNormalized {
meta: CustomizeRouteMeta; meta: CustomizeRouteMeta;
} }
/** /**
* @description `meta` * @description `meta`
*/ */
interface CustomizeRouteMeta { interface CustomizeRouteMeta {
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */ /** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */
title: string; title: string;
/** 菜单图标 `可选` */ /** 菜单图标 `可选` */
icon?: string | FunctionalComponent | IconifyIcon; icon?: string | FunctionalComponent | IconifyIcon;
/** 菜单名称右侧的额外图标 */ /** 菜单名称右侧的额外图标 */
extraIcon?: string | FunctionalComponent | IconifyIcon; extraIcon?: string | FunctionalComponent | IconifyIcon;
/** 是否在菜单中显示(默认`true``可选` */ /** 是否在菜单中显示(默认`true``可选` */
showLink?: boolean; showLink?: boolean;
/** 是否显示父级菜单 `可选` */ /** 是否显示父级菜单 `可选` */
showParent?: boolean; showParent?: boolean;
/** 页面级别权限设置 `可选` */ /** 页面级别权限设置 `可选` */
roles?: Array<string>; roles?: Array<string>;
/** 按钮级别权限设置 `可选` */ /** 按钮级别权限设置 `可选` */
auths?: Array<string>; auths?: Array<string>;
/** 路由组件缓存(开启 `true`、关闭 `false``可选` */ /** 路由组件缓存(开启 `true`、关闭 `false``可选` */
keepAlive?: boolean; keepAlive?: boolean;
/** 内嵌的`iframe`链接 `可选` */ /** 内嵌的`iframe`链接 `可选` */
frameSrc?: string; frameSrc?: string;
/** `iframe`页是否开启首次加载动画(默认`true``可选` */ /** `iframe`页是否开启首次加载动画(默认`true``可选` */
frameLoading?: boolean; frameLoading?: boolean;
/** 页面加载动画(两种模式,第二种权重更高,第一种直接采用`vue`内置的`transitions`动画,第二种是使用`animate.css`编写进、离场动画,平台更推荐使用第二种模式,已经内置了`animate.css`,直接写对应的动画名即可)`可选` */ /** 页面加载动画(两种模式,第二种权重更高,第一种直接采用`vue`内置的`transitions`动画,第二种是使用`animate.css`编写进、离场动画,平台更推荐使用第二种模式,已经内置了`animate.css`,直接写对应的动画名即可)`可选` */
transition?: { transition?: {
/** /**
* @description * @description
* @see {@link https://next.router.vuejs.org/guide/advanced/transitions.html#transitions} * @see {@link https://next.router.vuejs.org/guide/advanced/transitions.html#transitions}
* @see animate.css {@link https://animate.style} * @see animate.css {@link https://animate.style}
*/ */
name?: string; name?: string;
/** 进场动画 */ /** 进场动画 */
enterTransition?: string; enterTransition?: string;
/** 离场动画 */ /** 离场动画 */
leaveTransition?: string; leaveTransition?: string;
}; };
/** 当前菜单名称或自定义信息禁止添加到标签页(默认`false` */ /** 当前菜单名称或自定义信息禁止添加到标签页(默认`false` */
hiddenTag?: boolean; hiddenTag?: boolean;
/** 当前菜单名称是否固定显示在标签页且不可关闭(默认`false` */ /** 当前菜单名称是否固定显示在标签页且不可关闭(默认`false` */
fixedTag?: boolean; fixedTag?: boolean;
/** 动态路由可打开的最大数量 `可选` */ /** 动态路由可打开的最大数量 `可选` */
dynamicLevel?: number; dynamicLevel?: number;
/** /**
* `query``params``showLink: false` * `query``params``showLink: false`
* `activePath``activePath``path` * `activePath``activePath``path`
*/ */
activePath?: string; activePath?: string;
} }
/** /**
* @description * @description
*/ */
interface RouteChildrenConfigsTable { interface RouteChildrenConfigsTable {
/** 子路由地址 `必填` */ /** 子路由地址 `必填` */
path: string; path: string;
/** 路由名字(对应不要重复,和当前组件的`name`保持一致)`必填` */ /** 路由名字(对应不要重复,和当前组件的`name`保持一致)`必填` */
name?: string; name?: string;
/** 路由重定向 `可选` */ /** 路由重定向 `可选` */
redirect?: string; redirect?: string;
/** 按需加载组件 `可选` */ /** 按需加载组件 `可选` */
component?: RouteComponent; component?: RouteComponent;
meta?: CustomizeRouteMeta; meta?: CustomizeRouteMeta;
/** 子路由配置项 */ /** 子路由配置项 */
children?: Array<RouteChildrenConfigsTable>; children?: Array<RouteChildrenConfigsTable>;
} }
/** /**
* @description * @description
*/ */
interface RouteConfigsTable { interface RouteConfigsTable {
/** 路由地址 `必填` */ /** 路由地址 `必填` */
path: string; path: string;
/** 路由名字(保持唯一)`可选` */ /** 路由名字(保持唯一)`可选` */
name?: string; name?: string;
/** `Layout`组件 `可选` */ /** `Layout`组件 `可选` */
component?: RouteComponent; component?: RouteComponent;
/** 路由重定向 `可选` */ /** 路由重定向 `可选` */
redirect?: string; redirect?: string;
meta?: { meta?: {
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */ /** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */
title: string; title: string;
/** 菜单图标 `可选` */ /** 菜单图标 `可选` */
icon?: string | FunctionalComponent | IconifyIcon; icon?: string | FunctionalComponent | IconifyIcon;
/** 是否在菜单中显示(默认`true``可选` */ /** 是否在菜单中显示(默认`true``可选` */
showLink?: boolean; showLink?: boolean;
/** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */ /** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */
rank?: number; rank?: number;
}; };
/** 子路由配置项 */ /** 子路由配置项 */
children?: Array<RouteChildrenConfigsTable>; children?: Array<RouteChildrenConfigsTable>;
} }
} }
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript // https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
declare module "vue-router" { declare module 'vue-router' {
interface RouteMeta extends CustomizeRouteMeta {} interface RouteMeta extends CustomizeRouteMeta {}
} }

39
types/shims-tsx.d.ts vendored
View File

@ -1,23 +1,26 @@
import type { VNode } from "vue"; import type Vue, { VNode } from 'vue';
import type Vue from "vue";
declare module "*.tsx" { declare module '*.tsx' {
import Vue from "compatible-vue"; import Vue from 'compatible-vue';
export default Vue; export default Vue;
} }
declare global { declare global {
namespace JSX { namespace JSX {
interface Element extends VNode {} interface Element extends VNode {}
interface ElementClass extends Vue {}
interface ElementAttributesProperty { interface ElementClass extends Vue {}
$props: any;
} interface ElementAttributesProperty {
interface IntrinsicElements { $props: any;
[elem: string]: any; }
}
interface IntrinsicAttributes { interface IntrinsicElements {
[elem: string]: any; [elem: string]: any;
} }
}
interface IntrinsicAttributes {
[elem: string]: any;
}
}
} }

14
types/shims-vue.d.ts vendored
View File

@ -1,10 +1,10 @@
declare module "*.vue" { declare module '*.vue' {
import type { DefineComponent } from "vue"; import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>; const component: DefineComponent<{}, {}, any>;
export default component; export default component;
} }
declare module "*.scss" { declare module '*.scss' {
const scss: Record<string, string>; const scss: Record<string, string>;
export default scss; export default scss;
} }

View File

@ -0,0 +1,6 @@
// 返回响应内容
export interface Result<T> {
code: number;
data: T;
message: string;
}

View File

@ -1,32 +1,31 @@
import { getPluginsList } from "./build/plugins"; import { getPluginsList } from './build/plugins';
import { exclude, include } from "./build/optimize"; import { exclude, include } from './build/optimize';
import { type ConfigEnv, loadEnv, type UserConfigExport } from "vite"; import { type ConfigEnv, loadEnv, type UserConfigExport } from 'vite';
import { __APP_INFO__, alias, root, wrapperEnv } from "./build/utils"; import { __APP_INFO__, alias, root, wrapperEnv } from './build/utils';
import { serverOptions } from "./build/server"; import { serverOptions } from './build/server';
import { buildEnvironment } from "./build/buildEnv"; import { buildEnvironment } from './build/buildEnv';
export default ({ mode }: ConfigEnv): UserConfigExport => { export default ({ mode }: ConfigEnv): UserConfigExport => {
const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } = const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } = wrapperEnv(loadEnv(mode, root));
wrapperEnv(loadEnv(mode, root)); return {
return { base: VITE_PUBLIC_PATH,
base: VITE_PUBLIC_PATH, root,
root, resolve: { alias },
resolve: { alias }, // 服务端渲染
// 服务端渲染 server: serverOptions(mode),
server: serverOptions(mode), plugins: getPluginsList(VITE_CDN, VITE_COMPRESSION, VITE_PORT),
plugins: getPluginsList(VITE_CDN, VITE_COMPRESSION, VITE_PORT), // https://cn.vitejs.dev/config/dep-optimization-options.html#dep-optimization-options
// https://cn.vitejs.dev/config/dep-optimization-options.html#dep-optimization-options optimizeDeps: { include, exclude },
optimizeDeps: { include, exclude }, esbuild: {
esbuild: { pure: ['console.log', 'debugger'],
pure: ["console.log", "debugger"], jsxFactory: 'h',
jsxFactory: "h", jsxFragment: 'Fragment',
jsxFragment: "Fragment", jsxInject: "import { h } from 'vue';",
jsxInject: "import { h } from 'vue';" },
}, build: buildEnvironment(),
build: buildEnvironment(), define: {
define: { __INTLIFY_PROD_DEVTOOLS__: false,
__INTLIFY_PROD_DEVTOOLS__: false, __APP_INFO__: JSON.stringify(__APP_INFO__),
__APP_INFO__: JSON.stringify(__APP_INFO__) },
} };
};
}; };