增加菜单页面,增加小程序管理页面,增加店铺员工页面

This commit is contained in:
YeMingfei666 2025-02-17 10:03:35 +08:00
parent 4adee9121e
commit 960bbada59
29 changed files with 2508 additions and 280 deletions

View File

@ -1,14 +1,50 @@
import request from "@/utils/request";
const baseURL = "account/admin/";
import { Account_BaseUrl } from "@/api/config";
const baseURL = Account_BaseUrl + "/admin/menus";
const MenuApi = {
/** 获取当前用户菜单列表*/
getRoutes() {
return request<any, RouteVO>({
url: `${baseURL}menus`,
return request<any, RouteVO[]>({
url: `${baseURL}`,
method: "get",
});
},
/**获取所有菜单 */
getList(data: getListRequest) {
return request<any, MenuVO[]>({
url: `${baseURL}/list`,
method: "get",
params: data
});
},
get(id: string) {
return request<any, MenuVO>({
url: `${baseURL}/detail`,
method: "get",
data: { id }
});
},
add(data: addRequest) {
return request<any>({
url: `${baseURL}`,
method: "post",
data
});
},
edit(id: string | number, data: editRequest) {
return request<any>({
url: `${baseURL}`,
method: "put",
data: { ...data, id }
});
},
delete(id: number) {
return request<any>({
url: `${baseURL}`,
method: "delete",
data: { id }
});
},
};
export default MenuApi;
@ -27,6 +63,8 @@ export interface RouteVO {
path?: string;
/** 跳转链接 */
redirect?: string;
title: string,
menuId: string | number,
}
/** Meta路由属性 */
export interface Meta {
@ -41,3 +79,230 @@ export interface Meta {
/** 路由title */
title?: string;
}
export interface getListRequest {
/**
*
*/
endTime?: string;
/**
*
*/
startTime?: string;
/**
*
*/
title?: string;
[property: string]: any;
}
/**
* MenuAddDTO
*/
export interface addRequest {
/**
*
*/
activeMenu?: string;
/**
*
*/
cache?: number;
/**
*
*/
component?: string;
/**
*
*/
hidden?: number;
/**
*
*/
icon?: string;
/**
*
*/
iFrame?: number;
/**
*
*/
menuSort?: number;
/**
*
*/
path?: string;
/**
*
*/
permission?: string;
/**
*
*/
pid?: number;
title: string;
/**
* 0 1 2
*/
type: number | null;
[property: string]: any;
}
/**
* MenuEditDTO
*/
export interface editRequest {
/**
* 0 1 2
*/
type: number;
/**
*
*/
activeMenu?: string;
/**
*
*/
cache?: number;
/**
*
*/
component?: string;
/**
*
*/
hidden?: number;
/**
*
*/
icon?: string;
id: number | string;
/**
*
*/
iFrame?: number;
/**
*
*/
menuSort?: number;
/**
*
*/
path?: string;
/**
*
*/
permission?: string;
/**
*
*/
pid?: number;
title: string;
[property: string]: any;
}
/**
*
*
* CzgResult«List«MenuVO»»
*/
export interface getListResponse {
code?: number | null;
data?: MenuVO[] | null;
msg?: null | string;
[property: string]: any;
}
/**
* MenuVO
*/
export interface MenuVO {
/**
*
*/
activeMenu?: string;
/**
*
*/
cache?: boolean;
/**
*
*/
children?: MenuVO[];
/**
*
*/
component?: string;
/**
*
*/
createBy?: string;
/**
*
*/
createTime?: string;
/**
*
*/
hidden?: boolean;
/**
*
*/
icon?: string;
/**
*
*/
iFrame?: boolean;
/**
* 使 01
*/
isShop?: number;
/**
* ID
*/
menuId?: number;
/**
*
*/
menuSort?: number;
/**
*
*/
name?: string;
/**
*
*/
path?: string;
/**
*
*/
permission?: string;
/**
*
*/
permissions?: string[];
/**
* ID
*/
pid?: number;
/**
*
*/
subCount?: number;
/**
*
*/
title?: string;
/**
* 0 1 3
*/
type?: number;
/**
*
*/
updateBy?: string;
/**
*
*/
updateTime?: string;
[property: string]: any;
}

View File

@ -0,0 +1,256 @@
import request from "@/utils/request";
import { Account_BaseUrl } from "@/api/config";
const baseURL = Account_BaseUrl + "/admin/shopStaff";
const ShopStaffApi = {
getList(params: getListRequest) {
return request<any, getListResponse>({
url: `${baseURL}`,
method: "get",
params: params,
});
},
get(id: number | string) {
return request<any>({
url: `${baseURL}/detail`,
method: "get",
params: { id }
});
},
add(data: addRequest) {
return request<any>({
url: `${baseURL}`,
method: "post",
data,
});
},
edit(data: editRequest) {
return request<any>({
url: `${baseURL}`,
method: "put",
data,
});
},
delete(id: number | string) {
return request<any>({
url: `${baseURL}`,
method: "delete",
data: {
id
}
});
},
};
export default ShopStaffApi;
export interface getListRequest {
/**
*
*/
code?: string;
/**
*
*/
name?: string;
[property: string]: any;
}
/**
*
*
* CzgResult«Page«ShopStaff»»
*/
export interface getListResponse {
code?: number | null;
data?: PageShopStaff;
msg?: null | string;
[property: string]: any;
}
/**
* Page«ShopStaff»
*/
export interface PageShopStaff {
/**
*
*/
maxPageSize?: number | null;
/**
* COUNT
*/
optimizeCountQuery?: boolean | null;
/**
*
*/
pageNumber?: number | null;
/**
*
*/
pageSize?: number | null;
/**
*
*/
records?: ShopStaff[] | null;
/**
*
*/
totalPage?: number | null;
/**
*
*/
totalRow?: number | null;
[property: string]: any;
}
/**
*
*
* ShopStaff
*/
export interface ShopStaff {
/**
*
*/
code?: null | string;
createTime?: null | string;
/**
* 1 0
*/
discountType?: null | string;
/**
* 使 sys_user id
*/
id?: number | null;
/**
* 01
*/
isManage?: number | null;
/**
* pc端登录 01
*/
isPc?: number | null;
/**
*
*/
maxDiscountAmount?: number | null;
/**
*
*/
name?: null | string;
/**
* shopId
*/
shopId?: number | null;
/**
* 10
*/
status?: boolean | null;
/**
* master商户账号staff员工
*/
type?: null | string;
updateTime?: null | string;
[property: string]: any;
}
/**
*
*
* ShopStaffAddDTO
*/
export interface addRequest {
/**
*
*/
accountName: null | string;
/**
*
*/
accountPwd: null | string;
/**
*
*/
code: null | string;
/**
* 1 0
*/
discountType?: number | null;
/**
*
*/
isManage?: number | null;
/**
* pc登录
*/
isPc?: number | null;
/**
*
*/
maxDiscountAmount?: number | null;
/**
*
*/
name?: null | string;
/**
*
*/
phone: null | string;
/**
* id
*/
roleId: number | null;
/**
* 10
*/
status?: number | null;
[property: string]: any;
}
/**
*
*
* ShopStaffEditDTO
*/
export interface editRequest {
/**
*
*/
accountPwd?: null | string;
/**
*
*/
code?: null | string;
/**
* 1 0
*/
discountType?: number | null;
id: number | null;
/**
*
*/
isManage?: number | null;
/**
* pc登录
*/
isPc?: number | null;
/**
*
*/
maxDiscountAmount?: number | null;
/**
*
*/
name?: null | string;
/**
*
*/
phone?: null | string;
/**
* id
*/
roleId?: number | null;
/**
* 10
*/
status?: number | null;
[property: string]: any;
}

View File

@ -1,6 +1,6 @@
import request from "@/utils/request";
import { System_BaseUrl } from "@/api/config";
const baseURL = System_BaseUrl + "/admin/order";
import { Order_BaseUrl } from "@/api/config";
const baseURL = Order_BaseUrl + "/admin/order";
const OrderApi = {
getList(params: getListRequest) {
return request<any, getListResponse>({
@ -9,9 +9,9 @@ const OrderApi = {
params: params,
});
},
add() {},
edit() {},
delete() {},
add() { },
edit() { },
delete() { },
};
export default OrderApi;

View File

@ -0,0 +1,187 @@
import request from "@/utils/request";
import { System_BaseUrl } from "@/api/config";
const baseURL = System_BaseUrl + "/admin/miniAppPages";
const VersionApi = {
getList(data: getListRequest) {
return request<any, getListResponse>({
url: `${baseURL}/page`,
method: "get",
params: data
});
},
add(data: addRequest) {
return request<any>({
url: `${baseURL}`,
method: "post",
data,
});
},
edit(data: editRequest) {
return request<any>({
url: `${baseURL}`,
method: "put",
data,
});
},
delete(id: string) {
return request<any>({
url: `${baseURL}/` + id,
method: "delete",
});
},
};
export default VersionApi;
export interface getListRequest {
/**
*
*/
name?: string;
/**
*
*/
page?: number;
/**
*
*/
path?: string;
/**
*
*/
size?: number;
/**
* -1 1 0
*/
status?: number;
[property: string]: any;
}
/**
* CzgResultPageMiniAppPagesDTO
*/
export interface getListResponse {
code?: number;
data?: PageMiniAppPagesDTO;
msg?: string;
[property: string]: any;
}
/**
* PageMiniAppPagesDTO
*/
export interface PageMiniAppPagesDTO {
maxPageSize?: number;
optimizeCountQuery?: boolean;
pageNumber?: number;
pageSize?: number;
records?: MiniAppPagesDTO[];
totalPage?: number;
totalRow?: number;
[property: string]: any;
}
/**
* MiniAppPagesDTO
*/
export interface MiniAppPagesDTO {
/**
*
*/
description?: string;
/**
*
*/
icon?: string;
/**
* id
*/
id?: number;
/**
*
*/
name?: string;
/**
*
*/
path?: string;
/**
*
*/
sort?: number;
/**
* 1:启用 0:禁用
*/
status?: number;
[property: string]: any;
}
/**
* MiniAppPagesDTOInsertGroup
*/
export interface addRequest {
/**
*
*/
description?: string;
/**
*
*/
icon?: string;
/**
* id
*/
id?: number;
/**
*
*/
name: string;
/**
*
*/
path: string;
/**
*
*/
sort?: number;
/**
* 1:启用 0:禁用
*/
status?: number;
[property: string]: any;
}
/**
* MiniAppPagesDTOUpdateGroup
*/
export interface editRequest {
/**
*
*/
description?: string;
/**
*
*/
icon?: string;
/**
* id
*/
id: number;
/**
*
*/
name: string;
/**
*
*/
path: string;
/**
*
*/
sort?: number;
/**
* 1:启用 0:禁用
*/
status?: number;
[property: string]: any;
}

View File

@ -17,7 +17,10 @@
>
<el-row :gutter="20">
<template v-for="item in formItems" :key="item.prop">
<el-col v-show="!item.hidden" v-bind="item.col">
<template v-if="item.type === 'title'">
<h3>{{ item.label }}</h3>
</template>
<el-col v-else v-show="!item.hidden" v-bind="item.col">
<el-form-item :label="item.label" :prop="item.prop">
<!-- Label -->
<template v-if="item.tips" #label>
@ -40,7 +43,7 @@
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- textarea 输入框 -->
<template v-if="item.type === 'textarea'">
<template v-else-if="item.type === 'textarea'">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" type="textarea" />
</template>
<!-- Select 选择器 -->
@ -51,6 +54,14 @@
</template>
</el-select>
</template>
<!-- radio-button 选择器 -->
<template v-else-if="item.type === 'radio-button'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio-button v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
@ -140,7 +151,12 @@
>
<el-row :gutter="20">
<template v-for="item in formItems" :key="item.prop">
<el-col v-show="!item.hidden" v-bind="item.col">
<template v-if="item.type === 'title'">
<el-col :span="24">
<h3>{{ item.label }}</h3>
</el-col>
</template>
<el-col v-else v-show="!item.hidden" v-bind="item.col">
<el-form-item :label="item.label" :prop="item.prop">
<!-- Label -->
<template v-if="item.tips" #label>
@ -167,9 +183,10 @@
/>
</template>
<!-- textarea 输入框 -->
<template v-if="item.type === 'textarea'">
<template v-else-if="item.type === 'textarea'">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" type="textarea" />
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="formData[item.prop]" v-bind="item.attrs">
@ -178,6 +195,14 @@
</template>
</el-select>
</template>
<!-- radio-button 选择器 -->
<template v-else-if="item.type === 'radio-button'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio-button v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
@ -390,4 +415,8 @@ function handleDisabled(disable: boolean) {
defineExpose({ setModalVisible, getFormData, setFormData, setFormItemData, handleDisabled });
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
h3 {
color: rgb(63, 158, 255);
}
</style>

View File

@ -233,6 +233,8 @@ export type IFormItems<T = any> = Array<{
| "input-number"
| "text"
| "UpImage"
| "radio-button"
| "title"
| "custom";
// 组件属性
disabled?: boolean;

View File

@ -1,22 +1,37 @@
/**
*
*/
// export const enum MenuTypeEnum {
// /**
// * 目录
// */
// CATALOG = "CATALOG",
// /**
// * 菜单
// */
// MENU = "MENU",
// /**
// * 按钮
// */
// BUTTON = "BUTTON",
// /**
// * 外链
// */
// EXTLINK = "EXTLINK",
// }
export const enum MenuTypeEnum {
/**
*
*/
CATALOG = "CATALOG",
/**
*
*/
MENU = "MENU",
MENU = 0,
/**
*
*/
BUTTON = "BUTTON",
BUTTON = 1,
/**
*
*
*/
EXTLINK = "EXTLINK",
EXTLINK = 2,
}

View File

@ -134,7 +134,7 @@ export const constantRoutes: RouteRecordRaw[] = [
},
{
path: "staff",
component: () => import("@/views/shop/staff.vue"),
component: () => import("@/views/shop/staff/index.vue"),
name: "shopStaff",
meta: {
title: "员工列表",
@ -189,6 +189,26 @@ export const constantRoutes: RouteRecordRaw[] = [
keepAlive: true,
},
},
{
path: "menu",
component: () => import("@/views/admim/system/menu/index.vue"),
name: "adminSysMenu",
meta: {
title: "菜单管理",
affix: false,
keepAlive: true,
},
},
{
path: "miniAppPages",
component: () => import("@/views/admim/system/miniAppPages/index.vue"),
name: "adminSysMiniAppPages",
meta: {
title: "小程序页面",
affix: false,
keepAlive: true,
},
},
{
path: "version",
component: () => import("@/views/admim/system/version/index.vue"),

View File

@ -86,6 +86,7 @@ declare global {
value: string | number;
/** 文本 */
label: string;
[property: string]: any
/** 子列表 */
children?: OptionType[];
}
@ -103,4 +104,4 @@ declare global {
messageList: Array<string>;
}
}
export {};
export { };

View File

@ -0,0 +1,558 @@
<template>
<div class="app-container">
<div class="search-bar">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="关键字" prop="title">
<el-input
v-model="queryParams.title"
placeholder="菜单名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
<el-button icon="refresh" @click="handleResetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<el-card shadow="never">
<div class="mb-10px">
<el-button
v-hasPerm="['sys:menu:add']"
type="success"
icon="plus"
@click="handleOpenDialog('0')"
>
新增
</el-button>
</div>
<el-table
v-loading="loading"
:data="menuTableData"
highlight-current-row
row-key="menuId"
:tree-props="{
children: 'children',
hasChildren: 'hasChildren',
}"
@row-click="handleRowClick"
>
<el-table-column label="菜单名称" min-width="100">
<template #default="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="图标" align="center" width="80">
<template #default="scope">
<div v-if="scope.row.icon" :class="`i-svg:${scope.row.icon}`" />
</template>
</el-table-column>
<el-table-column label="排序" align="left" width="150" prop="menuSort" />
<el-table-column label="权限标识" align="center" width="200" prop="perm" />
<el-table-column label="组件路径" align="left" width="250" prop="component" />
<el-table-column label="外链" align="left" width="80" prop="iFrame">
<template #default="scope">
{{ scope.row.iFrame ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column label="缓存" align="left" width="80" prop="iFrame">
<template #default="scope">
{{ scope.row.iFrame ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column label="可见" align="left" width="80" prop="iFrame">
<template #default="scope">
{{ scope.row.iFrame ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column label="商家可用" align="left" width="80" prop="iFrame">
<template #default="scope">
{{ scope.row.iFrame ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column label="商家可用" align="left" width="80" prop="iFrame">
<template #default="scope">
{{ scope.row.iFrame ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column fixed="right" align="center" label="操作" width="220">
<template #default="scope">
<el-button
v-if="scope.row.type == 'MENU' || scope.row.type == 'MENU'"
v-hasPerm="['sys:menu:add']"
type="primary"
link
size="small"
icon="plus"
@click.stop="handleOpenDialog(scope.row.id)"
>
新增
</el-button>
<el-button
v-hasPerm="['sys:menu:edit']"
type="primary"
link
size="small"
icon="edit"
@click.stop="handleOpenDialog(undefined, scope.row.id)"
>
编辑
</el-button>
<el-button
v-hasPerm="['sys:menu:delete']"
type="danger"
link
size="small"
icon="delete"
@click.stop="handleDelete(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-drawer v-model="dialog.visible" :title="dialog.title" size="50%" @close="handleCloseDialog">
<el-form ref="editRequestRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="父级菜单" prop="pid">
<el-tree-select
v-model="formData.pid"
placeholder="选择上级菜单"
:data="menuOptions"
filterable
check-strictly
:render-after-expand="false"
/>
</el-form-item>
<el-form-item label="菜单名称" prop="title">
<el-input v-model="formData.title" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="formData.type" @change="handleMenuTypeChange">
<el-radio :value="0">菜单</el-radio>
<el-radio :value="1">按钮</el-radio>
<el-radio :value="2">接口</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.iFrame == 1" label="外链地址" prop="path">
<el-input v-model="formData.routePath" placeholder="请输入外链完整路径" />
</el-form-item>
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="routeName">
<template #label>
<div class="flex-y-center">
路由名称
<el-tooltip placement="bottom" effect="light">
<template #content>
如果需要开启缓存需保证页面 defineOptions 中的 name 与此处一致建议使用驼峰
</template>
<el-icon class="ml-1 cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="formData.routeName" placeholder="User" />
</el-form-item>
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="routePath">
<template #label>
<div class="flex-y-center">
路由路径
<el-tooltip placement="bottom" effect="light">
<template #content>
定义应用中不同页面对应的 URL 路径目录需以 / 开头菜单项不用例如系统管理目录
/system user
</template>
<el-icon class="ml-1 cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input
v-if="formData.type == MenuTypeEnum.MENU"
v-model="formData.routePath"
placeholder="system"
/>
<el-input v-else v-model="formData.routePath" placeholder="user" />
</el-form-item>
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="component">
<template #label>
<div class="flex-y-center">
组件路径
<el-tooltip placement="bottom" effect="light">
<template #content>
组件页面完整路径相对于 src/views/ system/user/index缺省后缀 .vue
</template>
<el-icon class="ml-1 cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="formData.component" placeholder="system/user/index" style="width: 95%">
<template v-if="formData.type == MenuTypeEnum.MENU" #prepend>src/views/</template>
<template v-if="formData.type == MenuTypeEnum.MENU" #append>.vue</template>
</el-input>
</el-form-item>
<el-form-item v-if="formData.type == MenuTypeEnum.MENU">
<template #label>
<div class="flex-y-center">
路由参数
<el-tooltip placement="bottom" effect="light">
<template #content>
组件页面使用 `useRoute().query.参数名` 获取路由参数值
</template>
<el-icon class="ml-1 cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<div v-if="!formData.params || formData.params.length === 0">
<el-button type="success" plain @click="formData.params = [{ key: '', value: '' }]">
添加路由参数
</el-button>
</div>
<div v-else>
<div v-for="(item, index) in formData.params" :key="index">
<el-input v-model="item.key" placeholder="参数名" style="width: 100px" />
<span class="mx-1">=</span>
<el-input v-model="item.value" placeholder="参数值" style="width: 100px" />
<el-icon
v-if="formData.params.indexOf(item) === formData.params.length - 1"
class="ml-2 cursor-pointer color-[var(--el-color-success)]"
style="vertical-align: -0.15em"
@click="formData.params.push({ key: '', value: '' })"
>
<CirclePlusFilled />
</el-icon>
<el-icon
class="ml-2 cursor-pointer color-[var(--el-color-danger)]"
style="vertical-align: -0.15em"
@click="formData.params.splice(formData.params.indexOf(item), 1)"
>
<DeleteFilled />
</el-icon>
</div>
</div>
</el-form-item>
<el-form-item v-if="formData.type !== MenuTypeEnum.BUTTON" prop="hidden" label="显示状态">
<el-radio-group v-model="formData.hidden">
<el-radio :value="1">显示</el-radio>
<el-radio :value="0">隐藏</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="formData.type === MenuTypeEnum.MENU || formData.type === MenuTypeEnum.MENU"
>
<template #label>
<div class="flex-y-center">
始终显示
<el-tooltip placement="bottom" effect="light">
<template #content>
选择即使目录或菜单下只有一个子节点也会显示父节点
<br />
选择如果目录或菜单下只有一个子节点则只显示该子节点隐藏父节点
<br />
如果是叶子节点请选择
</template>
<el-icon class="ml-1 cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-radio-group v-model="formData.alwaysShow">
<el-radio :value="1"></el-radio>
<el-radio :value="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.type === MenuTypeEnum.MENU" label="缓存页面">
<el-radio-group v-model="formData.cache">
<el-radio :value="1">开启</el-radio>
<el-radio :value="0">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="menuSort">
<el-input-number
v-model="formData.menuSort"
style="width: 100px"
controls-position="right"
:min="0"
/>
</el-form-item>
<!-- 权限标识 -->
<el-form-item v-if="formData.type == MenuTypeEnum.BUTTON" label="权限标识" prop="perm">
<el-input v-model="formData.perm" placeholder="sys:user:add" />
</el-form-item>
<el-form-item v-if="formData.type !== MenuTypeEnum.BUTTON" label="图标" prop="icon">
<!-- 图标选择器 -->
<icon-select v-model="formData.icon" />
</el-form-item>
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" label="跳转路由">
<el-input v-model="formData.redirect" placeholder="跳转路由" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="handleCloseDialog"> </el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "SysMenu",
inheritAttrs: false,
});
import MenuAPI, { getListRequest, editRequest, addRequest, MenuVO } from "@/api/account/menu";
import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
const queryFormRef = ref();
const editRequestRef = ref();
const loading = ref(false);
const dialog = reactive({
title: "新增菜单",
visible: false,
});
//
const queryParams = reactive<getListRequest>({});
//
const menuTableData = ref<MenuVO[]>([]);
//
const menuOptions = ref<OptionType[]>([]);
//
{
}
const initialeditRequestData = ref<editRequest>({
id: "",
pid: 0,
hidden: 0,
menuSort: 1,
type: 0, //
alwaysShow: 0,
cache: 1,
params: [],
title: "",
icon: "",
iFrame: 0,
permission: "",
});
//
const formData = ref({ ...initialeditRequestData.value });
//
const rules = reactive({
pid: [{ required: true, message: "请选择父级菜单", trigger: "blur" }],
title: [{ required: true, message: "请输入菜单名称", trigger: "blur" }],
type: [{ required: true, message: "请选择菜单类型", trigger: "blur" }],
routeName: [{ required: true, message: "请输入路由名称", trigger: "blur" }],
routePath: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
component: [{ required: true, message: "请输入组件路径", trigger: "blur" }],
hidden: [{ required: true, message: "请选择显示状态", trigger: "change" }],
});
// ID
const selectedMenuId = ref<string | undefined>();
//
function handleQuery() {
loading.value = true;
MenuAPI.getList(queryParams)
.then((data) => {
menuTableData.value = data;
})
.finally(() => {
loading.value = false;
});
}
//
function handleResetQuery() {
queryFormRef.value.resetFields();
handleQuery();
}
//
function handleRowClick(row: MenuVO) {
selectedMenuId.value = row.id;
}
/**
* 返回格式化后的menus菜单数据
*/
function returnFilterMenuData(menus: MenuVO[]): OptionType[] {
return menus.map((menu) => {
const { title, menuId, children, ...rest } = menu;
return {
value: menuId,
label: title,
children: children && returnFilterMenuData(children),
...rest,
} as OptionType;
});
}
/**
* 打开表单弹窗
*
* @param pid 父菜单ID
* @param menuId 菜单ID
*/
function handleOpenDialog(pid?: string, menuId?: string) {
MenuAPI.getList({})
.then((data) => {
menuOptions.value = [{ value: "0", label: "顶级菜单", children: returnFilterMenuData(data) }];
})
.then(() => {
dialog.visible = true;
if (menuId) {
dialog.title = "编辑菜单";
MenuAPI.get(menuId).then((data) => {
initialeditRequestData.value = { ...data };
formData.value = data;
});
} else {
dialog.title = "新增菜单";
formData.value.pid = pid?.toString();
}
});
}
//
function handleMenuTypeChange() {
//
if (formData.value.type !== initialeditRequestData.value.type) {
if (formData.value.type === MenuTypeEnum.MENU) {
//
if (initialeditRequestData.value.type === MenuTypeEnum.MENU) {
formData.value.component = "";
} else {
//
formData.value.routePath = initialeditRequestData.value.routePath;
formData.value.component = initialeditRequestData.value.component;
}
}
}
}
/**
* 提交表单
*/
function handleSubmit() {
editRequestRef.value.validate((isValid: boolean) => {
if (isValid) {
const menuId = formData.value.id;
if (menuId) {
//
if (formData.value.pid == menuId) {
ElMessage.error("父级菜单不能为当前菜单");
return;
}
MenuAPI.edit(menuId, formData.value).then(() => {
ElMessage.success("修改成功");
handleCloseDialog();
handleQuery();
});
} else {
MenuAPI.add(formData.value).then(() => {
ElMessage.success("新增成功");
handleCloseDialog();
handleQuery();
});
}
}
});
}
//
function handleDelete(menuId: number) {
if (!menuId) {
ElMessage.warning("请勾选删除项");
return false;
}
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(
() => {
loading.value = true;
MenuAPI.delete(menuId)
.then(() => {
ElMessage.success("删除成功");
handleQuery();
})
.finally(() => {
loading.value = false;
});
},
() => {
ElMessage.info("已取消删除");
}
);
}
function resetForm() {
editRequestRef.value.resetFields();
editRequestRef.value.clearValidate();
formData.value = {
id: "",
pid: 0,
hidden: 0,
menuSort: 1,
type: 0, //
alwaysShow: 0,
cache: 1,
params: [],
title: "",
icon: "",
iFrame: 0,
permission: "",
};
}
//
function handleCloseDialog() {
dialog.visible = false;
resetForm();
}
onMounted(() => {
handleQuery();
});
</script>

View File

@ -0,0 +1,89 @@
import VersionApi, { type addRequest } from "@/api/system/version";
import { sourceOptions, typeOptions, isForceOptions } from "./config";
import type { IModalConfig } from "@/components/CURD/types";
const modalConfig: IModalConfig<addRequest> = {
pageName: "sys:user",
dialog: {
title: "新增版本",
width: 800,
draggable: true,
},
form: {
labelWidth: 140,
},
formAction: function (data) {
return VersionApi.add({ ...data, url: typeof data.url === "string" ? data.url : data.url[0] });
},
beforeSubmit(data) {
console.log("提交之前处理", data);
},
formItems: [
{
label: "渠道",
prop: "source",
rules: [{ required: true, message: "请选择渠道", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择渠道",
},
options: sourceOptions,
},
{
label: "类型",
prop: "type",
rules: [{ required: true, message: "请选择类型", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择类型",
},
col: {
xs: 24,
sm: 12,
},
options: typeOptions,
},
{
type: "input",
label: "版本号",
prop: "version",
rules: [{ required: true, message: "请输入版本号", trigger: "blur" }],
attrs: {
placeholder: "请输入版本号",
},
},
{
type: "radio",
label: "是否强制更新",
prop: "isForce",
rules: [{ required: true, message: "请输入版本号", trigger: "blur" }],
attrs: {
placeholder: "请输入版本号",
},
initialValue: 0,
options: isForceOptions,
},
{
type: "textarea",
label: "更新提示内容",
prop: "message",
rules: [{ required: true, message: "请输入更新提示内容", trigger: "blur" }],
attrs: {
placeholder: "请输入更新提示内容",
},
},
{
type: "custom",
label: "版本文件",
prop: "url",
rules: [{ required: true, message: "请上传版本文件", trigger: "blur" }],
attrs: {
placeholder: "请上传版本文件",
},
initialValue: [],
},
],
};
// 如果有异步数据会修改配置的推荐用reactive包裹而纯静态配置的可以直接导出
export default reactive(modalConfig);

View File

@ -0,0 +1,45 @@
import { property } from "lodash";
export const sourceOptions: options[] = [
{ label: "桌面端", value: "pc" },
{ label: "管理端", value: "manager_app" },
{ label: "电话机点餐", value: "phone_book " },
];
export const typeOptions: options[] = [
{ label: "windows", value: "0" },
{ label: "安卓", value: "1" },
{ label: "IOS", value: "2" },
];
export const isForceOptions: options[] = [
{ label: "不强制更新", value: 0 },
{ label: "强制更新", value: 1 },
];
export type optionsType = "source" | "type" | "isForce";
export function returnOptions(type: optionsType) {
if (type === "source") {
return sourceOptions;
}
if (type === "type") {
return typeOptions;
}
if (type === "isForce") {
return isForceOptions;
}
}
export function returnOptionsLabel(optionsType: optionsType, value: string | number) {
const options = returnOptions(optionsType);
if (!options) {
return "";
}
const option = options.find((item) => item.value === value);
return option ? option.label : "";
}
export interface options {
label: string;
value: string | number;
[property: string]: any;
}

View File

@ -0,0 +1,85 @@
import VersionApi from "@/api/system/version";
import type { editRequest } from "@/api/system/version";
import type { IContentConfig } from "@/components/CURD/types";
const contentConfig: IContentConfig<editRequest> = {
pageName: "sys:user",
table: {
border: true,
highlightCurrentRow: true,
},
pagination: {
background: true,
layout: "prev,pager,next,jumper,total,sizes",
pageSize: 20,
pageSizes: [10, 20, 30, 50],
},
indexAction: function (params) {
return VersionApi.getList();
},
deleteAction: VersionApi.delete,
// modifyAction: function (data) {
// // return VersionApi.edit(data);
// },
pk: "id",
toolbar: ["add"],
defaultToolbar: ["refresh", "filter", "search"],
cols: [
{ type: "selection", width: 50, align: "center" },
{ label: "id", align: "center", prop: "id", width: 100, show: true },
{
label: "渠道",
align: "center",
prop: "source",
width: 120,
templet: "custom",
slotName: "options",
},
{
label: "类型",
align: "center",
prop: "type",
width: 120,
templet: "custom",
slotName: "options",
},
{
label: "版本号",
align: "center",
width: 120,
prop: "version",
},
{
label: "是否强制升级",
align: "center",
prop: "isForce",
width: 120,
templet: "switch",
slotName: "isForce",
},
{
label: "更新内容",
align: "center",
prop: "message",
width: 240,
},
{
label: "下载地址",
align: "center",
prop: "url",
templet: "url",
slotName: "url",
},
{
label: "操作",
align: "center",
fixed: "right",
width: 280,
templet: "tool",
operat: ["edit", "delete"],
},
],
};
export default contentConfig;

View File

@ -0,0 +1,86 @@
import VersionApi, { type editRequest } from "@/api/system/version";
import type { IModalConfig } from "@/components/CURD/types";
import { sourceOptions, typeOptions, isForceOptions } from "./config";
const modalConfig: IModalConfig<editRequest> = {
pageName: "sys:user",
dialog: {
title: "编辑版本",
width: 800,
draggable: true,
},
pk: "id",
formAction: function (data) {
return VersionApi.edit({ ...data, url: typeof data.url === "string" ? data.url : data.url[0] });
},
beforeSubmit(data) {
console.log("提交之前处理", data);
},
formItems: [
{
label: "渠道",
prop: "source",
rules: [{ required: true, message: "请选择渠道", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择渠道",
},
options: sourceOptions,
},
{
label: "类型",
prop: "type",
rules: [{ required: true, message: "请选择类型", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择类型",
},
col: {
xs: 24,
sm: 12,
},
options: typeOptions,
},
{
type: "input",
label: "版本号",
prop: "version",
rules: [{ required: true, message: "请输入版本号", trigger: "blur" }],
attrs: {
placeholder: "请输入版本号",
},
},
{
type: "radio",
label: "是否强制更新",
prop: "isForce",
rules: [{ required: true, message: "请输入版本号", trigger: "blur" }],
attrs: {
placeholder: "请输入版本号",
},
initialValue: 0,
options: isForceOptions,
},
{
type: "textarea",
label: "更新提示内容",
prop: "message",
rules: [{ required: true, message: "请输入更新提示内容", trigger: "blur" }],
attrs: {
placeholder: "请输入更新提示内容",
},
},
{
type: "custom",
label: "版本文件",
prop: "url",
rules: [{ required: true, message: "请上传版本文件", trigger: "blur" }],
attrs: {
placeholder: "请上传版本文件",
},
initialValue: [],
},
],
};
export default reactive(modalConfig);

View File

@ -0,0 +1,21 @@
import type { ISearchConfig } from "@/components/CURD/types";
const searchConfig: ISearchConfig = {
pageName: "sys:user",
formItems: [
{
type: "input",
label: "版本号",
prop: "keywords",
attrs: {
placeholder: "请输入版本号",
clearable: true,
style: {
width: "200px",
},
},
},
],
};
export default searchConfig;

View File

@ -0,0 +1,117 @@
<template>
<div class="app-container">
<!-- 列表 -->
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #options="scope">
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
</template>
</page-content>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
</div>
</template>
<script setup lang="ts">
// import Api from "@/api/system/miniProgramVersion";
import type { IObject, IOperatData } from "@/components/CURD/types";
import usePage from "@/components/CURD/usePage";
import addModalConfig from "./config/add";
import contentConfig from "./config/content";
import editModalConfig from "./config/edit";
import searchConfig from "./config/search";
import { returnOptionsLabel } from "./config/config";
const {
searchRef,
contentRef,
addModalRef,
editModalRef,
handleQueryClick,
handleResetClick,
// handleAddClick,
// handleEditClick,
handleSubmitClick,
handleExportClick,
handleSearchClick,
handleFilterChange,
} = usePage();
//
async function handleAddClick() {
addModalRef.value?.setModalVisible();
// addModalConfig.formItems[2]!.attrs!.data =
}
//
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
editModalRef.value?.setModalVisible();
// id
// const data = await Api.getFormData(row.id);
console.log({ ...row, url: [row.url] });
editModalRef.value?.setFormData({ ...row, url: [row.url] });
}
1;
//
function handleToolbarClick(name: string) {
console.log(name);
if (name === "custom1") {
ElMessage.success("点击了自定义1按钮");
}
}
//
async function handleOperatClick(data: IOperatData) {
console.log(data);
}
</script>

View File

@ -31,7 +31,7 @@
v-loading="loading"
:data="roleList"
highlight-current-row
border
:border="true"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
@ -209,7 +209,7 @@ defineOptions({
});
import RoleApi, { SysRole, addRequest, getListRequest } from "@/api/account/role";
import MenuAPI from "@/api/system/menu";
import MenuAPI, { type RouteVO } from "@/api/account/menu";
const queryFormRef = ref();
const addRequestRef = ref();
@ -228,7 +228,7 @@ const queryParams = reactive<getListRequest>({
//
const roleList = ref<SysRole[]>();
//
const menuPermOptions = ref<OptionType[]>([]);
const menuPermOptions = ref<RouteVO[]>([]);
//
const dialog = reactive({
@ -372,6 +372,23 @@ function handleDelete(roleId?: number) {
);
}
interface typeRoute extends RouteVO {
label: string | number;
value: string | number;
children: typeRoute[];
}
function returnMenu(menu: RouteVO[]): typeRoute[] {
return menu.map((v) => {
return {
...v,
label: v.title,
value: v.menuId,
children: v.children ? returnMenu(v.children) : [],
};
});
}
//
async function handleOpenAssignPermDialog(row: SysRole) {
const roleId = row.id;
@ -383,7 +400,8 @@ async function handleOpenAssignPermDialog(row: SysRole) {
checkedRole.value.name = row.name as string;
//
menuPermOptions.value = await MenuAPI.getOptions();
let arr: RouteVO[] = await MenuAPI.getRoutes();
menuPermOptions.value = returnMenu(arr);
//
// RoleApi.getRoleMenuIds(roleId)

View File

@ -1,133 +0,0 @@
import type { IContentConfig } from "@/components/CURD/types";
const contentConfig: IContentConfig = {
pageName: "sys:user",
table: {
showOverflowTooltip: true,
},
toolbar: [],
indexAction: function (params) {
// 模拟发起网络请求获取列表数据
console.log("indexAction:", params);
return Promise.resolve({
total: 2,
list: [
{
id: 1,
username: "tom",
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
percent: 99,
price: 10,
url: "https://www.baidu.com",
icon: "el-icon-setting",
gender: 1,
status: 1,
status2: 1,
sort: 99,
createTime: 1715647982437,
},
{
id: 2,
username: "jerry",
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
percent: 88,
price: 999,
url: "https://www.google.com",
icon: "el-icon-user",
gender: 0,
status: 0,
status2: 0,
sort: 0,
createTime: 1715648977426,
},
],
});
},
modifyAction(data) {
// 模拟发起网络请求修改字段
// console.log("modifyAction:", data);
ElMessage.success(JSON.stringify(data));
return Promise.resolve(null);
},
cols: [
{ type: "index", width: 50, align: "center" },
{ label: "ID", align: "center", prop: "id", show: false },
{ label: "文本", align: "center", prop: "username" },
{ label: "图片", align: "center", prop: "avatar", templet: "image" },
{
label: "百分比",
align: "center",
prop: "percent",
templet: "percent",
},
{
label: "货币符",
align: "center",
prop: "price",
templet: "price",
priceFormat: "$",
},
{ label: "链接", align: "center", prop: "url", width: 180, templet: "url" },
{ label: "图标", align: "center", prop: "icon", templet: "icon" },
{
label: "列表值",
align: "center",
prop: "gender",
templet: "list",
selectList: { "0": "女", "1": "男" },
},
{
label: "自定义",
align: "center",
prop: "status",
templet: "custom",
slotName: "status",
},
{
label: "Switch",
align: "center",
prop: "status2",
templet: "switch",
activeValue: 1,
inactiveValue: 0,
activeText: "启用",
inactiveText: "禁用",
},
{
label: "输入框",
align: "center",
prop: "sort",
templet: "input",
inputType: "number",
},
{
label: "日期格式化",
align: "center",
prop: "createTime",
minWidth: 120,
templet: "date",
dateFormat: "YYYY/MM/DD HH:mm:ss",
},
{
label: "操作栏",
align: "center",
fixed: "right",
width: 220,
templet: "tool",
operat: [
{
name: "reset_pwd",
auth: "password:reset",
icon: "refresh-left",
text: "重置密码",
type: "primary",
render(row) {
return row.id === 1;
},
},
],
},
],
};
export default contentConfig;

View File

@ -1,96 +1,74 @@
<template>
<div class="app-container">
<!-- 列表 -->
<template v-if="isA">
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #options="scope">
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
</template>
</page-content>
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #options="scope">
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
</template>
</page-content>
<!-- 新增 -->
<page-modal
ref="addModalRef"
:modal-config="addModalConfig"
@submit-click="handleSubmitClick"
>
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
</template>
<template v-else>
<page-content
ref="contentRef"
:content-config="contentConfig2"
@operat-click="handleOperatClick"
>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
</page-content>
</template>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
</div>
</template>
<script setup lang="ts">
import VersionApi from "@/api/system/version";
import DeptAPI from "@/api/system/dept";
import RoleAPI from "@/api/system/role";
import type { IObject, IOperatData } from "@/components/CURD/types";
import usePage from "@/components/CURD/usePage";
import addModalConfig from "./config/add";
import contentConfig from "./config/content";
import contentConfig2 from "./config/content2";
import editModalConfig from "./config/edit";
import searchConfig from "./config/search";
import { returnOptionsLabel } from "./config/config";
@ -113,10 +91,7 @@ const {
//
async function handleAddClick() {
addModalRef.value?.setModalVisible();
//
// addModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
//
// addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// addModalConfig.formItems[2]!.attrs!.data =
}
//
async function handleEditClick(row: IObject) {
@ -138,38 +113,5 @@ function handleToolbarClick(name: string) {
//
async function handleOperatClick(data: IOperatData) {
console.log(data);
//
if (data.name === "reset_pwd") {
ElMessageBox.prompt("请输入用户「" + data.row.username + "」的新密码", "重置密码", {
confirmButtonText: "确定",
cancelButtonText: "取消",
}).then(({ value }) => {
if (!value || value.length < 6) {
ElMessage.warning("密码至少需要6位字符请重新输入");
return false;
}
VersionApi.resetPassword(data.row.id, value).then(() => {
ElMessage.success("密码重置成功,新密码是:" + value);
});
});
} else if (data.name === "detail") {
//
editModalRef.value?.handleDisabled(true);
//
editModalRef.value?.setModalVisible();
//
editModalConfig.drawer = { ...editModalConfig.drawer, title: "用户详情" };
//
editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
//
editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// id
const formData = await VersionApi.getFormData(data.row.id);
//
editModalRef.value?.setFormData(formData);
}
}
//
const isA = ref(true);
</script>

View File

@ -1,4 +1,4 @@
import type { statusType } from "@/api/system/order";
import type { statusType } from "@/api/order/order";
export const statusOptions: statusOptions[] = [
{ label: "全部", value: "" },
{ label: "待支付", value: "unpaid" },

View File

@ -1,4 +1,4 @@
import OrderApi from "@/api/system/order";
import OrderApi from "@/api/order/order";
import type { editRequest } from "@/api/system/version";
import type { IContentConfig } from "@/components/CURD/types";

View File

@ -1 +0,0 @@
<template></template>

View File

@ -0,0 +1,14 @@
<template>
<h3>{{ title }}</h3>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
default: "",
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,177 @@
import ShopStaffApi, { type addRequest } from "@/api/account/shopStaff";
import type { IModalConfig } from "@/components/CURD/types";
import { discountTypeOptions } from './config'
const modalConfig: IModalConfig<addRequest> = {
pageName: "sys:user",
dialog: {
title: "添加员工",
width: 800,
draggable: true,
},
form: {
labelWidth: 140,
},
formAction: function (data) {
return ShopStaffApi.add(data);
},
beforeSubmit(data) {
console.log("提交之前处理", data);
},
formItems: [
{
label: "基本信息设置",
prop: "",
type: "title",
slotName: "title",
},
{
label: "角色",
prop: "roleId",
rules: [{ required: true, message: "请选择角色", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择角色",
},
col: {
xs: 24,
sm: 12,
},
options: []
},
{
label: "员工姓名",
prop: "name",
rules: [{ required: false, message: "请输入员工姓名", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入员工姓名",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "员工编号",
prop: "code",
rules: [{ required: true, message: "请输入员工编号", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入员工编号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "手机号",
prop: "phone",
rules: [{
required: true,
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
}],
type: "input",
attrs: {
placeholder: "请输入手机号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "登录账号",
prop: "accountName",
rules: [{ required: true, message: "请输入登录账号", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入登录账号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "登录密码",
prop: "accountPwd",
rules: [{ required: true, message: "请输入登录密码", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入登录密码",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "优惠类型",
prop: "discountType",
rules: [{ required: false, message: "请选择优惠类型", trigger: "blur" }],
type: "radio-button",
col: {
xs: 24,
sm: 12,
},
options: discountTypeOptions,
},
{
label: "最大优惠金额",
prop: "maxDiscountAmount",
rules: [{ required: false, message: "请输入最大优惠金额", trigger: "blur" }],
col: {
xs: 24,
sm: 12,
},
},
{
label: "是否启用",
prop: "status",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
col: {
xs: 24,
sm: 12,
},
},
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
{
label: "是否允许管理端登录",
prop: "isManage",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
},
{
label: "是否允许pc登录",
prop: "isPc",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
},
],
};
// 如果有异步数据会修改配置的推荐用reactive包裹而纯静态配置的可以直接导出
export default reactive(modalConfig);

View File

@ -0,0 +1,29 @@
export const discountTypeOptions: options[] = [
{ label: "金额", value: "0" },
{ label: "折扣", value: "1" },
];
export type optionsType = "discountType" | "";
export function returnOptions(type: optionsType) {
if (type === "discountType") {
return discountTypeOptions;
}
}
export function returnOptionsLabel(optionsType: optionsType, value: string | number) {
const options = returnOptions(optionsType);
if (!options) {
return "";
}
const option = options.find((item) => item.value === value);
return option ? option.label : "";
}
export interface options {
label: string;
value: string | number;
[property: string]: any;
}

View File

@ -0,0 +1,62 @@
import ShopStaffApi, { type getListRequest } from "@/api/account/shopStaff";
import type { PageShopStaff } from "@/api/account/shopStaff";
import type { IContentConfig } from "@/components/CURD/types";
const contentConfig = {
pageName: "sys:user",
table: {
border: true,
highlightCurrentRow: true,
},
pagination: {
background: true,
layout: "prev,pager,next,jumper,total,sizes",
pageSize: 20,
pageSizes: [10, 20, 30, 50],
},
deleteAction: function (id: string) {
return ShopStaffApi.delete(id);
},
indexAction: function (params: getListRequest) {
return ShopStaffApi.getList(params);
},
// modifyAction: function (data) {
// // return ShopStaffApi.edit(data);
// },
pk: "id",
toolbar: ["add"],
defaultToolbar: ["refresh", "filter", "search"],
cols: [
{ type: "selection", width: 50, align: "center" },
{ prop: "id", width: 50, align: "center", show: false },
{
label: "员工名称",
align: "center",
prop: "name",
},
{
label: "员工编号",
align: "center",
prop: "code",
},
{
label: "最大优惠金额",
align: "center",
prop: "maxDiscountAmount",
},
{
label: "优惠类型",
align: "center",
prop: "discountType",
},
{
label: "操作",
align: "center",
fixed: "right",
templet: "tool",
operat: ["edit", "delete"],
},
],
};
export default contentConfig;

View File

@ -0,0 +1,177 @@
import ShopStaffApi, { type editRequest } from "@/api/account/shopStaff";
import type { IModalConfig } from "@/components/CURD/types";
import { discountTypeOptions } from './config'
const modalConfig: IModalConfig<editRequest> = {
pageName: "sys:user",
dialog: {
title: "编辑员工",
width: 800,
draggable: true,
},
form: {
labelWidth: 140,
},
formAction: function (data) {
return ShopStaffApi.edit(data);
},
beforeSubmit(data) {
console.log("提交之前处理", data);
},
formItems: [
{
label: "基本信息设置",
prop: "",
type: "title",
slotName: "title",
},
{
label: "角色",
prop: "roleId",
rules: [{ required: true, message: "请选择角色", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择角色",
},
col: {
xs: 24,
sm: 12,
},
options: []
},
{
label: "员工姓名",
prop: "name",
rules: [{ required: false, message: "请输入员工姓名", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入员工姓名",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "员工编号",
prop: "code",
rules: [{ required: true, message: "请输入员工编号", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入员工编号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "手机号",
prop: "phone",
rules: [{
required: true,
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur",
}],
type: "input",
attrs: {
placeholder: "请输入手机号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "登录账号",
prop: "accountName",
rules: [{ required: true, message: "请输入登录账号", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入登录账号",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "登录密码",
prop: "accountPwd",
rules: [{ required: true, message: "请输入登录密码", trigger: "blur" }],
type: "input",
attrs: {
placeholder: "请输入登录密码",
},
col: {
xs: 24,
sm: 12,
},
},
{
label: "优惠类型",
prop: "discountType",
rules: [{ required: false, message: "请选择优惠类型", trigger: "blur" }],
type: "radio-button",
col: {
xs: 24,
sm: 12,
},
options: discountTypeOptions,
},
{
label: "最大优惠金额",
prop: "maxDiscountAmount",
rules: [{ required: false, message: "请输入最大优惠金额", trigger: "blur" }],
col: {
xs: 24,
sm: 12,
},
},
{
label: "是否启用",
prop: "status",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
col: {
xs: 24,
sm: 12,
},
},
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
{
label: "是否允许管理端登录",
prop: "isManage",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
},
{
label: "是否允许pc登录",
prop: "isPc",
type: "radio",
options: [
{ label: "正常", value: 1 },
{ label: "禁用", value: 0 },
],
initialValue: 1,
},
],
};
// 如果有异步数据会修改配置的推荐用reactive包裹而纯静态配置的可以直接导出
export default reactive(modalConfig);

View File

@ -0,0 +1,34 @@
import type { ISearchConfig } from "@/components/CURD/types";
import { paramTypeOptions } from './config'
const searchConfig: ISearchConfig = {
pageName: "sys:user",
formItems: [
{
type: "input",
label: "账号名",
prop: "name",
attrs: {
placeholder: "请输入账号名",
clearable: true,
style: {
width: "200px",
},
},
},
{
type: "input",
label: "员工编号",
prop: "code",
attrs: {
placeholder: "请输入员工编号",
clearable: true,
style: {
width: "200px",
},
},
},
],
};
export default searchConfig;

View File

@ -0,0 +1,133 @@
<template>
<div class="app-container">
<!-- 列表 -->
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #options="scope">
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
</template>
</page-content>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
</template>
</page-modal>
</div>
</template>
<script setup lang="ts">
import type { IObject, IOperatData } from "@/components/CURD/types";
import usePage from "@/components/CURD/usePage";
import addModalConfig from "./config/add";
import contentConfig from "./config/content";
import editModalConfig from "./config/edit";
import searchConfig from "./config/search";
import { returnOptionsLabel } from "./config/config";
import RoleApi, { type SysRole } from "@/api/account/role";
import ShopStaffApi from "@/api/account/shopStaff";
const {
searchRef,
contentRef,
addModalRef,
editModalRef,
handleQueryClick,
handleResetClick,
// handleAddClick,
// handleEditClick,
handleSubmitClick,
handleExportClick,
handleSearchClick,
handleFilterChange,
} = usePage();
//
async function init() {
const res = await RoleApi.getList({ page: 1, size: 100 });
const roleArr = res.records.map((item: SysRole) => {
return {
...item,
label: item.name,
value: item.id,
};
});
addModalConfig.formItems[1]!.options = roleArr;
editModalConfig.formItems[1]!.options = roleArr;
}
init();
//
async function handleAddClick() {
addModalRef.value?.setModalVisible();
}
//
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
editModalRef.value?.setModalVisible();
// id
await ShopStaffApi.get(row.id).then((res) => {
console.log(res);
editModalRef.value?.setFormData({ ...res });
});
}
1;
//
function handleToolbarClick(name: string) {
console.log(name);
if (name === "custom1") {
ElMessage.success("点击了自定义1按钮");
}
}
//
async function handleOperatClick(data: IOperatData) {}
//
</script>