mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
feat: 测试代码生成
This commit is contained in:
@ -71,10 +71,10 @@ public class VelocityUtils {
|
|||||||
velocityContext.put("pkColumn", genTable.getPkColumn());
|
velocityContext.put("pkColumn", genTable.getPkColumn());
|
||||||
velocityContext.put("importList", getImportList(genTable));
|
velocityContext.put("importList", getImportList(genTable));
|
||||||
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
|
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
|
||||||
velocityContext.put("columns", getColumns(genTable));
|
|
||||||
velocityContext.put("table", genTable);
|
|
||||||
velocityContext.put("dicts", getDicts(genTable));
|
velocityContext.put("dicts", getDicts(genTable));
|
||||||
velocityContext.put("dictList", getDictList(genTable));
|
velocityContext.put("dictList", getDictList(genTable));
|
||||||
|
velocityContext.put("columns", getColumns(genTable));
|
||||||
|
velocityContext.put("table", genTable);
|
||||||
setMenuVelocityContext(velocityContext, genTable);
|
setMenuVelocityContext(velocityContext, genTable);
|
||||||
if (GenConstants.TPL_TREE.equals(tplCategory)) {
|
if (GenConstants.TPL_TREE.equals(tplCategory)) {
|
||||||
setTreeVelocityContext(velocityContext, genTable);
|
setTreeVelocityContext(velocityContext, genTable);
|
||||||
@ -178,7 +178,7 @@ public class VelocityUtils {
|
|||||||
if (template.contains("soy.index.vue.vm")) {
|
if (template.contains("soy.index.vue.vm")) {
|
||||||
fileName = StringUtils.format("soybean/views/{}/{}/index.vue", moduleName, businessName);
|
fileName = StringUtils.format("soybean/views/{}/{}/index.vue", moduleName, businessName);
|
||||||
} else if (template.contains("soy.api.d.ts.vm")) {
|
} else if (template.contains("soy.api.d.ts.vm")) {
|
||||||
fileName = StringUtils.format("soybean/typings/api/{}.d.ts", moduleName);
|
fileName = StringUtils.format("soybean/typings/api/{}.api.d.ts", moduleName);
|
||||||
} else if (template.contains("soy.api.ts.vm")) {
|
} else if (template.contains("soy.api.ts.vm")) {
|
||||||
fileName = StringUtils.format("soybean/api/{}/{}.ts", moduleName, businessName);
|
fileName = StringUtils.format("soybean/api/{}/{}.ts", moduleName, businessName);
|
||||||
} else if (template.contains("soy.search.vue.vm")) {
|
} else if (template.contains("soy.search.vue.vm")) {
|
||||||
|
2
docs/template/api/soy.api.ts.vm
vendored
2
docs/template/api/soy.api.ts.vm
vendored
@ -28,7 +28,7 @@ export function fetchUpdate${BusinessName} (data: Api.${ModuleName}.${BusinessNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 批量删除${functionName} */
|
/** 批量删除${functionName} */
|
||||||
export function fetchDelete${BusinessName} (${pkColumn.javaField}s: CommonType.IdType[]) {
|
export function fetchBatchDelete${BusinessName} (${pkColumn.javaField}s: CommonType.IdType[]) {
|
||||||
return request<boolean>({
|
return request<boolean>({
|
||||||
url: `/${moduleName}/${businessName}/${${pkColumn.javaField}s.join(',')}`,
|
url: `/${moduleName}/${businessName}/${${pkColumn.javaField}s.join(',')}`,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
|
@ -3,6 +3,7 @@ import { computed, reactive, watch } from 'vue';
|
|||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { fetchCreate${BusinessName}, fetchUpdate${BusinessName} } from '@/service/api/${moduleName}/${businessName}';
|
import { fetchCreate${BusinessName}, fetchUpdate${BusinessName} } from '@/service/api/${moduleName}/${businessName}';
|
||||||
|
#if($dictList && $dictList.size() > 0)import { useDict } from '@/hooks/business/dict';#end
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: '${BusinessName}OperateDrawer'
|
name: '${BusinessName}OperateDrawer'
|
||||||
|
5
docs/template/modules/soy.search.vue.vm
vendored
5
docs/template/modules/soy.search.vue.vm
vendored
@ -3,6 +3,7 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { useNaiveForm } from '@/hooks/common/form';
|
import { useNaiveForm } from '@/hooks/common/form';
|
||||||
|
#if($dictList && $dictList.size() > 0)import { useDict } from '@/hooks/business/dict';#end
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: '${BusinessName}Search'
|
name: '${BusinessName}Search'
|
||||||
@ -27,14 +28,14 @@ const model = defineModel<Api.$ModuleName.${BusinessName}SearchParams>('model',
|
|||||||
|
|
||||||
#if($dictList && $dictList.size() > 0)
|
#if($dictList && $dictList.size() > 0)
|
||||||
#foreach($dict in $dictList)
|
#foreach($dict in $dictList)
|
||||||
const { options: ${dict.name}Options } = useDict(${dict.type}#if($dict.immediate), false#end);
|
const { options: ${dict.name}Options } = useDict('${dict.type}#if($dict.immediate)', false#end);
|
||||||
#end#end
|
#end#end
|
||||||
|
|
||||||
async function reset() {
|
async function reset() {
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
const dateRange${AttrName}.value = undefined;
|
dateRange${AttrName}.value = undefined;
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
await restoreValidation();
|
await restoreValidation();
|
||||||
|
6
docs/template/soy.index.vue.vm
vendored
6
docs/template/soy.index.vue.vm
vendored
@ -26,8 +26,8 @@ const {
|
|||||||
} = useTable({
|
} = useTable({
|
||||||
apiFn: fetchGet${BusinessName}List,
|
apiFn: fetchGet${BusinessName}List,
|
||||||
apiParams: {
|
apiParams: {
|
||||||
current: 1,
|
pageNum: 1,
|
||||||
size: 10,
|
pageSize: 10,
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
// the value can not be undefined, otherwise the property in Form will not be reactive
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
@ -109,7 +109,7 @@ async function handleDelete(#foreach($column in $columns)#if($column.isPk == '1'
|
|||||||
onDeleted();
|
onDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function edit(id: CommonType.IdType) {
|
async function edit(#foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end: CommonType.IdType) {
|
||||||
handleEdit('#foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end', #foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end);
|
handleEdit('#foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end', #foreach($column in $columns)#if($column.isPk == '1')$column.javaField#end#end);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
28
src/components/custom/dict-radio.vue
Normal file
28
src/components/custom/dict-radio.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useDict } from '@/hooks/business/dict';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DictRadio' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
dictCode: string;
|
||||||
|
immediate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
immediate: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = defineModel<string | null>('value', { required: false });
|
||||||
|
|
||||||
|
const { options } = useDict(props.dictCode, props.immediate);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NRadioGroup v-model:value="value">
|
||||||
|
<NSpace>
|
||||||
|
<NRadio v-for="option in options" :key="option.value" :value="option.value" :label="option.label" />
|
||||||
|
</NSpace>
|
||||||
|
</NRadioGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -1,46 +1,30 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="ts">
|
||||||
import { ref, useAttrs } from 'vue';
|
import { useAttrs } from 'vue';
|
||||||
import { useLoading } from '@sa/hooks';
|
import type { SelectProps } from 'naive-ui';
|
||||||
import type { SelectOption, SelectProps } from 'naive-ui';
|
import { useDict } from '@/hooks/business/dict';
|
||||||
import { fetchGetDictTypeOption } from '@/service/api/system';
|
|
||||||
|
|
||||||
defineOptions({ name: 'DictSelect' });
|
defineOptions({ name: 'DictSelect' });
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
dictCode: string;
|
||||||
|
immediate?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
immediate: true
|
||||||
|
});
|
||||||
|
|
||||||
const value = defineModel<string>('value', { required: true });
|
const value = defineModel<string | null>('value', { required: true });
|
||||||
|
|
||||||
const attrs: SelectProps = useAttrs();
|
const attrs: SelectProps = useAttrs();
|
||||||
const options = ref<SelectOption[]>([]);
|
const { options } = useDict(props.dictCode, props.immediate);
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
|
||||||
|
|
||||||
async function getDeptOptions() {
|
|
||||||
startLoading();
|
|
||||||
const { error, data } = await fetchGetDictTypeOption();
|
|
||||||
if (error) return;
|
|
||||||
options.value = data.map(dict => ({
|
|
||||||
value: dict.dictType!,
|
|
||||||
label: () => (
|
|
||||||
<div class="w-520px flex justify-between">
|
|
||||||
<span>{dict.dictType}</span>
|
|
||||||
<span>{dict.dictName}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
endLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeptOptions();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NSelect
|
<NSelect
|
||||||
v-model:value="value"
|
v-model:value="value"
|
||||||
:loading="loading"
|
:loading="!options.length"
|
||||||
:options="options"
|
:options="options"
|
||||||
:clear-filter-after-select="false"
|
:clear-filter-after-select="false"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
|
@ -19,8 +19,8 @@ export const menuTypeOptions = transformRecordToOption(menuTypeRecord);
|
|||||||
|
|
||||||
/** menu is frame */
|
/** menu is frame */
|
||||||
export const menuIsFrameRecord: Record<Api.System.IsMenuFrame, string> = {
|
export const menuIsFrameRecord: Record<Api.System.IsMenuFrame, string> = {
|
||||||
'0': '缓存',
|
'0': '是',
|
||||||
'1': '不缓存',
|
'1': '否',
|
||||||
'2': 'iframe'
|
'2': 'iframe'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export function useDownload() {
|
|||||||
window.$loading?.startLoading('正在下载数据,请稍候...');
|
window.$loading?.startLoading('正在下载数据,请稍候...');
|
||||||
const token = localStg.get('token');
|
const token = localStg.get('token');
|
||||||
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
||||||
const now = new Date().getTime();
|
const now = Date.now();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
Object.keys(params).forEach(key => formData.append(key, params[key]));
|
Object.keys(params).forEach(key => formData.append(key, params[key]));
|
||||||
fetch(`${baseURL}${url}?t=${now}`, {
|
fetch(`${baseURL}${url}?t=${now}`, {
|
||||||
@ -58,7 +58,7 @@ export function useDownload() {
|
|||||||
const token = localStg.get('token');
|
const token = localStg.get('token');
|
||||||
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
||||||
const url = `/resource/oss/download/${ossId}`;
|
const url = `/resource/oss/download/${ossId}`;
|
||||||
const now = new Date().getTime();
|
const now = Date.now();
|
||||||
let fileName = String(`${ossId}-${now}`);
|
let fileName = String(`${ossId}-${now}`);
|
||||||
fetch(`${baseURL}${url}?t=${now}`, {
|
fetch(`${baseURL}${url}?t=${now}`, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -80,7 +80,7 @@ export function useDownload() {
|
|||||||
window.$loading?.startLoading('正在下载数据,请稍候...');
|
window.$loading?.startLoading('正在下载数据,请稍候...');
|
||||||
const token = localStg.get('token');
|
const token = localStg.get('token');
|
||||||
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
||||||
const now = new Date().getTime();
|
const now = Date.now();
|
||||||
fetch(`${baseURL}${url}${url.includes('?') ? '&' : '?'}t=${now}`, {
|
fetch(`${baseURL}${url}${url.includes('?') ? '&' : '?'}t=${now}`, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -160,7 +160,8 @@ const local: App.I18n.Schema = {
|
|||||||
system: 'System Management',
|
system: 'System Management',
|
||||||
system_menu: 'Menu Management',
|
system_menu: 'Menu Management',
|
||||||
tool: 'System Tools',
|
tool: 'System Tools',
|
||||||
tool_gen: 'Code Generation'
|
tool_gen: 'Code Generation',
|
||||||
|
system_user: 'User Management'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
@ -160,7 +160,8 @@ const local: App.I18n.Schema = {
|
|||||||
system: '系统管理',
|
system: '系统管理',
|
||||||
system_menu: '菜单管理',
|
system_menu: '菜单管理',
|
||||||
tool: '系统工具',
|
tool: '系统工具',
|
||||||
tool_gen: '代码生成'
|
tool_gen: '代码生成',
|
||||||
|
system_user: '用户管理'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
@ -22,5 +22,6 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
login: () => import("@/views/_builtin/login/index.vue"),
|
login: () => import("@/views/_builtin/login/index.vue"),
|
||||||
home: () => import("@/views/home/index.vue"),
|
home: () => import("@/views/home/index.vue"),
|
||||||
system_menu: () => import("@/views/system/menu/index.vue"),
|
system_menu: () => import("@/views/system/menu/index.vue"),
|
||||||
|
system_user: () => import("@/views/system/user/index.vue"),
|
||||||
tool_gen: () => import("@/views/tool/gen/index.vue"),
|
tool_gen: () => import("@/views/tool/gen/index.vue"),
|
||||||
};
|
};
|
||||||
|
@ -96,6 +96,15 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
localIcon: 'menu-tree-table',
|
localIcon: 'menu-tree-table',
|
||||||
order: 3
|
order: 3
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'system_user',
|
||||||
|
path: '/system/user',
|
||||||
|
component: 'view.system_user',
|
||||||
|
meta: {
|
||||||
|
title: 'system_user',
|
||||||
|
i18nKey: 'route.system_user'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -171,6 +171,7 @@ const routeMap: RouteMap = {
|
|||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||||
"system": "/system",
|
"system": "/system",
|
||||||
"system_menu": "/system/menu",
|
"system_menu": "/system/menu",
|
||||||
|
"system_user": "/system/user",
|
||||||
"tool": "/tool",
|
"tool": "/tool",
|
||||||
"tool_gen": "/tool/gen"
|
"tool_gen": "/tool/gen"
|
||||||
};
|
};
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * from './menu';
|
export * from './menu';
|
||||||
export * from './dict';
|
export * from './dict';
|
||||||
|
export * from './user';
|
||||||
|
36
src/service/api/system/user.ts
Normal file
36
src/service/api/system/user.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { request } from '@/service/request';
|
||||||
|
|
||||||
|
/** 获取用户信息列表 */
|
||||||
|
export function fetchGetUserList(params?: Api.System.UserSearchParams) {
|
||||||
|
return request<Api.System.UserList>({
|
||||||
|
url: '/system/user/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增用户信息 */
|
||||||
|
export function fetchCreateUser(data: Api.System.UserOperateParams) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/system/user',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改用户信息 */
|
||||||
|
export function fetchUpdateUser(data: Api.System.UserOperateParams) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/system/user',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除用户信息 */
|
||||||
|
export function fetchBatchDeleteUser(userIds: CommonType.IdType[]) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: `/system/user/${userIds.join(',')}`,
|
||||||
|
method: 'delete'
|
||||||
|
});
|
||||||
|
}
|
7
src/typings/api/api.d.ts
vendored
7
src/typings/api/api.d.ts
vendored
@ -21,9 +21,9 @@ declare namespace Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** common search params of table */
|
/** common search params of table */
|
||||||
type CommonSearchParams<T = any> = Pick<Common.PaginatingCommonParams, 'pageNum' | 'pageSize'> &
|
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'pageNum' | 'pageSize'> &
|
||||||
CommonType.RecordNullable<{
|
CommonType.RecordNullable<{
|
||||||
orderByColumn: keyof T;
|
orderByColumn: string;
|
||||||
isAsc: 'asc' | 'desc';
|
isAsc: 'asc' | 'desc';
|
||||||
params: { [key: string]: any };
|
params: { [key: string]: any };
|
||||||
}>;
|
}>;
|
||||||
@ -69,8 +69,7 @@ declare namespace Api {
|
|||||||
type CommonTenantRecord<T = any> = {
|
type CommonTenantRecord<T = any> = {
|
||||||
/** record tenant id */
|
/** record tenant id */
|
||||||
tenantId: string;
|
tenantId: string;
|
||||||
} & CommonRecord &
|
} & CommonRecord<T>;
|
||||||
T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
191
src/typings/api/system.api.d.ts
vendored
191
src/typings/api/system.api.d.ts
vendored
@ -13,32 +13,32 @@ declare namespace Api {
|
|||||||
/** role */
|
/** role */
|
||||||
type Role = Common.CommonRecord<{
|
type Role = Common.CommonRecord<{
|
||||||
/** 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) */
|
/** 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) */
|
||||||
dataScope?: string;
|
dataScope: string;
|
||||||
/** 部门树选择项是否关联显示 */
|
/** 部门树选择项是否关联显示 */
|
||||||
deptCheckStrictly?: boolean;
|
deptCheckStrictly: boolean;
|
||||||
/** 用户是否存在此角色标识 默认不存在 */
|
/** 用户是否存在此角色标识 默认不存在 */
|
||||||
flag?: boolean;
|
flag: boolean;
|
||||||
/** 菜单树选择项是否关联显示 */
|
/** 菜单树选择项是否关联显示 */
|
||||||
menuCheckStrictly?: boolean;
|
menuCheckStrictly: boolean;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
/** 角色ID */
|
/** 角色ID */
|
||||||
roleId?: number;
|
roleId: number;
|
||||||
/** 角色权限字符串 */
|
/** 角色权限字符串 */
|
||||||
roleKey?: string;
|
roleKey: string;
|
||||||
/** 角色名称 */
|
/** 角色名称 */
|
||||||
roleName?: string;
|
roleName: string;
|
||||||
/** 显示顺序 */
|
/** 显示顺序 */
|
||||||
roleSort?: number;
|
roleSort: number;
|
||||||
/** 角色状态(0正常 1停用) */
|
/** 角色状态(0正常 1停用) */
|
||||||
status?: string;
|
status: string;
|
||||||
/** 是否管理员 */
|
/** 是否管理员 */
|
||||||
superAdmin?: boolean;
|
superAdmin: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** role search params */
|
/** role search params */
|
||||||
type RoleSearchParams = CommonType.RecordNullable<
|
type RoleSearchParams = CommonType.RecordNullable<
|
||||||
Pick<Role, 'roleName' | 'roleKey' | 'status'> & Common.CommonSearchParams<Role>
|
Pick<Role, 'roleName' | 'roleKey' | 'status'> & Common.CommonSearchParams
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/** role list */
|
/** role list */
|
||||||
@ -58,40 +58,59 @@ declare namespace Api {
|
|||||||
/** user */
|
/** user */
|
||||||
type User = Common.CommonTenantRecord<{
|
type User = Common.CommonTenantRecord<{
|
||||||
/** 用户ID */
|
/** 用户ID */
|
||||||
userId?: CommonType.IdType;
|
userId: CommonType.IdType;
|
||||||
/** 部门ID */
|
/** 部门ID */
|
||||||
deptId?: CommonType.IdType;
|
deptId: CommonType.IdType;
|
||||||
|
/** 部门名称 */
|
||||||
|
deptName: string;
|
||||||
/** 用户账号 */
|
/** 用户账号 */
|
||||||
userName?: string;
|
userName: string;
|
||||||
/** 用户昵称 */
|
/** 用户昵称 */
|
||||||
nickName?: string;
|
nickName: string;
|
||||||
/** 用户类型(sys_user系统用户) */
|
/** 用户类型(sys_user系统用户) */
|
||||||
userType?: string;
|
userType: string;
|
||||||
/** 用户邮箱 */
|
/** 用户邮箱 */
|
||||||
email?: string;
|
email: string;
|
||||||
/** 手机号码 */
|
/** 手机号码 */
|
||||||
phonenumber?: string;
|
phonenumber: string;
|
||||||
/** 用户性别(0男 1女 2未知) */
|
/** 用户性别(0男 1女 2未知) */
|
||||||
sex?: string;
|
sex: string;
|
||||||
/** 头像地址 */
|
/** 头像地址 */
|
||||||
avatar?: number;
|
avatar: string;
|
||||||
/** 密码 */
|
/** 密码 */
|
||||||
password?: string;
|
password: string;
|
||||||
/** 帐号状态(0正常 1停用) */
|
/** 帐号状态(0正常 1停用) */
|
||||||
status?: string;
|
status: string;
|
||||||
/** 删除标志(0代表存在 2代表删除) */
|
/** 删除标志(0代表存在 2代表删除) */
|
||||||
delFlag?: string;
|
delFlag: string;
|
||||||
/** 最后登录IP */
|
/** 最后登录IP */
|
||||||
loginIp?: string;
|
loginIp: string;
|
||||||
/** 最后登录时间 */
|
/** 最后登录时间 */
|
||||||
loginDate?: Date;
|
loginDate: Date;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** user search params */
|
/** user search params */
|
||||||
type UserSearchParams = CommonType.RecordNullable<
|
type UserSearchParams = CommonType.RecordNullable<
|
||||||
Pick<User, 'userName' | 'sex' | 'nickName' | 'phonenumber' | 'email' | 'status'> & Common.CommonSearchParams<User>
|
Pick<User, 'deptId' | 'userName' | 'nickName' | 'phonenumber' | 'status'> & Common.CommonSearchParams
|
||||||
|
>;
|
||||||
|
|
||||||
|
/** user operate params */
|
||||||
|
type UserOperateParams = CommonType.RecordNullable<
|
||||||
|
Pick<
|
||||||
|
User,
|
||||||
|
| 'userId'
|
||||||
|
| 'deptId'
|
||||||
|
| 'userName'
|
||||||
|
| 'nickName'
|
||||||
|
| 'email'
|
||||||
|
| 'phonenumber'
|
||||||
|
| 'sex'
|
||||||
|
| 'password'
|
||||||
|
| 'status'
|
||||||
|
| 'remark'
|
||||||
|
> & { roleIds: string[] }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/** user list */
|
/** user list */
|
||||||
@ -100,33 +119,33 @@ declare namespace Api {
|
|||||||
/** tenant */
|
/** tenant */
|
||||||
interface Tenant {
|
interface Tenant {
|
||||||
/** id */
|
/** id */
|
||||||
id?: CommonType.IdType;
|
id: CommonType.IdType;
|
||||||
/** 租户编号 */
|
/** 租户编号 */
|
||||||
tenantId?: string;
|
tenantId: string;
|
||||||
/** 联系人 */
|
/** 联系人 */
|
||||||
contactUserName?: string;
|
contactUserName: string;
|
||||||
/** 联系电话 */
|
/** 联系电话 */
|
||||||
contactPhone?: string;
|
contactPhone: string;
|
||||||
/** 企业名称 */
|
/** 企业名称 */
|
||||||
companyName?: string;
|
companyName: string;
|
||||||
/** 统一社会信用代码 */
|
/** 统一社会信用代码 */
|
||||||
licenseNumber?: string;
|
licenseNumber: string;
|
||||||
/** 地址 */
|
/** 地址 */
|
||||||
address?: string;
|
address: string;
|
||||||
/** 域名 */
|
/** 域名 */
|
||||||
domain?: string;
|
domain: string;
|
||||||
/** 企业简介 */
|
/** 企业简介 */
|
||||||
intro?: string;
|
intro: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
/** 租户套餐编号 */
|
/** 租户套餐编号 */
|
||||||
packageId?: number;
|
packageId: number;
|
||||||
/** 过期时间 */
|
/** 过期时间 */
|
||||||
expireTime?: Date;
|
expireTime: Date;
|
||||||
/** 用户数量(-1不限制) */
|
/** 用户数量(-1不限制) */
|
||||||
accountCount?: number;
|
accountCount: number;
|
||||||
/** 租户状态(0正常 1停用) */
|
/** 租户状态(0正常 1停用) */
|
||||||
status?: string;
|
status: string;
|
||||||
/** 删除标志(0代表存在 2代表删除) */
|
/** 删除标志(0代表存在 2代表删除) */
|
||||||
delFlag: string;
|
delFlag: string;
|
||||||
}
|
}
|
||||||
@ -159,39 +178,39 @@ declare namespace Api {
|
|||||||
|
|
||||||
type Menu = Common.CommonRecord<{
|
type Menu = Common.CommonRecord<{
|
||||||
/** 菜单 ID */
|
/** 菜单 ID */
|
||||||
menuId?: CommonType.IdType;
|
menuId: CommonType.IdType;
|
||||||
/** 父菜单 ID */
|
/** 父菜单 ID */
|
||||||
parentId?: CommonType.IdType;
|
parentId: CommonType.IdType;
|
||||||
/** 菜单名称 */
|
/** 菜单名称 */
|
||||||
menuName?: string;
|
menuName: string;
|
||||||
/** 显示顺序 */
|
/** 显示顺序 */
|
||||||
orderNum?: number;
|
orderNum: number;
|
||||||
/** 路由地址 */
|
/** 路由地址 */
|
||||||
path?: string;
|
path: string;
|
||||||
/** 组件路径 */
|
/** 组件路径 */
|
||||||
component?: string;
|
component: string;
|
||||||
/** 路由参数 */
|
/** 路由参数 */
|
||||||
queryParam?: string;
|
queryParam: string;
|
||||||
/** 是否为外链(0是 1否 2iframe) */
|
/** 是否为外链(0是 1否 2iframe) */
|
||||||
isFrame?: IsMenuFrame;
|
isFrame: IsMenuFrame;
|
||||||
/** 是否缓存(0缓存 1不缓存) */
|
/** 是否缓存(0缓存 1不缓存) */
|
||||||
isCache?: Common.YesOrNoStatus;
|
isCache: Common.YesOrNoStatus;
|
||||||
/** 菜单类型(M目录 C菜单 F按钮) */
|
/** 菜单类型(M目录 C菜单 F按钮) */
|
||||||
menuType?: MenuType;
|
menuType: MenuType;
|
||||||
/** 显示状态(0显示 1隐藏) */
|
/** 显示状态(0显示 1隐藏) */
|
||||||
visible?: Common.VisibleStatus;
|
visible: Common.VisibleStatus;
|
||||||
/** 菜单状态(0正常 1停用) */
|
/** 菜单状态(0正常 1停用) */
|
||||||
status?: Common.EnableStatus;
|
status: Common.EnableStatus;
|
||||||
/** 权限标识 */
|
/** 权限标识 */
|
||||||
perms?: string;
|
perms: string;
|
||||||
/** 菜单图标 */
|
/** 菜单图标 */
|
||||||
icon?: string;
|
icon: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
/** 父菜单名称 */
|
/** 父菜单名称 */
|
||||||
parentName?: string;
|
parentName: string;
|
||||||
/** 子菜单 */
|
/** 子菜单 */
|
||||||
children?: MenuList;
|
children: MenuList;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** menu list */
|
/** menu list */
|
||||||
@ -201,57 +220,59 @@ declare namespace Api {
|
|||||||
type MenuSearchParams = CommonType.RecordNullable<Pick<Menu, 'menuName' | 'status' | 'menuType' | 'parentId'>>;
|
type MenuSearchParams = CommonType.RecordNullable<Pick<Menu, 'menuName' | 'status' | 'menuType' | 'parentId'>>;
|
||||||
|
|
||||||
/** menu operate params */
|
/** menu operate params */
|
||||||
type MenuOperateParams = Pick<
|
type MenuOperateParams = CommonType.RecordNullable<
|
||||||
Menu,
|
Pick<
|
||||||
| 'menuId'
|
Menu,
|
||||||
| 'menuName'
|
| 'menuId'
|
||||||
| 'parentId'
|
| 'menuName'
|
||||||
| 'orderNum'
|
| 'parentId'
|
||||||
| 'path'
|
| 'orderNum'
|
||||||
| 'component'
|
| 'path'
|
||||||
| 'queryParam'
|
| 'component'
|
||||||
| 'isFrame'
|
| 'queryParam'
|
||||||
| 'isCache'
|
| 'isFrame'
|
||||||
| 'menuType'
|
| 'isCache'
|
||||||
| 'visible'
|
| 'menuType'
|
||||||
| 'status'
|
| 'visible'
|
||||||
| 'perms'
|
| 'status'
|
||||||
| 'icon'
|
| 'perms'
|
||||||
| 'remark'
|
| 'icon'
|
||||||
|
| 'remark'
|
||||||
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/** 字典类型 */
|
/** 字典类型 */
|
||||||
type DictType = Common.CommonRecord<{
|
type DictType = Common.CommonRecord<{
|
||||||
/** 字典主键 */
|
/** 字典主键 */
|
||||||
dictId?: number;
|
dictId: number;
|
||||||
/** 字典名称 */
|
/** 字典名称 */
|
||||||
dictName?: string;
|
dictName: string;
|
||||||
/** 字典类型 */
|
/** 字典类型 */
|
||||||
dictType?: string;
|
dictType: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** 字典数据 */
|
/** 字典数据 */
|
||||||
type DictData = Common.CommonRecord<{
|
type DictData = Common.CommonRecord<{
|
||||||
/** 样式属性(其他样式扩展) */
|
/** 样式属性(其他样式扩展) */
|
||||||
cssClass?: string;
|
cssClass: string;
|
||||||
/** 字典编码 */
|
/** 字典编码 */
|
||||||
dictCode?: number;
|
dictCode: number;
|
||||||
/** 字典标签 */
|
/** 字典标签 */
|
||||||
dictLabel?: string;
|
dictLabel: string;
|
||||||
/** 字典排序 */
|
/** 字典排序 */
|
||||||
dictSort?: number;
|
dictSort: number;
|
||||||
/** 字典类型 */
|
/** 字典类型 */
|
||||||
dictType?: string;
|
dictType: string;
|
||||||
/** 字典键值 */
|
/** 字典键值 */
|
||||||
dictValue?: string;
|
dictValue: string;
|
||||||
/** 是否默认(Y是 N否) */
|
/** 是否默认(Y是 N否) */
|
||||||
isDefault?: string;
|
isDefault: string;
|
||||||
/** 表格回显样式 */
|
/** 表格回显样式 */
|
||||||
listClass?: string;
|
listClass: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@ -11,8 +11,10 @@ declare module 'vue' {
|
|||||||
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||||
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||||
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||||
|
copy: typeof import('./../components/custom/dict-select copy.vue')['default']
|
||||||
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||||
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||||
|
DictRadio: typeof import('./../components/custom/dict-radio.vue')['default']
|
||||||
DictSelect: typeof import('./../components/custom/dict-select.vue')['default']
|
DictSelect: typeof import('./../components/custom/dict-select.vue')['default']
|
||||||
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
|
||||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||||
|
2
src/typings/elegant-router.d.ts
vendored
2
src/typings/elegant-router.d.ts
vendored
@ -25,6 +25,7 @@ declare module "@elegant-router/types" {
|
|||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
||||||
"system": "/system";
|
"system": "/system";
|
||||||
"system_menu": "/system/menu";
|
"system_menu": "/system/menu";
|
||||||
|
"system_user": "/system/user";
|
||||||
"tool": "/tool";
|
"tool": "/tool";
|
||||||
"tool_gen": "/tool/gen";
|
"tool_gen": "/tool/gen";
|
||||||
};
|
};
|
||||||
@ -89,6 +90,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "login"
|
| "login"
|
||||||
| "home"
|
| "home"
|
||||||
| "system_menu"
|
| "system_menu"
|
||||||
|
| "system_user"
|
||||||
| "tool_gen"
|
| "tool_gen"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ interface Props {
|
|||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'submitted', menuType?: Api.System.MenuType): void;
|
(e: 'submitted', menuType: Api.System.MenuType): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
@ -45,7 +45,7 @@ const { options: enableStatusOptions } = useDict('sys_normal_disable');
|
|||||||
|
|
||||||
const iconType = ref<Api.System.IconType>('1');
|
const iconType = ref<Api.System.IconType>('1');
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
const { defaultRequiredRule } = useFormRules();
|
const { createRequiredRule } = useFormRules();
|
||||||
const queryList = ref<{ key: string; value: string }[]>([]);
|
const queryList = ref<{ key: string; value: string }[]>([]);
|
||||||
|
|
||||||
const drawerTitle = computed(() => {
|
const drawerTitle = computed(() => {
|
||||||
@ -82,10 +82,10 @@ function createDefaultModel(): Model {
|
|||||||
type RuleKey = Extract<keyof Model, 'menuName' | 'orderNum' | 'path' | 'component'>;
|
type RuleKey = Extract<keyof Model, 'menuName' | 'orderNum' | 'path' | 'component'>;
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
menuName: { ...defaultRequiredRule, message: '菜单名称不能为空' },
|
menuName: createRequiredRule('菜单名称不能为空'),
|
||||||
orderNum: { ...defaultRequiredRule, type: 'number', message: '菜单排序不能为空' },
|
orderNum: createRequiredRule('菜单排序不能为空'),
|
||||||
path: { ...defaultRequiredRule, message: '路由地址不能为空' },
|
path: createRequiredRule('路由地址不能为空'),
|
||||||
component: { ...defaultRequiredRule, message: '组件路径不能为空' }
|
component: createRequiredRule('组件路径不能为空')
|
||||||
};
|
};
|
||||||
|
|
||||||
const isBtn = computed(() => model.menuType === 'F');
|
const isBtn = computed(() => model.menuType === 'F');
|
||||||
@ -210,7 +210,7 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
emit('submitted', menuType);
|
emit('submitted', menuType!);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(visible, () => {
|
watch(visible, () => {
|
||||||
@ -344,7 +344,7 @@ const FormTipComponent = defineComponent({
|
|||||||
ignore-path-change
|
ignore-path-change
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:path="`query[${index}].key`"
|
:path="`query[${index}].key`"
|
||||||
:rule="{ ...defaultRequiredRule, validator: value => isNotNull(value) }"
|
:rule="{ ...createRequiredRule('请输入 Key'), validator: value => isNotNull(value) }"
|
||||||
>
|
>
|
||||||
<NInput v-model:value="queryList[index].key" placeholder="Key" @keydown.enter.prevent />
|
<NInput v-model:value="queryList[index].key" placeholder="Key" @keydown.enter.prevent />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
@ -354,7 +354,7 @@ const FormTipComponent = defineComponent({
|
|||||||
ignore-path-change
|
ignore-path-change
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:path="`query[${index}].value`"
|
:path="`query[${index}].value`"
|
||||||
:rule="{ ...defaultRequiredRule, validator: value => isNotNull(value) }"
|
:rule="{ ...createRequiredRule('请输入 Value'), validator: value => isNotNull(value) }"
|
||||||
>
|
>
|
||||||
<NInput v-model:value="queryList[index].value" placeholder="Value" @keydown.enter.prevent />
|
<NInput v-model:value="queryList[index].value" placeholder="Value" @keydown.enter.prevent />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
179
src/views/system/user/index.vue
Normal file
179
src/views/system/user/index.vue
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { NButton, NPopconfirm } from 'naive-ui';
|
||||||
|
import { fetchBatchDeleteUser, fetchGetUserList } from '@/service/api/system';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { useTable, useTableOperate } from '@/hooks/common/table';
|
||||||
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UserList'
|
||||||
|
});
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const {
|
||||||
|
columns,
|
||||||
|
columnChecks,
|
||||||
|
data,
|
||||||
|
getData,
|
||||||
|
getDataByPage,
|
||||||
|
loading,
|
||||||
|
mobilePagination,
|
||||||
|
searchParams,
|
||||||
|
resetSearchParams
|
||||||
|
} = useTable({
|
||||||
|
apiFn: fetchGetUserList,
|
||||||
|
apiParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
||||||
|
// the value can not be undefined, otherwise the property in Form will not be reactive
|
||||||
|
deptId: null,
|
||||||
|
userName: null,
|
||||||
|
nickName: null,
|
||||||
|
phonenumber: null,
|
||||||
|
status: null,
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
columns: () => [
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
align: 'center',
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'index',
|
||||||
|
title: $t('common.index'),
|
||||||
|
align: 'center',
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'deptId',
|
||||||
|
title: '部门',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userName',
|
||||||
|
title: '用户名称',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nickName',
|
||||||
|
title: '用户昵称',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'phonenumber',
|
||||||
|
title: '手机号码',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: '帐号状态',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
title: $t('common.operate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 130,
|
||||||
|
render: row => (
|
||||||
|
<div class="flex-center gap-8px">
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => edit(row.userId!)}>
|
||||||
|
{$t('common.edit')}
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm onPositiveClick={() => handleDelete(row.userId!)}>
|
||||||
|
{{
|
||||||
|
default: () => $t('common.confirmDelete'),
|
||||||
|
trigger: () => (
|
||||||
|
<NButton type="error" ghost size="small">
|
||||||
|
{$t('common.delete')}
|
||||||
|
</NButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
||||||
|
useTableOperate(data, getData);
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
// request
|
||||||
|
const { error } = await fetchBatchDeleteUser(checkedRowKeys.value);
|
||||||
|
if (error) return;
|
||||||
|
onBatchDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(userId: CommonType.IdType) {
|
||||||
|
// request
|
||||||
|
const { error } = await fetchBatchDeleteUser([userId]);
|
||||||
|
if (error) return;
|
||||||
|
onDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function edit(userId: CommonType.IdType) {
|
||||||
|
handleEdit('userId', userId);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
||||||
|
<NCard title="用户信息列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
|
||||||
|
<template #header-extra>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="checkedRowKeys.length === 0"
|
||||||
|
:loading="loading"
|
||||||
|
@add="handleAdd"
|
||||||
|
@delete="handleBatchDelete"
|
||||||
|
@refresh="getData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<NDataTable
|
||||||
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
size="small"
|
||||||
|
:flex-height="!appStore.isMobile"
|
||||||
|
:scroll-x="962"
|
||||||
|
:loading="loading"
|
||||||
|
remote
|
||||||
|
:row-key="row => row.userId"
|
||||||
|
:pagination="mobilePagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
<UserOperateDrawer
|
||||||
|
v-model:visible="drawerVisible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
@submitted="getDataByPage"
|
||||||
|
/>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
175
src/views/system/user/modules/user-operate-drawer.vue
Normal file
175
src/views/system/user/modules/user-operate-drawer.vue
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, watch } from 'vue';
|
||||||
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { fetchCreateUser, fetchUpdateUser } from '@/service/api/system';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'UserOperateDrawer'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** the type of operation */
|
||||||
|
operateType: NaiveUI.TableOperateType;
|
||||||
|
/** the edit row data */
|
||||||
|
rowData?: Api.System.User | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'submitted'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
const { createRequiredRule } = useFormRules();
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||||
|
add: '新增用户信息',
|
||||||
|
edit: '编辑用户信息'
|
||||||
|
};
|
||||||
|
return titles[props.operateType];
|
||||||
|
});
|
||||||
|
|
||||||
|
type Model = Api.System.UserOperateParams;
|
||||||
|
|
||||||
|
const model: Model = reactive(createDefaultModel());
|
||||||
|
|
||||||
|
function createDefaultModel(): Model {
|
||||||
|
return {
|
||||||
|
deptId: null,
|
||||||
|
userName: '',
|
||||||
|
nickName: '',
|
||||||
|
email: '',
|
||||||
|
phonenumber: '',
|
||||||
|
sex: '',
|
||||||
|
password: '',
|
||||||
|
status: '',
|
||||||
|
remark: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleKey = Extract<keyof Model, 'userName' | 'nickName' | 'password' | 'status'>;
|
||||||
|
|
||||||
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
|
userName: createRequiredRule('用户名称不能为空'),
|
||||||
|
nickName: createRequiredRule('用户昵称不能为空'),
|
||||||
|
password: createRequiredRule('密码不能为空'),
|
||||||
|
status: createRequiredRule('帐号状态不能为空')
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleUpdateModelWhenEdit() {
|
||||||
|
if (props.operateType === 'add') {
|
||||||
|
Object.assign(model, createDefaultModel());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
|
Object.assign(model, props.rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await validate();
|
||||||
|
|
||||||
|
// request
|
||||||
|
if (props.operateType === 'add') {
|
||||||
|
const { deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||||
|
const { error } = await fetchCreateUser({
|
||||||
|
deptId,
|
||||||
|
userName,
|
||||||
|
nickName,
|
||||||
|
email,
|
||||||
|
phonenumber,
|
||||||
|
sex,
|
||||||
|
password,
|
||||||
|
status,
|
||||||
|
remark
|
||||||
|
});
|
||||||
|
if (error) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.operateType === 'edit') {
|
||||||
|
const { userId, deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||||
|
const { error } = await fetchUpdateUser({
|
||||||
|
userId,
|
||||||
|
deptId,
|
||||||
|
userName,
|
||||||
|
nickName,
|
||||||
|
email,
|
||||||
|
phonenumber,
|
||||||
|
sex,
|
||||||
|
password,
|
||||||
|
status,
|
||||||
|
remark
|
||||||
|
});
|
||||||
|
if (error) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.$message?.success($t('common.updateSuccess'));
|
||||||
|
closeDrawer();
|
||||||
|
emit('submitted');
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, () => {
|
||||||
|
if (visible.value) {
|
||||||
|
handleUpdateModelWhenEdit();
|
||||||
|
restoreValidation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||||
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
|
<NForm ref="formRef" :model="model" :rules="rules">
|
||||||
|
<NFormItem label="部门" path="deptId">
|
||||||
|
<!-- <NInput v-model:value="model.deptId" placeholder="请输入部门" /> -->
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="用户名称" path="userName">
|
||||||
|
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="用户昵称" path="nickName">
|
||||||
|
<NInput v-model:value="model.nickName" placeholder="请输入用户昵称" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="用户邮箱" path="email">
|
||||||
|
<NInput v-model:value="model.email" placeholder="请输入用户邮箱" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="手机号码" path="phonenumber">
|
||||||
|
<NInput v-model:value="model.phonenumber" placeholder="请输入手机号码" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="用户性别" path="sex">
|
||||||
|
<DictRadio v-model:value="model.sex" dict-code="sys_user_sex" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="密码" path="password">
|
||||||
|
<NInput v-model:value="model.password" placeholder="请输入密码" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="帐号状态" path="status">
|
||||||
|
<NInput v-model:value="model.status" placeholder="请输入帐号状态" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="备注" path="remark">
|
||||||
|
<NInput v-model:value="model.remark" placeholder="请输入备注" />
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace :size="16">
|
||||||
|
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NDrawerContent>
|
||||||
|
</NDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
87
src/views/system/user/modules/user-search.vue
Normal file
87
src/views/system/user/modules/user-search.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { useNaiveForm } from '@/hooks/common/form';
|
||||||
|
defineOptions({
|
||||||
|
name: 'UserSearch'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'reset'): void;
|
||||||
|
(e: 'search'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
|
||||||
|
const dateRangeCreateTime = ref<[string, string]>();
|
||||||
|
|
||||||
|
const model = defineModel<Api.System.UserSearchParams>('model', { required: true });
|
||||||
|
|
||||||
|
async function reset() {
|
||||||
|
dateRangeCreateTime.value = undefined;
|
||||||
|
await restoreValidation();
|
||||||
|
emit('reset');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
await validate();
|
||||||
|
if (dateRangeCreateTime.value?.length) {
|
||||||
|
model.value.params!.beginCreateTime = dateRangeCreateTime.value[0];
|
||||||
|
model.value.params!.endCreateTime = dateRangeCreateTime.value[0];
|
||||||
|
}
|
||||||
|
emit('search');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||||
|
<NCollapse>
|
||||||
|
<NCollapseItem :title="$t('common.search')" name="user-search">
|
||||||
|
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||||
|
<NGrid responsive="screen" item-responsive>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" label="用户名称" path="userName" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" label="用户昵称" path="nickName" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.nickName" placeholder="请输入用户昵称" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" label="手机号码" path="phonenumber" class="pr-24px">
|
||||||
|
<NInput v-model:value="model.phonenumber" placeholder="请输入手机号码" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" label="帐号状态" path="status" class="pr-24px">
|
||||||
|
<NSelect v-model:value="model.status" placeholder="请选择帐号状态" :options="[]" clearable />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24 s:12 m:6" label="创建时间" path="createTime" class="pr-24px">
|
||||||
|
<NDatePicker
|
||||||
|
v-model:formatted-value="dateRangeCreateTime"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi span="24" class="pr-24px">
|
||||||
|
<NSpace class="w-full" justify="end">
|
||||||
|
<NButton @click="reset">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-refresh class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.reset') }}
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" ghost @click="search">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-search class="text-icon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('common.search') }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NForm>
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
</NCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -218,7 +218,7 @@ async function handleGenCode(row?: Api.Tool.GenTable) {
|
|||||||
if (error) return;
|
if (error) return;
|
||||||
window.$message?.success('生成成功');
|
window.$message?.success('生成成功');
|
||||||
} else {
|
} else {
|
||||||
zip(`/tool/gen/batchGenCode?tableIdStr=${tableIds}`, `ruoyi-${new Date().getTime()}.zip`);
|
zip(`/tool/gen/batchGenCode?tableIdStr=${tableIds}`, `RuoYi-${row?.tableId ? `${row.className}` : Date.now()}.zip`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +269,7 @@ const columns: NaiveUI.TableColumn<Api.Tool.GenTableColumn>[] = [
|
|||||||
consistent-menu-width={false}
|
consistent-menu-width={false}
|
||||||
render-label={renderLabel}
|
render-label={renderLabel}
|
||||||
render-tag={renderTag}
|
render-tag={renderTag}
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user