feat: 状态展示统一为圆点
- 新增状态圆点组件,用户管理、角色与字典的状态改用圆点 - 用户管理操作列放宽到 200,登录页移动端版本号紧随项目名 - 应用中心分组按内容高度排列 - 函数注释统一为展开式多行 - 格式校验脚本忽略不存在的目录
This commit is contained in:
@@ -36,7 +36,9 @@ const tableList = [
|
||||
},
|
||||
];
|
||||
|
||||
/** 下划线命名转驼峰首字母大写,如 sys_user → SysUser */
|
||||
/**
|
||||
* 下划线命名转驼峰首字母大写,如 sys_user → SysUser
|
||||
*/
|
||||
function toPascalCase(name: string): string {
|
||||
return name
|
||||
.split("_")
|
||||
@@ -196,7 +198,9 @@ const genConfigMap: Record<string, any> = {
|
||||
},
|
||||
};
|
||||
|
||||
/** 未配置表的默认生成配置(无 id,进入抽屉后从基础配置步骤开始) */
|
||||
/**
|
||||
* 未配置表的默认生成配置(无 id,进入抽屉后从基础配置步骤开始)
|
||||
*/
|
||||
function buildDefaultConfig(tableName: string) {
|
||||
return {
|
||||
tableName,
|
||||
@@ -214,7 +218,9 @@ function buildDefaultConfig(tableName: string) {
|
||||
};
|
||||
}
|
||||
|
||||
/** 构造代码生成预览文件列表 */
|
||||
/**
|
||||
* 构造代码生成预览文件列表
|
||||
*/
|
||||
function buildPreviewFiles(tableName: string, tableComment: string) {
|
||||
const bizName = tableName.replace(/^sys_/, "");
|
||||
const entityName = toPascalCase(bizName);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"lint:lint-staged": "lint-staged",
|
||||
"lint": "pnpm run lint:eslint && pnpm run lint:prettier && pnpm run lint:stylelint",
|
||||
"lint:check": "eslint src mock vite.config.ts uno.config.ts eslint.config.ts",
|
||||
"format:check": "prettier --check \"src/**/*.{ts,vue,scss,css,json}\" \"mock/**/*.ts\" \"*.{ts,cjs,mjs,json,yaml}\" \".github/workflows/*.{yml,yaml}\"",
|
||||
"format:check": "prettier --check --no-error-on-unmatched-pattern \"src/**/*.{ts,vue,scss,css,json}\" \"mock/**/*.ts\" \"*.{ts,cjs,mjs,json,yaml}\" \".github/workflows/*.{yml,yaml}\"",
|
||||
"style:check": "stylelint \"src/**/*.{css,scss,vue}\"",
|
||||
"check": "pnpm run type-check && pnpm run lint:check && pnpm run format:check && pnpm run style:check",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
27
src/components/StatusDot/index.vue
Normal file
27
src/components/StatusDot/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<!-- 状态圆点:启用为成功色,停用为占位色 -->
|
||||
<template>
|
||||
<span class="status-dot" :class="{ 'is-stopped': !active }" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: "StatusDot" });
|
||||
|
||||
/**
|
||||
* 是否处于启用状态
|
||||
*/
|
||||
defineProps<{ active: boolean }>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: var(--el-color-success);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-dot.is-stopped {
|
||||
background-color: var(--el-text-color-placeholder);
|
||||
}
|
||||
</style>
|
||||
@@ -161,7 +161,9 @@ export interface ISelectConfig<T = any> {
|
||||
placeholder?: string;
|
||||
/** popover 组件属性 */
|
||||
popover?: Partial<Omit<PopoverProps, "visible" | "v-model:visible">>;
|
||||
/** 列表的网络请求函数(需返回 Promise) */
|
||||
/**
|
||||
* 列表的网络请求函数(需返回 Promise)
|
||||
*/
|
||||
indexAction: (_queryParams: T) => Promise<any>;
|
||||
/** 主键(跨页选择必填,默认为 id) */
|
||||
pk?: string;
|
||||
|
||||
@@ -78,7 +78,9 @@ export function usePageTable<T, Q extends PageQuery = PageQuery>(
|
||||
export interface UsePageTableOptions<T, Q extends PageQuery> {
|
||||
/** 初值会深复制,请求和编辑不会改变重置基准。 */
|
||||
initialParams: Q;
|
||||
/** 接收查询快照;请求异常由调用方处理。 */
|
||||
/**
|
||||
* 接收查询快照;请求异常由调用方处理。
|
||||
*/
|
||||
request: (params: Q) => Promise<PageResult<T>>;
|
||||
/** 恢复参数前,同步重置页面自有的搜索控件。 */
|
||||
onBeforeReset?: () => void;
|
||||
@@ -93,10 +95,16 @@ export interface UsePageTableReturn<T, Q extends PageQuery> {
|
||||
total: Ref<number>;
|
||||
params: Q;
|
||||
fetchData: () => Promise<void>;
|
||||
/** 回到第一页并查询。 */
|
||||
/**
|
||||
* 回到第一页并查询。
|
||||
*/
|
||||
handleQuery: () => Promise<void>;
|
||||
/** 恢复初始条件后重新查询。 */
|
||||
/**
|
||||
* 恢复初始条件后重新查询。
|
||||
*/
|
||||
handleResetQuery: () => Promise<void>;
|
||||
/** 仅恢复参数,不发请求。 */
|
||||
/**
|
||||
* 仅恢复参数,不发请求。
|
||||
*/
|
||||
resetParams: () => void;
|
||||
}
|
||||
|
||||
@@ -721,11 +721,17 @@ const xModalEvent: VxeModalListeners = {
|
||||
const crudStore = {
|
||||
/** 表单类型,true 表示修改,false 表示新增 */
|
||||
isUpdate: true,
|
||||
/** 加载表格数据 */
|
||||
/**
|
||||
* 加载表格数据
|
||||
*/
|
||||
commitQuery: () => xGridDom.value?.commitProxy("query"),
|
||||
/** 清空表格数据 */
|
||||
/**
|
||||
* 清空表格数据
|
||||
*/
|
||||
clearTable: () => xGridDom.value?.reloadData([]),
|
||||
/** 新增后是否跳入最后一页 */
|
||||
/**
|
||||
* 新增后是否跳入最后一页
|
||||
*/
|
||||
afterInsert: () => {
|
||||
const pager = xGridDom.value?.getProxyInfo()?.pager;
|
||||
if (pager) {
|
||||
@@ -735,7 +741,9 @@ const crudStore = {
|
||||
}
|
||||
}
|
||||
},
|
||||
/** 删除后是否返回上一页 */
|
||||
/**
|
||||
* 删除后是否返回上一页
|
||||
*/
|
||||
afterDelete: () => {
|
||||
const tableData: RowMeta[] = xGridDom.value!.getData();
|
||||
const pager = xGridDom.value?.getProxyInfo()?.pager;
|
||||
@@ -743,7 +751,9 @@ const crudStore = {
|
||||
--pager.currentPage;
|
||||
}
|
||||
},
|
||||
/** 点击显示弹窗 */
|
||||
/**
|
||||
* 点击显示弹窗
|
||||
*/
|
||||
onShowModal: (row?: RowMeta) => {
|
||||
if (row) {
|
||||
crudStore.isUpdate = true;
|
||||
@@ -755,7 +765,9 @@ const crudStore = {
|
||||
}
|
||||
xModalDom.value?.open();
|
||||
},
|
||||
/** 确定并保存 */
|
||||
/**
|
||||
* 确定并保存
|
||||
*/
|
||||
onSubmitForm: () => {
|
||||
if (xModalOpt.loading) return;
|
||||
// 表单校验
|
||||
@@ -775,7 +787,9 @@ const crudStore = {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
/** 删除 */
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
onDelete: (row?: RowMeta) => {
|
||||
let ids = [];
|
||||
if (row === undefined) {
|
||||
@@ -808,7 +822,9 @@ const crudStore = {
|
||||
onModify: (id: number, field: string, value: any) => {
|
||||
console.debug("🚀 ~ onModify", { id, field, value });
|
||||
},
|
||||
/** 更多自定义方法 */
|
||||
/**
|
||||
* 更多自定义方法
|
||||
*/
|
||||
moreFn: () => {},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -954,6 +954,18 @@ $input-h: 44px;
|
||||
|
||||
.login-brand {
|
||||
padding: 24px 24px 28px;
|
||||
|
||||
/* 版本号移到名称下方,避免与右上角工具栏图标重叠 */
|
||||
&__identity {
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.login-card {
|
||||
|
||||
@@ -61,9 +61,10 @@
|
||||
<el-table-column label="排序" prop="sort" align="center" width="80" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === CommonStatus.ENABLED ? 'success' : 'info'">
|
||||
<span class="dict-item-panel__status">
|
||||
<StatusDot :active="scope.row.status === CommonStatus.ENABLED" />
|
||||
{{ scope.row.status === CommonStatus.ENABLED ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -172,6 +173,7 @@ import { FullScreen, QuestionFilled, Refresh } from "@element-plus/icons-vue";
|
||||
import DictAPI from "@/api/system/dict";
|
||||
import type { DictItem, DictItemForm, DictItemQuery } from "@/api/system/dict";
|
||||
import { usePageTable, useTableSelection } from "@/composables";
|
||||
import StatusDot from "@/components/StatusDot/index.vue";
|
||||
import { CommonStatus } from "@/enums";
|
||||
import { useDictStoreHook } from "@/stores/dict";
|
||||
|
||||
@@ -378,4 +380,10 @@ onMounted(() => {
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
|
||||
.dict-item-panel__status {
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
@click="handleTypeClick(row)"
|
||||
>
|
||||
<div class="dict-type__info">
|
||||
<span class="dict-type__name" :title="row.name">{{ row.name }}</span>
|
||||
<span class="dict-type__name" :title="row.name">
|
||||
<StatusDot :active="row.status === CommonStatus.ENABLED" />
|
||||
{{ row.name }}
|
||||
</span>
|
||||
<span class="dict-type__code" :title="row.dictCode">{{ row.dictCode }}</span>
|
||||
</div>
|
||||
<div class="dict-type__actions">
|
||||
@@ -143,6 +146,7 @@ import { ArrowLeft, ArrowRight, Delete, Edit, Plus } from "@element-plus/icons-v
|
||||
import DictAPI from "@/api/system/dict";
|
||||
import type { DictTypeForm, DictTypeItem, DictTypeQuery } from "@/api/system/dict";
|
||||
import { usePageTable } from "@/composables";
|
||||
import StatusDot from "@/components/StatusDot/index.vue";
|
||||
import { CommonStatus } from "@/enums";
|
||||
import DictItemPanel from "./components/DictItemPanel.vue";
|
||||
|
||||
@@ -376,6 +380,20 @@ onMounted(() => {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-right: 6px;
|
||||
vertical-align: middle;
|
||||
background-color: var(--el-color-success);
|
||||
border-radius: 50%;
|
||||
|
||||
&.is-stopped {
|
||||
background-color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -34,10 +34,7 @@
|
||||
>
|
||||
<div class="role-list__info">
|
||||
<span class="role-list__title">
|
||||
<span
|
||||
class="role-list__dot"
|
||||
:class="{ 'is-stopped': row.status !== CommonStatus.ENABLED }"
|
||||
/>
|
||||
<StatusDot :active="row.status === CommonStatus.ENABLED" />
|
||||
<span class="role-list__name" :title="row.name">{{ row.name }}</span>
|
||||
</span>
|
||||
<span class="role-list__meta">
|
||||
@@ -310,6 +307,7 @@ import type { MenuItem } from "@/api/system/menu";
|
||||
import DeptAPI from "@/api/system/dept";
|
||||
import type { OptionItem } from "@/api/common";
|
||||
import { usePageTable } from "@/composables";
|
||||
import StatusDot from "@/components/StatusDot/index.vue";
|
||||
import { CommonStatus } from "@/enums";
|
||||
import { ROLE_ROOT } from "@/constants";
|
||||
|
||||
@@ -957,18 +955,6 @@ onMounted(() => {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
flex: 0 0 auto;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: var(--el-color-success);
|
||||
border-radius: 50%;
|
||||
|
||||
&.is-stopped {
|
||||
background-color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
<el-table-column label="手机号码" prop="mobile" width="130" />
|
||||
<el-table-column label="邮箱" prop="email" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="创建时间" prop="createTime" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" fixed="right" width="260">
|
||||
<el-table-column label="操作" fixed="right" width="200">
|
||||
<template #default="scope">
|
||||
<TableActions :actions="getUserActions(scope.row as UserItem)" />
|
||||
</template>
|
||||
|
||||
@@ -381,9 +381,13 @@ onBeforeUnmount(() => {
|
||||
defineExpose({
|
||||
/** 供承载页触发的保存动作(与工具栏保存共用一套导出逻辑) */
|
||||
save: handleSave,
|
||||
/** 画布是否有未保存的修改 */
|
||||
/**
|
||||
* 画布是否有未保存的修改
|
||||
*/
|
||||
isDirty: () => dirty.value,
|
||||
/** 持久化成功后清除脏标记(由承载页调用) */
|
||||
/**
|
||||
* 持久化成功后清除脏标记(由承载页调用)
|
||||
*/
|
||||
markSaved: () => {
|
||||
dirty.value = false;
|
||||
},
|
||||
|
||||
@@ -195,6 +195,13 @@ function handleAppClick(app: AppCard): void {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 分组按内容高度排列,不占用页面剩余高度 */
|
||||
.page-content :deep(.el-card__body) {
|
||||
grid-template-rows: none;
|
||||
grid-auto-rows: min-content;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.app-section + .app-section {
|
||||
padding-top: 24px;
|
||||
margin-top: 24px;
|
||||
|
||||
Reference in New Issue
Block a user