Compare commits

..

12 Commits

Author SHA1 Message Date
xiaoxian521
30209f62a9 release: update 3.9.7 2022-12-26 14:31:04 +08:00
xiaoxian521
1081506d10 chore: 默认关闭 CachingAsyncRoutes 动态路由缓存本地,可自行开启 2022-12-26 12:53:28 +08:00
xiaoxian521
590d6bf607 chore: update 2022-12-23 18:34:16 +08:00
xiaoxian521
3365b99d3d docs: update README.md 2022-12-21 11:37:25 +08:00
xiaoxian521
31707cebf7 chore: update 2022-12-19 14:20:56 +08:00
xiaoxian521
dac3ea3c21 release: update 3.9.6 2022-12-19 12:14:18 +08:00
xiaoxian521
01a32988c1 chore: 更新流程图业务代码 2022-12-19 12:02:32 +08:00
xiaoxian521
2e2c306097 chore: 升级vite4 2022-12-19 11:58:49 +08:00
xiaoxian521
d36d5b09c5 fix: 修复tailwind.css错误的引入方式导致vitehmr慢的问题 2022-12-19 10:42:26 +08:00
RealityBoy
923f09db5b refactor: system pages (#399)
* refactor: system pages

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update
2022-12-16 14:34:10 +08:00
xiaoxian521
66fdfebb5e chore: update @pureadmin/theme latest 2022-12-15 12:28:51 +08:00
xiaoxian521
e032f9a6a7 chore: update 2022-12-13 15:24:29 +08:00
50 changed files with 1004 additions and 858 deletions

View File

@@ -1,3 +1,26 @@
# 3.9.7 (2022-12-26)
### 🍏 Perf
- Use `path.posix.resolve` instead of `path.resolve` to avoid drive letter problems when using `electron` in `windows` environment
- By default, the `CachingAsyncRoutes` dynamic routing cache is turned off locally, making it easier to debug in the development environment. It is not necessary to clear the local cached dynamic routing every time you modify the dynamic routing. It is recommended to enable it in the production environment
# 3.9.6 (2022-12-19)
### 🎫 Chores
- Upgrade `vite4` version
### 🐞 Bug fixes
- Fix the problem that `hmr` of `vite` is slow due to the wrong way of importing `tailwind.css`
### 🍏 Perf
- Update [@pureadmin/theme](https://github.com/pure-admin/pure-admin-theme) to the latest version, bringing more friendly type hints
- Optimize [PureTableBar](https://github.com/xiaoxian521/vue-pure-admin/tree/main/src/components/RePureTableBar) component
- Optimize the business code of the system management page to bring better code reference
# 3.9.5 (2022-12-13) # 3.9.5 (2022-12-13)
### ✔️ refactor ### ✔️ refactor

View File

@@ -1,3 +1,26 @@
# 3.9.7 (2022-12-26)
### 🍏 Perf
- Use `path.posix.resolve` instead of `path.resolve` to avoid drive letter problems when using `electron` in `windows` environment
- By default, the `CachingAsyncRoutes` dynamic routing cache is turned off locally, making it easier to debug in the development environment. It is not necessary to clear the local cached dynamic routing every time you modify the dynamic routing. It is recommended to enable it in the production environment
# 3.9.6 (2022-12-19)
### 🎫 Chores
- Upgrade `vite4` version
### 🐞 Bug fixes
- Fix the problem that `hmr` of `vite` is slow due to the wrong way of importing `tailwind.css`
### 🍏 Perf
- Update [@pureadmin/theme](https://github.com/pure-admin/pure-admin-theme) to the latest version, bringing more friendly type hints
- Optimize [PureTableBar](https://github.com/xiaoxian521/vue-pure-admin/tree/main/src/components/RePureTableBar) component
- Optimize the business code of the system management page to bring better code reference
# 3.9.5 (2022-12-13) # 3.9.5 (2022-12-13)
### ✔️ refactor ### ✔️ refactor

View File

@@ -1,3 +1,26 @@
# 3.9.7 (2022-12-26)
### 🍏 Perf
- 使用 `path.posix.resolve` 替代 `path.resolve` 避免 `windows` 环境下使用 `electron` 出现盘符问题
- 默认关闭 `CachingAsyncRoutes` 动态路由缓存本地,使其在开发环境下调试更方便,不用每次修改动态路由都要先清空本地缓存的动态路由,更推荐在生产环境开启
# 3.9.6 (2022-12-19)
### 🎫 Chores
- 升级 `vite4` 版本
### 🐞 Bug fixes
- 修复 `tailwind.css` 错误的引入方式导致 `vite``hmr` 慢的问题
### 🍏 Perf
- 更新 [@pureadmin/theme](https://github.com/pure-admin/pure-admin-theme) 至最新版,带来更友好的类型提示
- 优化 [PureTableBar](https://github.com/xiaoxian521/vue-pure-admin/tree/main/src/components/RePureTableBar) 组件
- 优化系统管理页面业务代码,带来更好的代码参考
# 3.9.5 (2022-12-13) # 3.9.5 (2022-12-13)
### ✔️ refactor ### ✔️ refactor

View File

@@ -140,7 +140,7 @@ pnpm build
[xiaoxian521](https://github.com/xiaoxian521)、[Ten-K](https://github.com/Ten-K) [xiaoxian521](https://github.com/xiaoxian521)、[Ten-K](https://github.com/Ten-K)
## 捐赠 ## 支持
如果您觉得这个项目对您有帮助,可以帮作者买一杯果汁 🍹 表示支持 如果您觉得这个项目对您有帮助,可以帮作者买一杯果汁 🍹 表示支持
@@ -148,9 +148,7 @@ pnpm build
## `QQ` 交流群 ## `QQ` 交流群
一群已满,下面是二群,群里严禁`黄``赌``毒``vpn`等违法行为! [点击去加入](https://yiming_chang.gitee.io/pure-admin-doc/pages/support/#qq-%E4%BA%A4%E6%B5%81%E7%BE%A4)
<img src="https://yiming_chang.gitee.io/pure-admin-doc/img/support/qq.png" width="150px" height="225px" />
## 许可证 ## 许可证
@@ -158,7 +156,7 @@ pnpm build
[MIT © xiaoxian521-2020](./LICENSE) [MIT © xiaoxian521-2020](./LICENSE)
## 捐赠 ## 支持
非常感谢您们的支持,相信项目会越来越好 :heart: 非常感谢您们的支持,相信项目会越来越好 :heart:

View File

@@ -27,9 +27,4 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
return ret; return ret;
}; };
/** 获取环境变量 */ export { warpperEnv };
const loadEnv = (): ViteEnv => {
return import.meta.env;
};
export { warpperEnv, loadEnv };

View File

@@ -52,7 +52,6 @@ const include = [
const exclude = [ const exclude = [
"@iconify-icons/ep", "@iconify-icons/ep",
"@iconify-icons/ri", "@iconify-icons/ri",
"@iconify-icons/mdi",
"@pureadmin/theme/dist/browser-utils" "@pureadmin/theme/dist/browser-utils"
]; ];

View File

@@ -6,10 +6,10 @@ import svgLoader from "vite-svg-loader";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
import { viteMockServe } from "vite-plugin-mock"; import { viteMockServe } from "vite-plugin-mock";
import { configCompressPlugin } from "./compress"; import { configCompressPlugin } from "./compress";
import VueI18n from "@intlify/vite-plugin-vue-i18n";
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 VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
import DefineOptions from "unplugin-vue-define-options/vite"; import DefineOptions from "unplugin-vue-define-options/vite";
import { genScssMultipleScopeVars } from "../src/layout/theme"; import { genScssMultipleScopeVars } from "../src/layout/theme";
@@ -22,8 +22,7 @@ export function getPluginsList(
const lifecycle = process.env.npm_lifecycle_event; const lifecycle = process.env.npm_lifecycle_event;
return [ return [
vue(), vue(),
// https://github.com/intlify/bundle-tools/tree/main/packages/vite-plugin-vue-i18n VueI18nPlugin({
VueI18n({
runtimeOnly: true, runtimeOnly: true,
compositionOnly: true, compositionOnly: true,
include: [resolve("locales/**")] include: [resolve("locales/**")]
@@ -40,14 +39,7 @@ export function getPluginsList(
themePreprocessorPlugin({ themePreprocessorPlugin({
scss: { scss: {
multipleScopeVars: genScssMultipleScopeVars(), multipleScopeVars: genScssMultipleScopeVars(),
// 在生产模式是否抽取独立的主题css文件extract为true以下属性有效 extract: true
extract: true,
// 会选取defaultScopeName对应的主题css文件在html添加link
themeLinkTagId: "head",
// "head"||"head-prepend" || "body" ||"body-prepend"
themeLinkTagInjectTo: "head",
// 是否对抽取的css文件内对应scopeName的权重类名移除
removeCssScopeName: false
} }
}), }),
// svg组件化支持 // svg组件化支持

View File

@@ -1,6 +1,6 @@
{ {
"name": "vue-pure-admin", "name": "vue-pure-admin",
"version": "3.9.5", "version": "3.9.7",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite", "dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
@@ -47,7 +47,7 @@
"dayjs": "^1.11.6", "dayjs": "^1.11.6",
"echarts": "^5.4.0", "echarts": "^5.4.0",
"el-table-infinite-scroll": "^3.0.1", "el-table-infinite-scroll": "^3.0.1",
"element-plus": "^2.2.26", "element-plus": "^2.2.27",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"intro.js": "^6.0.0", "intro.js": "^6.0.0",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
@@ -57,7 +57,7 @@
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.0.27", "pinia": "^2.0.28",
"qrcode": "^1.5.1", "qrcode": "^1.5.1",
"qs": "^6.11.0", "qs": "^6.11.0",
"responsive-storage": "^2.1.0", "responsive-storage": "^2.1.0",
@@ -71,8 +71,8 @@
"vue-pdf-embed": "^1.1.5", "vue-pdf-embed": "^1.1.5",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue-types": "^4.2.1", "vue-types": "^4.2.1",
"vue-virtual-scroller": "^2.0.0-alpha.1", "vue-virtual-scroller": "2.0.0-beta.7",
"vue3-danmaku": "^1.1.0", "vue3-danmaku": "^1.2.0",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xgplayer": "^2.32.1", "xgplayer": "^2.32.1",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
@@ -81,11 +81,10 @@
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@iconify-icons/ep": "^1.2.7", "@iconify-icons/ep": "^1.2.7",
"@iconify-icons/mdi": "^1.2.8",
"@iconify-icons/ri": "^1.2.3", "@iconify-icons/ri": "^1.2.3",
"@iconify/vue": "^4.0.0", "@iconify/vue": "^4.0.0",
"@intlify/vite-plugin-vue-i18n": "^6.0.3", "@intlify/unplugin-vue-i18n": "^0.8.1",
"@pureadmin/theme": "^2.4.0", "@pureadmin/theme": "^3.0.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/intro.js": "^5.1.0", "@types/intro.js": "^5.1.0",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
@@ -97,8 +96,8 @@
"@types/sortablejs": "^1.15.0", "@types/sortablejs": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-vue": "^3.2.0", "@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^2.1.1", "@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2", "@vue/eslint-config-typescript": "^11.0.2",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
@@ -131,7 +130,7 @@
"terser": "^5.15.1", "terser": "^5.15.1",
"typescript": "^4.9.3", "typescript": "^4.9.3",
"unplugin-vue-define-options": "^1.0.0", "unplugin-vue-define-options": "^1.0.0",
"vite": "3.1.8", "vite": "^4.0.3",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",

732
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"Version": "3.9.5", "Version": "3.9.7",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,
@@ -17,7 +17,7 @@
"ShowLogo": true, "ShowLogo": true,
"ShowModel": "smart", "ShowModel": "smart",
"MenuArrowIconNoTransition": true, "MenuArrowIconNoTransition": true,
"CachingAsyncRoutes": true, "CachingAsyncRoutes": false,
"TooltipEffect": "light", "TooltipEffect": "light",
"MapConfigure": { "MapConfigure": {
"amapKey": "97b3248d1553172e81f168cf94ea667e", "amapKey": "97b3248d1553172e81f168cf94ea667e",

View File

@@ -10,6 +10,11 @@ type Result = {
}; };
}; };
type ResultDept = {
success: boolean;
data?: Array<any>;
};
/** 获取用户管理列表 */ /** 获取用户管理列表 */
export const getUserList = (data?: object) => { export const getUserList = (data?: object) => {
return http.request<Result>("post", "/user", { data }); return http.request<Result>("post", "/user", { data });
@@ -22,5 +27,5 @@ export const getRoleList = (data?: object) => {
/** 获取部门管理列表 */ /** 获取部门管理列表 */
export const getDeptList = (data?: object) => { export const getDeptList = (data?: object) => {
return http.request<Result>("post", "/dept", { data }); return http.request<ResultDept>("post", "/dept", { data });
}; };

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -43,7 +43,3 @@ addIcon("setting", Setting);
addIcon("dept", Dept); addIcon("dept", Dept);
addIcon("lollipop", Lollipop); addIcon("lollipop", Lollipop);
addIcon("monitor", Monitor); addIcon("monitor", Monitor);
// 非菜单图标
import RefreshRight from "@iconify-icons/ep/refresh-right";
addIcon("refreshRight", RefreshRight);

View File

@@ -0,0 +1,5 @@
import pureTableBar from "./src/bar";
import { withInstall } from "@pureadmin/utils";
/** 配合 `@pureadmin/table` 实现快速便捷的表格操作 https://github.com/xiaoxian521/pure-admin-table */
export const PureTableBar = withInstall(pureTableBar);

View File

@@ -1,54 +1,25 @@
import { defineComponent, ref, computed, PropType } from "vue"; import { delay } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme"; import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { defineComponent, ref, computed, type PropType } from "vue";
import UnExpand from "@iconify-icons/mdi/arrow-expand-right"; import ExpandIcon from "./svg/expand.svg?component";
import { IconifyIconOffline } from "../../ReIcon"; import RefreshIcon from "./svg/refresh.svg?component";
import Expand from "@iconify-icons/mdi/arrow-expand-down"; import SettingIcon from "./svg/settings.svg?component";
import ArrowCollapse from "@iconify-icons/mdi/arrow-collapse-vertical"; import CollapseIcon from "./svg/collapse.svg?component";
import Setting from "@iconify-icons/ri/settings-3-line";
export const loadingSvg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
"
style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"
/>
`;
const props = { const props = {
// 头部最左边的标题 /** 头部最左边的标题 */
title: { title: {
type: String, type: String,
default: "列表" default: "列表"
}, },
// 表格数据 /** 对于树形表格如果想启用展开和折叠功能传入当前表格的ref即可 */
dataList: {
type: Array,
default: () => {
return [];
}
},
// 对于树形表格如果想启用展开和折叠功能传入当前表格的ref即可
tableRef: { tableRef: {
type: Object as PropType<any>, type: Object as PropType<any>
default() {
return {};
}
},
// 是否显示加载动画默认false 不加载
loading: {
type: Boolean,
default: false
} }
}; };
export default defineComponent({ export default defineComponent({
name: "TableProBar", name: "PureTableBar",
props, props,
emits: ["refresh"], emits: ["refresh"],
setup(props, { emit, slots, attrs }) { setup(props, { emit, slots, attrs }) {
@@ -56,6 +27,7 @@ export default defineComponent({
const checkList = ref([]); const checkList = ref([]);
const size = ref("default"); const size = ref("default");
const isExpandAll = ref(true); const isExpandAll = ref(true);
const loading = ref(false);
const getDropdownItemStyle = computed(() => { const getDropdownItemStyle = computed(() => {
return s => { return s => {
@@ -67,9 +39,26 @@ export default defineComponent({
}; };
}); });
const iconClass = computed(() => {
return [
"text-black",
"dark:text-white",
"duration-100",
"hover:!text-primary",
"cursor-pointer",
"outline-none"
];
});
function onReFresh() {
loading.value = true;
emit("refresh");
delay(500).then(() => (loading.value = false));
}
function onExpand() { function onExpand() {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleRowExpansionAll(props.dataList, isExpandAll.value); toggleRowExpansionAll(props.tableRef.data, isExpandAll.value);
} }
function toggleRowExpansionAll(data, isExpansion) { function toggleRowExpansionAll(data, isExpansion) {
@@ -88,7 +77,7 @@ export default defineComponent({
style={getDropdownItemStyle.value("large")} style={getDropdownItemStyle.value("large")}
onClick={() => (size.value = "large")} onClick={() => (size.value = "large")}
> >
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
style={getDropdownItemStyle.value("default")} style={getDropdownItemStyle.value("default")}
@@ -108,11 +97,8 @@ export default defineComponent({
const reference = { const reference = {
reference: () => ( reference: () => (
<IconifyIconOffline <SettingIcon
class="cursor-pointer" class={["w-[16px]", iconClass.value]}
icon={Setting}
width="16"
color="text_color_regular"
onMouseover={e => (buttonRef.value = e.currentTarget)} onMouseover={e => (buttonRef.value = e.currentTarget)}
/> />
) )
@@ -120,13 +106,7 @@ export default defineComponent({
return () => ( return () => (
<> <>
<div <div {...attrs} class="w-[99/100] mt-6 p-2 bg-bg_color">
{...attrs}
class="w-[99/100] mt-6 p-2 bg-bg_color"
v-loading={props.loading}
element-loading-svg={loadingSvg}
element-loading-svg-view-box="-10, -10, 50, 50"
>
<div class="flex justify-between w-full h-[60px] p-4"> <div class="flex justify-between w-full h-[60px] p-4">
<p class="font-bold truncate">{props.title}</p> <p class="font-bold truncate">{props.title}</p>
<div class="flex items-center justify-around"> <div class="flex items-center justify-around">
@@ -138,36 +118,32 @@ export default defineComponent({
content={isExpandAll.value ? "折叠" : "展开"} content={isExpandAll.value ? "折叠" : "展开"}
placement="top" placement="top"
> >
<IconifyIconOffline <ExpandIcon
class="cursor-pointer" class={["w-[16px]", iconClass.value]}
icon={isExpandAll.value ? UnExpand : Expand} style={{
width="16" transform: isExpandAll.value ? "none" : "rotate(-90deg)"
color="text_color_regular" }}
onClick={() => onExpand()} onClick={() => onExpand()}
/> />
</el-tooltip> </el-tooltip>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
</> </>
) : undefined} ) : null}
<el-tooltip effect="dark" content="刷新" placement="top"> <el-tooltip effect="dark" content="刷新" placement="top">
<IconifyIconOffline <RefreshIcon
class="cursor-pointer" class={[
icon="refreshRight" "w-[16px]",
width="16" iconClass.value,
color="text_color_regular" loading.value ? "animate-spin" : ""
onClick={() => emit("refresh")} ]}
onClick={() => onReFresh()}
/> />
</el-tooltip> </el-tooltip>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-tooltip effect="dark" content="密度" placement="top"> <el-tooltip effect="dark" content="密度" placement="top">
<el-dropdown v-slots={dropdown} trigger="click"> <el-dropdown v-slots={dropdown} trigger="click">
<IconifyIconOffline <CollapseIcon class={["w-[16px]", iconClass.value]} />
class="cursor-pointer"
icon={ArrowCollapse}
width="16"
color="text_color_regular"
/>
</el-dropdown> </el-dropdown>
</el-tooltip> </el-tooltip>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
@@ -199,11 +175,7 @@ export default defineComponent({
content="列设置" content="列设置"
/> />
</div> </div>
{props.dataList.length > 0 ? ( {slots.default({ size: size.value, checkList: checkList.value })}
slots.default({ size: size.value, checkList: checkList.value })
) : (
<el-empty description="暂无数据" />
)}
</div> </div>
</> </>
); );

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M13.79 10.21a1 1 0 0 0 1.42 0 1 1 0 0 0 0-1.42l-2.5-2.5a1 1 0 0 0-.33-.21 1 1 0 0 0-.76 0 1 1 0 0 0-.33.21l-2.5 2.5a1 1 0 0 0 1.42 1.42l.79-.8v5.18l-.79-.8a1 1 0 0 0-1.42 1.42l2.5 2.5a1 1 0 0 0 .33.21.94.94 0 0 0 .76 0 1 1 0 0 0 .33-.21l2.5-2.5a1 1 0 0 0-1.42-1.42l-.79.8V9.41ZM7 4h10a1 1 0 0 0 0-2H7a1 1 0 0 0 0 2Zm10 16H7a1 1 0 0 0 0 2h10a1 1 0 0 0 0-2Z"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4h9Z"/></svg>

After

Width:  |  Height:  |  Size: 163 B

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 11A8.1 8.1 0 0 0 4.5 9M4 5v4h4m-4 4a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"/></svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M3.34 17a10.018 10.018 0 0 1-.978-2.326 3 3 0 0 0 .002-5.347A9.99 9.99 0 0 1 4.865 4.99a3 3 0 0 0 4.631-2.674 9.99 9.99 0 0 1 5.007.002 3 3 0 0 0 4.632 2.672A9.99 9.99 0 0 1 20.66 7c.433.749.757 1.53.978 2.326a3 3 0 0 0-.002 5.347 9.99 9.99 0 0 1-2.501 4.337 3 3 0 0 0-4.631 2.674 9.99 9.99 0 0 1-5.007-.002 3 3 0 0 0-4.632-2.672A10.018 10.018 0 0 1 3.34 17zm5.66.196a4.993 4.993 0 0 1 2.25 2.77c.499.047 1 .048 1.499.001A4.993 4.993 0 0 1 15 17.197a4.993 4.993 0 0 1 3.525-.565c.29-.408.54-.843.748-1.298A4.993 4.993 0 0 1 18 12c0-1.26.47-2.437 1.273-3.334a8.126 8.126 0 0 0-.75-1.298A4.993 4.993 0 0 1 15 6.804a4.993 4.993 0 0 1-2.25-2.77c-.499-.047-1-.048-1.499-.001A4.993 4.993 0 0 1 9 6.803a4.993 4.993 0 0 1-3.525.565 7.99 7.99 0 0 0-.748 1.298A4.993 4.993 0 0 1 6 12a4.99 4.99 0 0 1-1.273 3.334 8.126 8.126 0 0 0 .75 1.298A4.993 4.993 0 0 1 9 17.196zM12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>

After

Width:  |  Height:  |  Size: 1011 B

View File

@@ -9,9 +9,9 @@ import {
} from "vue"; } from "vue";
import "./index.scss"; import "./index.scss";
import { propTypes } from "@/utils/propTypes"; import { propTypes } from "@/utils/propTypes";
import { IconifyIconOffline } from "../../ReIcon";
import { isString, cloneDeep } from "@pureadmin/utils"; import { isString, cloneDeep } from "@pureadmin/utils";
import QRCode, { QRCodeRenderersOptions } from "qrcode"; import QRCode, { QRCodeRenderersOptions } from "qrcode";
import RefreshRight from "@iconify-icons/ep/refresh-right";
interface QrcodeLogo { interface QrcodeLogo {
src?: string; src?: string;
@@ -244,9 +244,9 @@ export default defineComponent({
onClick={disabledClick} onClick={disabledClick}
> >
<div class="absolute top-[50%] left-[50%] font-bold"> <div class="absolute top-[50%] left-[50%] font-bold">
<IconifyIconOffline <iconify-icon-offline
class="cursor-pointer" class="cursor-pointer"
icon="refreshRight" icon={RefreshRight}
width="30" width="30"
color="var(--el-color-primary)" color="var(--el-color-primary)"
/> />

View File

@@ -1,5 +0,0 @@
import tableProBar from "./src/bar";
import { withInstall } from "@pureadmin/utils";
/** table-crud组件 */
export const TableProBar = withInstall(tableProBar);

View File

@@ -1,9 +1,8 @@
import { App } from "vue"; import { App } from "vue";
import axios from "axios"; import axios from "axios";
import { loadEnv } from "@build/index";
let config: object = {}; let config: object = {};
const { VITE_PUBLIC_PATH } = loadEnv(); 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);

View File

@@ -70,14 +70,8 @@ notices.value.map(v => (noticesNum.value += v.list.length));
height: 1px; height: 1px;
} }
// 如果上面的 notices 长度大于 3 请注释掉下面代码
:deep(.el-tabs__nav-wrap) { :deep(.el-tabs__nav-wrap) {
padding: 0 36px 0 36px; padding: 0 36px 0 36px;
} }
// 如果上面的 notices 长度大于 3 请注释掉下面代码
:deep(.el-tabs__active-bar) {
margin: 0 36px 0 36px;
}
} }
</style> </style>

View File

@@ -144,7 +144,8 @@ function resolvePath(routePath) {
if (httpReg.test(routePath) || httpReg.test(props.basePath)) { if (httpReg.test(routePath) || httpReg.test(props.basePath)) {
return routePath || props.basePath; return routePath || props.basePath;
} else { } else {
return path.resolve(props.basePath, routePath); // 使用path.posix.resolve替代path.resolve 避免windows环境下使用electron出现盘符问题
return path.posix.resolve(props.basePath, routePath);
} }
} }
</script> </script>

View File

@@ -2,10 +2,7 @@
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行) * @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
*/ */
type MultipleScopeVarsItem = { import { type multipleScopeVarsOptions } from "@pureadmin/theme";
scopeName: string;
varsContent: string;
};
/** 预设主题色 */ /** 预设主题色 */
const themeColors = { const themeColors = {
@@ -113,8 +110,8 @@ const themeColors = {
/** /**
* @description 将预设主题色处理成主题插件所需格式 * @description 将预设主题色处理成主题插件所需格式
*/ */
export const genScssMultipleScopeVars = (): MultipleScopeVarsItem[] => { export const genScssMultipleScopeVars = (): multipleScopeVarsOptions[] => {
const result = [] as MultipleScopeVarsItem[]; const result = [] as multipleScopeVarsOptions[];
Object.keys(themeColors).forEach(key => { Object.keys(themeColors).forEach(key => {
result.push({ result.push({
scopeName: `layout-theme-${key}`, scopeName: `layout-theme-${key}`,
@@ -129,7 +126,7 @@ export const genScssMultipleScopeVars = (): MultipleScopeVarsItem[] => {
$menuTitleHover: ${themeColors[key].menuTitleHover} !default; $menuTitleHover: ${themeColors[key].menuTitleHover} !default;
$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default; $menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;
` `
} as MultipleScopeVarsItem); } as multipleScopeVarsOptions);
}); });
return result; return result;
}; };

View File

@@ -16,6 +16,8 @@ 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文件导致热更新慢的问题
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";

View File

@@ -8,7 +8,6 @@ import {
} from "vue-router"; } from "vue-router";
import { router } from "./index"; import { router } from "./index";
import { isProxy, toRaw } from "vue"; import { isProxy, toRaw } from "vue";
import { loadEnv } from "../../build";
import { useTimeoutFn } from "@vueuse/core"; import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "@/layout/types"; import { RouteConfigs } from "@/layout/types";
import { import {
@@ -324,7 +323,7 @@ function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */ /** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
function getHistoryMode(): RouterHistory { function getHistoryMode(): RouterHistory {
const routerHistory = loadEnv().VITE_ROUTER_HISTORY; const routerHistory = import.meta.env.VITE_ROUTER_HISTORY;
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
const historyMode = routerHistory.split(","); const historyMode = routerHistory.split(",");
const leftMode = historyMode[0]; const leftMode = historyMode[0];

View File

@@ -3,7 +3,6 @@
@import "./element-plus.scss"; @import "./element-plus.scss";
@import "./sidebar.scss"; @import "./sidebar.scss";
@import "./dark.scss"; @import "./dark.scss";
@import "./tailwind.css";
/* 自定义全局 CssVar */ /* 自定义全局 CssVar */
:root { :root {

View File

@@ -1,12 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { loadEnv } from "@build/index";
defineOptions({ defineOptions({
name: "Button" name: "Button"
}); });
const { VITE_PUBLIC_PATH } = loadEnv(); const { VITE_PUBLIC_PATH } = import.meta.env;
const url = ref(`${VITE_PUBLIC_PATH}html/button.html`); const url = ref(`${VITE_PUBLIC_PATH}html/button.html`);
</script> </script>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { initRouter } from "@/router/utils"; import { initRouter } from "@/router/utils";
import { storageSession } from "@pureadmin/utils";
import { type CSSProperties, ref, computed } from "vue"; import { type CSSProperties, ref, computed } 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";
@@ -33,7 +34,7 @@ function onChange() {
.loginByUsername({ username: username.value, password: "admin123" }) .loginByUsername({ username: username.value, password: "admin123" })
.then(res => { .then(res => {
if (res.success) { if (res.success) {
sessionStorage.removeItem("async-routes"); storageSession().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage(); usePermissionStoreHook().clearAllCachePage();
initRouter(); initRouter();
} }

View File

@@ -1,4 +1,3 @@
import { IconifyIconOffline } from "@/components/ReIcon";
import ArrowRightSLine from "@iconify-icons/ri/arrow-right-s-line"; import ArrowRightSLine from "@iconify-icons/ri/arrow-right-s-line";
import CloseCircleLine from "@iconify-icons/ri/close-circle-line"; import CloseCircleLine from "@iconify-icons/ri/close-circle-line";
@@ -8,7 +7,7 @@ export function useColumns() {
cellRenderer: () => { cellRenderer: () => {
return ( return (
<span class="flex items-center -mt-6"> <span class="flex items-center -mt-6">
<IconifyIconOffline <iconify-icon-offline
icon={CloseCircleLine} icon={CloseCircleLine}
color="#F56C6C" color="#F56C6C"
width="18px" width="18px"
@@ -21,7 +20,7 @@ export function useColumns() {
style="color: var(--el-color-primary)" style="color: var(--el-color-primary)"
> >
<IconifyIconOffline <iconify-icon-offline
icon={ArrowRightSLine} icon={ArrowRightSLine}
color="var(--el-color-primary)" color="var(--el-color-primary)"
width="18px" width="18px"
@@ -36,7 +35,7 @@ export function useColumns() {
cellRenderer: () => { cellRenderer: () => {
return ( return (
<span class="flex items-center -mt-8"> <span class="flex items-center -mt-8">
<IconifyIconOffline <iconify-icon-offline
icon={CloseCircleLine} icon={CloseCircleLine}
color="#F56C6C" color="#F56C6C"
width="18px" width="18px"
@@ -49,7 +48,7 @@ export function useColumns() {
style="color: var(--el-color-primary)" style="color: var(--el-color-primary)"
> >
<IconifyIconOffline <iconify-icon-offline
icon={ArrowRightSLine} icon={ArrowRightSLine}
color="var(--el-color-primary)" color="var(--el-color-primary)"
width="18px" width="18px"

View File

@@ -1,64 +0,0 @@
import dayjs from "dayjs";
export function useColumns() {
const columns: TableColumnList = [
{
type: "selection",
width: 55,
align: "left",
hide: ({ checkList }) => !checkList.includes("勾选列")
},
{
label: "序号",
type: "index",
width: 60,
hide: ({ checkList }) => !checkList.includes("序号列")
},
{
label: "部门名称",
prop: "name",
width: 180,
align: "left"
},
{
label: "排序",
prop: "sort",
width: 60
},
{
label: "状态",
prop: "status",
width: 80,
cellRenderer: ({ row, props }) => (
<el-tag
size={props.size}
type={row.status === 1 ? "danger" : "success"}
effect="plain"
>
{row.status === 0 ? "关闭" : "开启"}
</el-tag>
)
},
{
label: "创建时间",
width: 180,
prop: "createTime",
formatter: ({ createTime }) =>
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
},
{
label: "备注",
prop: "remark"
},
{
label: "操作",
fixed: "right",
width: 140,
slot: "operation"
}
];
return {
columns
};
}

View File

@@ -0,0 +1,114 @@
import dayjs from "dayjs";
import { handleTree } from "@/utils/tree";
import { getDeptList } from "@/api/system";
import { reactive, ref, onMounted } from "vue";
export function useDept() {
const form = reactive({
user: "",
status: ""
});
const dataList = ref([]);
const loading = ref(true);
const columns: TableColumnList = [
{
type: "selection",
width: 55,
align: "left",
hide: ({ checkList }) => !checkList.includes("勾选列")
},
{
label: "序号",
type: "index",
minWidth: 70,
hide: ({ checkList }) => !checkList.includes("序号列")
},
{
label: "部门名称",
prop: "name",
width: 180,
align: "left"
},
{
label: "排序",
prop: "sort",
minWidth: 70
},
{
label: "状态",
prop: "status",
minWidth: 100,
cellRenderer: ({ row, props }) => (
<el-tag
size={props.size}
type={row.status === 1 ? "danger" : "success"}
effect="plain"
>
{row.status === 0 ? "关闭" : "开启"}
</el-tag>
)
},
{
label: "创建时间",
minWidth: 200,
prop: "createTime",
formatter: ({ createTime }) =>
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
},
{
label: "备注",
prop: "remark",
minWidth: 200
},
{
label: "操作",
fixed: "right",
width: 160,
slot: "operation"
}
];
function handleUpdate(row) {
console.log(row);
}
function handleDelete(row) {
console.log(row);
}
function handleSelectionChange(val) {
console.log("handleSelectionChange", val);
}
function resetForm(formEl) {
if (!formEl) return;
formEl.resetFields();
onSearch();
}
async function onSearch() {
loading.value = true;
const { data } = await getDeptList();
dataList.value = handleTree(data);
setTimeout(() => {
loading.value = false;
}, 500);
}
onMounted(() => {
onSearch();
});
return {
form,
loading,
columns,
dataList,
onSearch,
resetForm,
handleUpdate,
handleDelete,
handleSelectionChange
};
}

View File

@@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumns } from "./columns"; import { ref } from "vue";
import { handleTree } from "@/utils/tree"; import { useDept } from "./hook";
import { getDeptList } from "@/api/system"; import { PureTableBar } from "@/components/RePureTableBar";
import { FormInstance } from "element-plus";
import { reactive, ref, onMounted } from "vue";
import { TableProBar } from "@/components/ReTable";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete"; import Delete from "@iconify-icons/ep/delete";
import EditPen from "@iconify-icons/ep/edit-pen"; import EditPen from "@iconify-icons/ep/edit-pen";
import Search from "@iconify-icons/ep/search"; import Search from "@iconify-icons/ep/search";
@@ -16,47 +14,19 @@ defineOptions({
name: "Dept" name: "Dept"
}); });
const form = reactive({ const formRef = ref();
user: "",
status: ""
});
const dataList = ref([]);
const loading = ref(true);
const { columns } = useColumns();
const formRef = ref<FormInstance>();
const tableRef = ref(); const tableRef = ref();
const {
function handleUpdate(row) { form,
console.log(row); loading,
} columns,
dataList,
function handleDelete(row) { onSearch,
console.log(row); resetForm,
} handleUpdate,
handleDelete,
function handleSelectionChange(val) { handleSelectionChange
console.log("handleSelectionChange", val); } = useDept();
}
async function onSearch() {
loading.value = true;
const { data } = await getDeptList();
dataList.value = handleTree(data as any);
setTimeout(() => {
loading.value = false;
}, 500);
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
onMounted(() => {
onSearch();
});
</script> </script>
<template> <template>
@@ -68,10 +38,20 @@ onMounted(() => {
class="bg-bg_color w-[99/100] pl-8 pt-4" class="bg-bg_color w-[99/100] pl-8 pt-4"
> >
<el-form-item label="部门名称:" prop="user"> <el-form-item label="部门名称:" prop="user">
<el-input v-model="form.user" placeholder="请输入部门名称" clearable /> <el-input
v-model="form.user"
placeholder="请输入部门名称"
clearable
class="!w-[200px]"
/>
</el-form-item> </el-form-item>
<el-form-item label="状态:" prop="status"> <el-form-item label="状态:" prop="status">
<el-select v-model="form.status" placeholder="请选择状态" clearable> <el-select
v-model="form.status"
placeholder="请选择状态"
clearable
class="!w-[180px]"
>
<el-option label="开启" value="1" /> <el-option label="开启" value="1" />
<el-option label="关闭" value="0" /> <el-option label="关闭" value="0" />
</el-select> </el-select>
@@ -91,11 +71,9 @@ onMounted(() => {
</el-form-item> </el-form-item>
</el-form> </el-form>
<TableProBar <PureTableBar
title="部门列表" title="部门列表"
:loading="loading"
:tableRef="tableRef?.getTableRef()" :tableRef="tableRef?.getTableRef()"
:dataList="dataList"
@refresh="onSearch" @refresh="onSearch"
> >
<template #buttons> <template #buttons>
@@ -112,6 +90,7 @@ onMounted(() => {
showOverflowTooltip showOverflowTooltip
table-layout="auto" table-layout="auto"
default-expand-all default-expand-all
:loading="loading"
:size="size" :size="size"
:data="dataList" :data="dataList"
:columns="columns" :columns="columns"
@@ -150,6 +129,6 @@ onMounted(() => {
</template> </template>
</pure-table> </pure-table>
</template> </template>
</TableProBar> </PureTableBar>
</div> </div>
</template> </template>

View File

@@ -1,11 +1,25 @@
import { ref } from "vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { getRoleList } from "@/api/system";
import { ElMessageBox } from "element-plus"; import { ElMessageBox } from "element-plus";
import { type PaginationProps } from "@pureadmin/table";
import { reactive, ref, computed, onMounted } from "vue";
export function useColumns() { export function useRole() {
const form = reactive({
name: "",
code: "",
status: ""
});
const dataList = ref([]);
const loading = ref(true);
const switchLoadMap = ref({}); const switchLoadMap = ref({});
const pagination = reactive<PaginationProps>({
total: 0,
pageSize: 10,
currentPage: 1,
background: true
});
const columns: TableColumnList = [ const columns: TableColumnList = [
{ {
type: "selection", type: "selection",
@@ -21,19 +35,23 @@ export function useColumns() {
}, },
{ {
label: "角色编号", label: "角色编号",
prop: "id" prop: "id",
minWidth: 100
}, },
{ {
label: "角色名称", label: "角色名称",
prop: "name" prop: "name",
minWidth: 120
}, },
{ {
label: "角色标识", label: "角色标识",
prop: "code" prop: "code",
minWidth: 150
}, },
{ {
label: "角色类型", label: "角色类型",
prop: "type", prop: "type",
minWidth: 150,
cellRenderer: ({ row, props }) => ( cellRenderer: ({ row, props }) => (
<el-tag <el-tag
size={props.size} size={props.size}
@@ -46,12 +64,12 @@ export function useColumns() {
}, },
{ {
label: "显示顺序", label: "显示顺序",
prop: "sort" prop: "sort",
minWidth: 100
}, },
{ {
label: "状态", label: "状态",
prop: "status", minWidth: 130,
width: 130,
cellRenderer: scope => ( cellRenderer: scope => (
<el-switch <el-switch
size={scope.props.size === "small" ? "small" : "default"} size={scope.props.size === "small" ? "small" : "default"}
@@ -68,7 +86,7 @@ export function useColumns() {
}, },
{ {
label: "创建时间", label: "创建时间",
width: 180, minWidth: 180,
prop: "createTime", prop: "createTime",
formatter: ({ createTime }) => formatter: ({ createTime }) =>
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss") dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
@@ -80,6 +98,15 @@ export function useColumns() {
slot: "operation" slot: "operation"
} }
]; ];
const buttonClass = computed(() => {
return [
"!h-[20px]",
"reset-margin",
"!text-gray-500",
"dark:!text-white",
"dark:hover:!text-primary"
];
});
function onChange({ row, index }) { function onChange({ row, index }) {
ElMessageBox.confirm( ElMessageBox.confirm(
@@ -123,7 +150,59 @@ export function useColumns() {
}); });
} }
function handleUpdate(row) {
console.log(row);
}
function handleDelete(row) {
console.log(row);
}
function handleSizeChange(val: number) {
console.log(`${val} items per page`);
}
function handleCurrentChange(val: number) {
console.log(`current page: ${val}`);
}
function handleSelectionChange(val) {
console.log("handleSelectionChange", val);
}
async function onSearch() {
loading.value = true;
const { data } = await getRoleList();
dataList.value = data.list;
pagination.total = data.total;
setTimeout(() => {
loading.value = false;
}, 500);
}
const resetForm = formEl => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
onMounted(() => {
onSearch();
});
return { return {
columns form,
loading,
columns,
dataList,
pagination,
buttonClass,
onSearch,
resetForm,
handleUpdate,
handleDelete,
handleSizeChange,
handleCurrentChange,
handleSelectionChange
}; };
} }

View File

@@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumns } from "./columns"; import { ref } from "vue";
import { getRoleList } from "@/api/system"; import { useRole } from "./hook";
import { reactive, ref, onMounted } from "vue"; import { PureTableBar } from "@/components/RePureTableBar";
import { type FormInstance } from "element-plus";
import { TableProBar } from "@/components/ReTable";
import { type PaginationProps } from "@pureadmin/table";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Database from "@iconify-icons/ri/database-2-line"; import Database from "@iconify-icons/ri/database-2-line";
import More from "@iconify-icons/ep/more-filled"; import More from "@iconify-icons/ep/more-filled";
import Delete from "@iconify-icons/ep/delete"; import Delete from "@iconify-icons/ep/delete";
@@ -19,64 +17,22 @@ defineOptions({
name: "Role" name: "Role"
}); });
const form = reactive({ const formRef = ref();
name: "", const {
code: "", form,
status: "" loading,
}); columns,
dataList,
const dataList = ref([]); pagination,
const loading = ref(true); buttonClass,
const { columns } = useColumns(); onSearch,
resetForm,
const formRef = ref<FormInstance>(); handleUpdate,
handleDelete,
const pagination = reactive<PaginationProps>({ handleSizeChange,
total: 0, handleCurrentChange,
pageSize: 10, handleSelectionChange
currentPage: 1, } = useRole();
background: true
});
function handleUpdate(row) {
console.log(row);
}
function handleDelete(row) {
console.log(row);
}
function handleCurrentChange(val: number) {
console.log(`current page: ${val}`);
}
function handleSizeChange(val: number) {
console.log(`${val} items per page`);
}
function handleSelectionChange(val) {
console.log("handleSelectionChange", val);
}
async function onSearch() {
loading.value = true;
const { data } = await getRoleList();
dataList.value = data.list;
pagination.total = data.total;
setTimeout(() => {
loading.value = false;
}, 500);
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
onMounted(() => {
onSearch();
});
</script> </script>
<template> <template>
@@ -88,13 +44,28 @@ onMounted(() => {
class="bg-bg_color w-[99/100] pl-8 pt-4" class="bg-bg_color w-[99/100] pl-8 pt-4"
> >
<el-form-item label="角色名称:" prop="name"> <el-form-item label="角色名称:" prop="name">
<el-input v-model="form.name" placeholder="请输入角色名称" clearable /> <el-input
v-model="form.name"
placeholder="请输入角色名称"
clearable
class="!w-[200px]"
/>
</el-form-item> </el-form-item>
<el-form-item label="角色标识:" prop="code"> <el-form-item label="角色标识:" prop="code">
<el-input v-model="form.code" placeholder="请输入角色标识" clearable /> <el-input
v-model="form.code"
placeholder="请输入角色标识"
clearable
class="!w-[180px]"
/>
</el-form-item> </el-form-item>
<el-form-item label="状态:" prop="status"> <el-form-item label="状态:" prop="status">
<el-select v-model="form.status" placeholder="请选择状态" clearable> <el-select
v-model="form.status"
placeholder="请选择状态"
clearable
class="!w-[180px]"
>
<el-option label="已开启" value="1" /> <el-option label="已开启" value="1" />
<el-option label="已关闭" value="0" /> <el-option label="已关闭" value="0" />
</el-select> </el-select>
@@ -114,12 +85,7 @@ onMounted(() => {
</el-form-item> </el-form-item>
</el-form> </el-form>
<TableProBar <PureTableBar title="角色列表" @refresh="onSearch">
title="角色列表"
:loading="loading"
:dataList="dataList"
@refresh="onSearch"
>
<template #buttons> <template #buttons>
<el-button type="primary" :icon="useRenderIcon(AddFill)"> <el-button type="primary" :icon="useRenderIcon(AddFill)">
新增角色 新增角色
@@ -131,6 +97,7 @@ onMounted(() => {
align-whole="center" align-whole="center"
showOverflowTooltip showOverflowTooltip
table-layout="auto" table-layout="auto"
:loading="loading"
:size="size" :size="size"
:data="dataList" :data="dataList"
:columns="columns" :columns="columns"
@@ -151,8 +118,8 @@ onMounted(() => {
link link
type="primary" type="primary"
:size="size" :size="size"
@click="handleUpdate(row)"
:icon="useRenderIcon(EditPen)" :icon="useRenderIcon(EditPen)"
@click="handleUpdate(row)"
> >
修改 修改
</el-button> </el-button>
@@ -172,18 +139,18 @@ onMounted(() => {
</el-popconfirm> </el-popconfirm>
<el-dropdown> <el-dropdown>
<el-button <el-button
class="ml-3" class="ml-3 mt-[2px]"
link link
type="primary" type="primary"
:size="size" :size="size"
@click="handleUpdate(row)"
:icon="useRenderIcon(More)" :icon="useRenderIcon(More)"
@click="handleUpdate(row)"
/> />
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link link
type="primary" type="primary"
:size="size" :size="size"
@@ -194,7 +161,7 @@ onMounted(() => {
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link link
type="primary" type="primary"
:size="size" :size="size"
@@ -209,7 +176,7 @@ onMounted(() => {
</template> </template>
</pure-table> </pure-table>
</template> </template>
</TableProBar> </PureTableBar>
</div> </div>
</template> </template>

View File

@@ -1,11 +1,25 @@
import { ref } from "vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { getUserList } from "@/api/system";
import { ElMessageBox } from "element-plus"; import { ElMessageBox } from "element-plus";
import { type PaginationProps } from "@pureadmin/table";
import { reactive, ref, computed, onMounted } from "vue";
export function useColumns() { export function useUser() {
const form = reactive({
username: "",
mobile: "",
status: ""
});
const dataList = ref([]);
const loading = ref(true);
const switchLoadMap = ref({}); const switchLoadMap = ref({});
const pagination = reactive<PaginationProps>({
total: 0,
pageSize: 10,
currentPage: 1,
background: true
});
const columns: TableColumnList = [ const columns: TableColumnList = [
{ {
type: "selection", type: "selection",
@@ -21,19 +35,23 @@ export function useColumns() {
}, },
{ {
label: "用户编号", label: "用户编号",
prop: "id" prop: "id",
minWidth: 130
}, },
{ {
label: "用户名称", label: "用户名称",
prop: "username" prop: "username",
minWidth: 130
}, },
{ {
label: "用户昵称", label: "用户昵称",
prop: "nickname" prop: "nickname",
minWidth: 130
}, },
{ {
label: "性别", label: "性别",
prop: "sex", prop: "sex",
minWidth: 90,
cellRenderer: ({ row, props }) => ( cellRenderer: ({ row, props }) => (
<el-tag <el-tag
size={props.size} size={props.size}
@@ -47,16 +65,18 @@ export function useColumns() {
{ {
label: "部门", label: "部门",
prop: "dept", prop: "dept",
minWidth: 90,
formatter: ({ dept }) => dept.name formatter: ({ dept }) => dept.name
}, },
{ {
label: "手机号码", label: "手机号码",
prop: "mobile" prop: "mobile",
minWidth: 90
}, },
{ {
label: "状态", label: "状态",
prop: "status", prop: "status",
width: 130, minWidth: 90,
cellRenderer: scope => ( cellRenderer: scope => (
<el-switch <el-switch
size={scope.props.size === "small" ? "small" : "default"} size={scope.props.size === "small" ? "small" : "default"}
@@ -73,7 +93,7 @@ export function useColumns() {
}, },
{ {
label: "创建时间", label: "创建时间",
width: 180, minWidth: 90,
prop: "createTime", prop: "createTime",
formatter: ({ createTime }) => formatter: ({ createTime }) =>
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss") dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
@@ -85,6 +105,15 @@ export function useColumns() {
slot: "operation" slot: "operation"
} }
]; ];
const buttonClass = computed(() => {
return [
"!h-[20px]",
"reset-margin",
"!text-gray-500",
"dark:!text-white",
"dark:hover:!text-primary"
];
});
function onChange({ row, index }) { function onChange({ row, index }) {
ElMessageBox.confirm( ElMessageBox.confirm(
@@ -128,7 +157,59 @@ export function useColumns() {
}); });
} }
function handleUpdate(row) {
console.log(row);
}
function handleDelete(row) {
console.log(row);
}
function handleSizeChange(val: number) {
console.log(`${val} items per page`);
}
function handleCurrentChange(val: number) {
console.log(`current page: ${val}`);
}
function handleSelectionChange(val) {
console.log("handleSelectionChange", val);
}
async function onSearch() {
loading.value = true;
const { data } = await getUserList();
dataList.value = data.list;
pagination.total = data.total;
setTimeout(() => {
loading.value = false;
}, 500);
}
const resetForm = formEl => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
onMounted(() => {
onSearch();
});
return { return {
columns form,
loading,
columns,
dataList,
pagination,
buttonClass,
onSearch,
resetForm,
handleUpdate,
handleDelete,
handleSizeChange,
handleCurrentChange,
handleSelectionChange
}; };
} }

View File

@@ -1,11 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import tree from "./tree.vue"; import tree from "./tree.vue";
import { useColumns } from "./columns"; import { useUser } from "./hook";
import { getUserList } from "@/api/system"; import { PureTableBar } from "@/components/RePureTableBar";
import { reactive, ref, onMounted } from "vue";
import { type FormInstance } from "element-plus";
import { TableProBar } from "@/components/ReTable";
import { type PaginationProps } from "@pureadmin/table";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Role from "@iconify-icons/ri/admin-line"; import Role from "@iconify-icons/ri/admin-line";
@@ -21,69 +18,28 @@ defineOptions({
name: "User" name: "User"
}); });
const form = reactive({ const formRef = ref();
username: "", const {
mobile: "", form,
status: "" loading,
}); columns,
const dataList = ref([]); dataList,
const loading = ref(true); pagination,
const { columns } = useColumns(); buttonClass,
onSearch,
const formRef = ref<FormInstance>(); resetForm,
handleUpdate,
const pagination = reactive<PaginationProps>({ handleDelete,
total: 0, handleSizeChange,
pageSize: 10, handleCurrentChange,
currentPage: 1, handleSelectionChange
background: true } = useUser();
});
function handleUpdate(row) {
console.log(row);
}
function handleDelete(row) {
console.log(row);
}
function handleCurrentChange(val: number) {
console.log(`current page: ${val}`);
}
function handleSizeChange(val: number) {
console.log(`${val} items per page`);
}
function handleSelectionChange(val) {
console.log("handleSelectionChange", val);
}
async function onSearch() {
loading.value = true;
const { data } = await getUserList();
dataList.value = data.list;
pagination.total = data.total;
setTimeout(() => {
loading.value = false;
}, 500);
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
onMounted(() => {
onSearch();
});
</script> </script>
<template> <template>
<div class="main flex"> <div class="main">
<tree /> <tree class="w-[17%] float-left" />
<div class="flex-1 ml-4"> <div class="float-right w-[81%]">
<el-form <el-form
ref="formRef" ref="formRef"
:inline="true" :inline="true"
@@ -95,6 +51,7 @@ onMounted(() => {
v-model="form.username" v-model="form.username"
placeholder="请输入用户名称" placeholder="请输入用户名称"
clearable clearable
class="!w-[160px]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="手机号码:" prop="mobile"> <el-form-item label="手机号码:" prop="mobile">
@@ -102,10 +59,16 @@ onMounted(() => {
v-model="form.mobile" v-model="form.mobile"
placeholder="请输入手机号码" placeholder="请输入手机号码"
clearable clearable
class="!w-[160px]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="状态:" prop="status"> <el-form-item label="状态:" prop="status">
<el-select v-model="form.status" placeholder="请选择" clearable> <el-select
v-model="form.status"
placeholder="请选择"
clearable
class="!w-[160px]"
>
<el-option label="已开启" value="1" /> <el-option label="已开启" value="1" />
<el-option label="已关闭" value="0" /> <el-option label="已关闭" value="0" />
</el-select> </el-select>
@@ -125,12 +88,7 @@ onMounted(() => {
</el-form-item> </el-form-item>
</el-form> </el-form>
<TableProBar <PureTableBar title="用户管理" @refresh="onSearch">
title="用户管理"
:loading="loading"
:dataList="dataList"
@refresh="onSearch"
>
<template #buttons> <template #buttons>
<el-button type="primary" :icon="useRenderIcon(AddFill)"> <el-button type="primary" :icon="useRenderIcon(AddFill)">
新增用户 新增用户
@@ -141,6 +99,7 @@ onMounted(() => {
border border
align-whole="center" align-whole="center"
table-layout="auto" table-layout="auto"
:loading="loading"
:size="size" :size="size"
:data="dataList" :data="dataList"
:columns="columns" :columns="columns"
@@ -182,7 +141,7 @@ onMounted(() => {
</el-popconfirm> </el-popconfirm>
<el-dropdown> <el-dropdown>
<el-button <el-button
class="ml-3" class="ml-3 mt-[2px]"
link link
type="primary" type="primary"
:size="size" :size="size"
@@ -193,7 +152,7 @@ onMounted(() => {
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link link
type="primary" type="primary"
:size="size" :size="size"
@@ -204,7 +163,7 @@ onMounted(() => {
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link link
type="primary" type="primary"
:size="size" :size="size"
@@ -219,7 +178,7 @@ onMounted(() => {
</template> </template>
</pure-table> </pure-table>
</template> </template>
</TableProBar> </PureTableBar>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4h9Z"/></svg>

After

Width:  |  Height:  |  Size: 163 B

View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M4 2H2v20h2v-9h14.17l-5.5 5.5l1.41 1.42L22 12l-7.92-7.92l-1.41 1.42l5.5 5.5H4V2Z"/></svg>

After

Width:  |  Height:  |  Size: 166 B

View File

@@ -1,18 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { handleTree } from "@/utils/tree"; import { handleTree } from "@/utils/tree";
import type { ElTree } from "element-plus";
import { getDeptList } from "@/api/system"; import { getDeptList } from "@/api/system";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, watch, onMounted, getCurrentInstance } from "vue"; import { ref, computed, watch, onMounted, getCurrentInstance } from "vue";
import LocationCompany from "@iconify-icons/ep/add-location";
import UnExpand from "@iconify-icons/mdi/arrow-expand-right";
import Expand from "@iconify-icons/mdi/arrow-expand-down";
import More2Fill from "@iconify-icons/ri/more-2-fill";
import Reset from "@iconify-icons/ri/restart-line";
import Dept from "@iconify-icons/ri/git-branch-line"; import Dept from "@iconify-icons/ri/git-branch-line";
import OfficeBuilding from "@iconify-icons/ep/office-building"; import Reset from "@iconify-icons/ri/restart-line";
import Search from "@iconify-icons/ep/search"; import Search from "@iconify-icons/ep/search";
import More2Fill from "@iconify-icons/ri/more-2-fill";
import OfficeBuilding from "@iconify-icons/ep/office-building";
import LocationCompany from "@iconify-icons/ep/add-location";
import ExpandIcon from "./svg/expand.svg?component";
import UnExpandIcon from "./svg/unexpand.svg?component";
interface Tree { interface Tree {
id: number; id: number;
@@ -20,17 +19,26 @@ interface Tree {
highlight?: boolean; highlight?: boolean;
children?: Tree[]; children?: Tree[];
} }
const treeRef = ref();
const treeData = ref([]);
const isExpand = ref(true);
const searchValue = ref("");
const highlightMap = ref({});
const { proxy } = getCurrentInstance();
const defaultProps = { const defaultProps = {
children: "children", children: "children",
label: "name" label: "name"
}; };
const buttonClass = computed(() => {
const treeData = ref([]); return [
const searchValue = ref(""); "!h-[20px]",
const { proxy } = getCurrentInstance(); "reset-margin",
const treeRef = ref<InstanceType<typeof ElTree>>(); "!text-gray-500",
"dark:!text-white",
const highlightMap = ref({}); "dark:hover:!text-primary"
];
});
const filterNode = (value: string, data: Tree) => { const filterNode = (value: string, data: Tree) => {
if (!value) return true; if (!value) return true;
@@ -54,13 +62,14 @@ function nodeClick(value) {
} }
function toggleRowExpansionAll(status) { function toggleRowExpansionAll(status) {
isExpand.value = status;
const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes(); const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
nodes[i].expanded = status; nodes[i].expanded = status;
} }
} }
// 重置状态(选中状态、搜索框值、树初始化) /** 重置状态(选中状态、搜索框值、树初始化) */
function onReset() { function onReset() {
highlightMap.value = {}; highlightMap.value = {};
searchValue.value = ""; searchValue.value = "";
@@ -73,12 +82,12 @@ watch(searchValue, val => {
onMounted(async () => { onMounted(async () => {
const { data } = await getDeptList(); const { data } = await getDeptList();
treeData.value = handleTree(data as any); treeData.value = handleTree(data);
}); });
</script> </script>
<template> <template>
<div class="max-w-[260px] h-full min-h-[780px] bg-bg_color"> <div class="h-full min-h-[780px] bg-bg_color overflow-auto">
<div class="flex items-center h-[34px]"> <div class="flex items-center h-[34px]">
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表"> <p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
部门列表 部门列表
@@ -99,7 +108,7 @@ onMounted(async () => {
</el-icon> </el-icon>
</template> </template>
</el-input> </el-input>
<el-dropdown> <el-dropdown :hide-on-click="false">
<IconifyIconOffline <IconifyIconOffline
class="w-[28px] cursor-pointer" class="w-[28px] cursor-pointer"
width="18px" width="18px"
@@ -109,29 +118,18 @@ onMounted(async () => {
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link link
type="primary" type="primary"
:icon="useRenderIcon(Expand)" :icon="useRenderIcon(isExpand ? ExpandIcon : UnExpandIcon)"
@click="toggleRowExpansionAll(true)" @click="toggleRowExpansionAll(isExpand ? false : true)"
> >
展开全部 {{ isExpand ? "折叠全部" : "展开全部" }}
</el-button> </el-button>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item> <el-dropdown-item>
<el-button <el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary" :class="buttonClass"
link
type="primary"
:icon="useRenderIcon(UnExpand)"
@click="toggleRowExpansionAll(false)"
>
折叠全部
</el-button>
</el-dropdown-item>
<el-dropdown-item>
<el-button
class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary"
link link
type="primary" type="primary"
:icon="useRenderIcon(Reset)" :icon="useRenderIcon(Reset)"

View File

@@ -1,4 +1,3 @@
import { IconifyIconOffline } from "@/components/ReIcon";
import TypeIt from "@/components/ReTypeit"; import TypeIt from "@/components/ReTypeit";
import OfficeBuilding from "@iconify-icons/ep/office-building"; import OfficeBuilding from "@iconify-icons/ep/office-building";
import Tickets from "@iconify-icons/ep/tickets"; import Tickets from "@iconify-icons/ep/tickets";
@@ -21,7 +20,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={User} /> <iconify-icon-offline icon={User} />
</el-icon> </el-icon>
</div> </div>
@@ -32,7 +31,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={Iphone} /> <iconify-icon-offline icon={Iphone} />
</el-icon> </el-icon>
</div> </div>
@@ -43,7 +42,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={Location} /> <iconify-icon-offline icon={Location} />
</el-icon> </el-icon>
</div> </div>
@@ -57,7 +56,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={Tickets} /> <iconify-icon-offline icon={Tickets} />
</el-icon> </el-icon>
</div> </div>
@@ -76,7 +75,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={OfficeBuilding} /> <iconify-icon-offline icon={OfficeBuilding} />
</el-icon> </el-icon>
</div> </div>
@@ -90,7 +89,7 @@ export function useColumns() {
labelRenderer: () => ( labelRenderer: () => (
<div class="flex items-center"> <div class="flex items-center">
<el-icon> <el-icon>
<IconifyIconOffline icon={Notebook} /> <iconify-icon-offline icon={Notebook} />
</el-icon> </el-icon>
</div> </div>

View File

@@ -9,7 +9,5 @@ declare module "*.scss" {
export default scss; export default scss;
} }
declare module "@pureadmin/theme";
declare module "vue-virtual-scroller"; declare module "vue-virtual-scroller";
declare module "vuedraggable/src/vuedraggable"; declare module "vuedraggable/src/vuedraggable";
declare module "@pureadmin/theme/dist/browser-utils";