店铺管理-》员工管理增加权限设置

This commit is contained in:
2025-02-19 11:27:46 +08:00
parent 00ffce98c7
commit 0758aef0fb
15 changed files with 1843 additions and 1526 deletions

View File

@@ -6,11 +6,8 @@ VITE_APP_BASE_API=/dev-api
# 接口地址 # 接口地址
# VITE_APP_API_URL=https://admintestpapi.sxczgkj.cn/ # 线上
VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 正式 VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 正式
# VITE_APP_API_URL=https://api.youlai.tech # 线上
# VITE_APP_API_URL=http://localhost:8989 # 本地
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws # WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss

View File

@@ -4,3 +4,14 @@ VITE_APP_BASE_API = '/prod-api'
# WebSocket端点(可选) # WebSocket端点(可选)
#VITE_APP_WS_ENDPOINT=wss://api.youlai.tech/ws #VITE_APP_WS_ENDPOINT=wss://api.youlai.tech/ws
# 接口地址
VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 正式
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss
# 启用 Mock 服务
VITE_MOCK_DEV_SERVER=false

View File

@@ -4,14 +4,14 @@ const baseURL = Account_BaseUrl + "/admin";
const ShopStaffApi = { const ShopStaffApi = {
// 获取店铺权限列表 // 获取店铺权限列表
getshopPermission() { getshopPermission() {
return request<any>({ return request<any, PermissionResonpseResponse>({
url: `${baseURL}/shopPermission`, url: `${baseURL}/shopPermission`,
method: "get", method: "get",
}); });
}, },
// 获取员工对应的权限id // 获取员工对应的权限id
getPermission(id: number | string) { getPermission(id: number | string) {
return request<any>({ return request<any, string[]>({
url: `${baseURL}/shopStaff/permission`, url: `${baseURL}/shopStaff/permission`,
method: "get", method: "get",
params: { id } params: { id }
@@ -20,3 +20,56 @@ const ShopStaffApi = {
}; };
export default ShopStaffApi; export default ShopStaffApi;
/**
* 权限列表
*
* CzgResult«List«ShopPermission»»
*/
export interface PermissionResonpseResponse {
code?: number | null;
data?: ShopPermission[] | null;
msg?: null | string;
[property: string]: any;
}
/**
* 店铺权限 实体类。
*
* ShopPermission
*/
export interface ShopPermission {
children?: ShopPermission[] | null;
/**
* 权限code为了区分采用汉语拼音
*/
code?: null | string;
createTime?: null | string;
id: string;
/**
* 是否重要: 重要对应页面红色
*/
isImportant?: number | null;
/**
* 权限名称
*/
label?: null | string;
/**
* 层级
*/
level?: number | null;
/**
* 上级ID
*/
parentId?: number | null;
/**
* 排序
*/
sort?: number | null;
/**
* 权限类型staff 员工,
*/
type?: null | string;
updateTime?: null | string;
[property: string]: any;
}

View File

@@ -0,0 +1,78 @@
import request from "@/utils/request";
import { Account_BaseUrl } from "@/api/config";
const baseURL = Account_BaseUrl + "/admin/shopArea";
const ShopStaffApi = {
getList(params: getListRequest) {
return request<any>({
url: `${baseURL}`,
method: "get",
params
});
},
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 | number) {
return request<any>({
url: `${baseURL}`,
method: "delete",
data: { id }
});
},
};
export default ShopStaffApi;
export interface getListRequest {
/**
* 区域名称
*/
name?: string;
page?: string;
size?: string;
[property: string]: any;
}
/**
* ShopAreaEditDTO
*/
export interface editRequest {
/**
* id
*/
id: number | null;
/**
* 区域名称
*/
name?: null | string;
/**
* 排序
*/
sort?: number | null;
[property: string]: any;
}
/**
* ShopAreaAddDTO
*/
export interface addRequest {
/**
* 区域名称
*/
name: null | string;
/**
* 排序
*/
sort?: number | null;
[property: string]: any;
}

View File

@@ -1,9 +1,20 @@
<template> <template>
<!-- drawer --> <!-- drawer -->
<template v-if="modalConfig.component === 'drawer'"> <template v-if="modalConfig.component === 'drawer'">
<el-drawer v-model="modalVisible" :append-to-body="true" v-bind="modalConfig.drawer" @close="handleCloseModal"> <el-drawer
v-model="modalVisible"
:append-to-body="true"
v-bind="modalConfig.drawer"
@close="handleCloseModal"
>
<!-- 表单 --> <!-- 表单 -->
<el-form ref="formRef" label-width="auto" v-bind="modalConfig.form" :model="formData" :rules="formRules"> <el-form
ref="formRef"
label-width="auto"
v-bind="modalConfig.form"
:model="formData"
:rules="formRules"
>
<el-row :gutter="20"> <el-row :gutter="20">
<template v-for="item in formItems" :key="item.prop"> <template v-for="item in formItems" :key="item.prop">
<template v-if="item.type === 'title'"> <template v-if="item.type === 'title'">
@@ -15,7 +26,12 @@
<template v-if="item.tips" #label> <template v-if="item.tips" #label>
<span> <span>
{{ item.label }} {{ item.label }}
<el-tooltip placement="bottom" effect="light" :content="item.tips" :raw-content="true"> <el-tooltip
placement="bottom"
effect="light"
:content="item.tips"
:raw-content="true"
>
<el-icon style="vertical-align: -0.15em" size="16"> <el-icon style="vertical-align: -0.15em" size="16">
<QuestionFilled /> <QuestionFilled />
</el-icon> </el-icon>
@@ -90,13 +106,20 @@
</template> </template>
<!-- 自定义 --> <!-- 自定义 -->
<template v-else-if="item.type === 'custom'"> <template v-else-if="item.type === 'custom'">
<slot :name="item.slotName ?? item.prop" :prop="item.prop" :formData="formData" :attrs="item.attrs" /> <slot
:name="item.slotName ?? item.prop"
:prop="item.prop"
:formData="formData"
:attrs="item.attrs"
/>
</template> </template>
</el-form-item> </el-form-item>
</el-col> </el-col>
</template> </template>
</el-row> </el-row>
</el-form> </el-form>
<!-- 表单底部插槽 -->
<slot name="formFooter" />
<!-- 弹窗底部操作按钮 --> <!-- 弹窗底部操作按钮 -->
<template #footer> <template #footer>
<div v-if="!formDisable"> <div v-if="!formDisable">
@@ -111,13 +134,26 @@
</template> </template>
<!-- dialog --> <!-- dialog -->
<template v-else> <template v-else>
<el-dialog v-model="modalVisible" :align-center="true" :append-to-body="true" width="70vw" <el-dialog
v-bind="modalConfig.dialog" style="padding-right: 0" @close="handleCloseModal"> v-model="modalVisible"
:align-center="true"
:append-to-body="true"
width="70vw"
v-bind="modalConfig.dialog"
style="padding-right: 0"
@close="handleCloseModal"
>
<!-- 滚动 --> <!-- 滚动 -->
<el-scrollbar max-height="60vh"> <el-scrollbar max-height="60vh">
<!-- 表单 --> <!-- 表单 -->
<el-form ref="formRef" label-width="auto" v-bind="modalConfig.form" <el-form
style="padding-right: var(--el-dialog-padding-primary)" :model="formData" :rules="formRules"> ref="formRef"
label-width="auto"
v-bind="modalConfig.form"
style="padding-right: var(--el-dialog-padding-primary)"
:model="formData"
:rules="formRules"
>
<el-row :gutter="20"> <el-row :gutter="20">
<template v-for="item in formItems" :key="item.prop"> <template v-for="item in formItems" :key="item.prop">
<template v-if="item.type === 'title'"> <template v-if="item.type === 'title'">
@@ -131,7 +167,12 @@
<template v-if="item.tips" #label> <template v-if="item.tips" #label>
<span> <span>
{{ item.label }} {{ item.label }}
<el-tooltip placement="bottom" effect="light" :content="item.tips" :raw-content="true"> <el-tooltip
placement="bottom"
effect="light"
:content="item.tips"
:raw-content="true"
>
<el-icon style="vertical-align: -0.15em" size="16"> <el-icon style="vertical-align: -0.15em" size="16">
<QuestionFilled /> <QuestionFilled />
</el-icon> </el-icon>
@@ -140,7 +181,11 @@
</template> </template>
<!-- Input 输入框 --> <!-- Input 输入框 -->
<template v-if="item.type === 'input' || item.type === undefined"> <template v-if="item.type === 'input' || item.type === undefined">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" :disabled="item.disabled" /> <el-input
v-model="formData[item.prop]"
v-bind="item.attrs"
:disabled="item.disabled"
/>
</template> </template>
<!-- textarea 输入框 --> <!-- textarea 输入框 -->
<template v-else-if="item.type === 'textarea'"> <template v-else-if="item.type === 'textarea'">
@@ -207,8 +252,12 @@
</template> </template>
<!-- 自定义 --> <!-- 自定义 -->
<template v-else-if="item.type === 'custom'"> <template v-else-if="item.type === 'custom'">
<slot :name="item.slotName ?? item.prop" :prop="item.prop" :formData="formData" <slot
:attrs="item.attrs" /> :name="item.slotName ?? item.prop"
:prop="item.prop"
:formData="formData"
:attrs="item.attrs"
/>
</template> </template>
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -216,6 +265,8 @@
</el-row> </el-row>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
<!-- 表单底部插槽 -->
<slot name="formFooter" />
<!-- 弹窗底部操作按钮 --> <!-- 弹窗底部操作按钮 -->
<template #footer> <template #footer>
<div style="padding-right: var(--el-dialog-padding-primary)"> <div style="padding-right: var(--el-dialog-padding-primary)">
@@ -370,7 +421,14 @@ function handleDisabled(disable: boolean) {
} }
// 暴露的属性和方法 // 暴露的属性和方法
defineExpose({ setModalVisible, getFormData, setFormData, setFormItemData, handleDisabled, handleClose }); defineExpose({
setModalVisible,
getFormData,
setFormData,
setFormItemData,
handleDisabled,
handleClose,
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -11,7 +11,6 @@ const Layout = () => import("@/layout/index.vue");
export const usePermissionStore = defineStore("permission", () => { export const usePermissionStore = defineStore("permission", () => {
// 储所有路由,包括静态路由和动态路由 // 储所有路由,包括静态路由和动态路由
const routes = ref<RouteRecordRaw[]>(constantRoutes); const routes = ref<RouteRecordRaw[]>(constantRoutes);
console.log(router);
// 混合模式左侧菜单路由 // 混合模式左侧菜单路由
const mixedLayoutLeftRoutes = ref<RouteRecordRaw[]>([]); const mixedLayoutLeftRoutes = ref<RouteRecordRaw[]>([]);
// 路由是否加载完成 // 路由是否加载完成

View File

@@ -15,7 +15,6 @@ const service = axios.create({
// 请求拦截器 // 请求拦截器
service.interceptors.request.use( service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => { (config: InternalAxiosRequestConfig) => {
console.log(config);
const accessToken = getToken(); const accessToken = getToken();
// 如果 Authorization 设置为 no-auth则不携带 Token用于登录、刷新 Token 等接口 // 如果 Authorization 设置为 no-auth则不携带 Token用于登录、刷新 Token 等接口
if (config.headers.Authorization !== "no-auth" && accessToken) { if (config.headers.Authorization !== "no-auth" && accessToken) {

View File

@@ -1,4 +1,3 @@
class WebSocketManager { class WebSocketManager {
private client: WebSocket | null = null; private client: WebSocket | null = null;
private connected: boolean = false; private connected: boolean = false;
@@ -35,6 +34,11 @@ class WebSocketManager {
}; };
this.client.onerror = (error) => { this.client.onerror = (error) => {
console.error("WebSocket 发生错误:", error); console.error("WebSocket 发生错误:", error);
ElNotification({
title: "提示",
message: "WebSocket 发生错误",
type: "error",
});
}; };
this.client.onmessage = (event) => { this.client.onmessage = (event) => {
const message = event.data; const message = event.data;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
<el-radio-button :value="1">员工</el-radio-button> <el-radio-button :value="1">员工</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item prop="merchantName" v-if="state.loginForm.loginType == 'staff'"> <el-form-item prop="merchantName" v-if="state.loginForm.loginType == 1">
<el-input <el-input
v-model="state.loginForm.merchantName" v-model="state.loginForm.merchantName"
type="text" type="text"
@@ -145,7 +145,7 @@ function getCookie() {
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe), rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
code: "", code: "",
merchantName: "", merchantName: "",
loginType: "merchant", loginType: 0,
}; };
} }

View File

@@ -1 +1,52 @@
<template>11</template> <template>
<div v-for="(item, index) in list" :key="index">
<h4 class="title">
{{ item.label }}
</h4>
<div v-if="item.children" class="flex gap-10">
<el-checkbox-group v-model="model[index]">
<el-checkbox
v-for="(child, childIndex) in item.children"
:key="childIndex"
:value="child.id"
:label="child.label"
:style="returnStyle(child)"
></el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>
<script setup>
const props = defineProps({
list: {
type: Array,
default: () => [],
},
});
const model = defineModel({ type: Array, default: () => [] });
const importantPer = ["允许收款", "允许打折", "允许修改会员余额", "允许沽清"];
function returnStyle(child) {
if (importantPer.includes(child.label)) {
return "color:red";
}
}
for (const item in props.list) {
console.log(item);
model[item] = [];
}
function getFormData() {
return formData;
}
defineExpose({
getFormData,
});
</script>
<style scoped >
.title {
text-align: left;
}
</style>

View File

@@ -144,12 +144,7 @@ const modalConfig: IModalConfig<addRequest> = {
sm: 12, sm: 12,
}, },
}, },
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
{ {
label: "是否允许管理端登录", label: "是否允许管理端登录",
prop: "isManage", prop: "isManage",
@@ -170,6 +165,20 @@ const modalConfig: IModalConfig<addRequest> = {
], ],
initialValue: 1, initialValue: 1,
}, },
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
{
type: 'custom',
prop: 'permission',
slotName: 'permission',
label: '',
hidden: true
}
], ],
}; };

View File

@@ -27,7 +27,7 @@ const modalConfig: IModalConfig<editRequest> = {
{ {
label: "角色", label: "角色",
prop: "roleId", prop: "roleId",
rules: [{ required: true, message: "请选择角色", trigger: "blur" }], rules: [{ required: false, message: "请选择角色", trigger: "blur" }],
type: "select", type: "select",
attrs: { attrs: {
placeholder: "请选择角色", placeholder: "请选择角色",
@@ -54,7 +54,7 @@ const modalConfig: IModalConfig<editRequest> = {
{ {
label: "员工编号", label: "员工编号",
prop: "code", prop: "code",
rules: [{ required: true, message: "请输入员工编号", trigger: "blur" }], rules: [{ required: false, message: "请输入员工编号", trigger: "blur" }],
type: "input", type: "input",
attrs: { attrs: {
placeholder: "请输入员工编号", placeholder: "请输入员工编号",
@@ -68,7 +68,7 @@ const modalConfig: IModalConfig<editRequest> = {
label: "手机号", label: "手机号",
prop: "phone", prop: "phone",
rules: [{ rules: [{
required: true, required: false,
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码", message: "请输入正确的手机号码",
trigger: "blur", trigger: "blur",
@@ -85,7 +85,7 @@ const modalConfig: IModalConfig<editRequest> = {
{ {
label: "登录账号", label: "登录账号",
prop: "accountName", prop: "accountName",
rules: [{ required: true, message: "请输入登录账号", trigger: "blur" }], rules: [{ required: false, message: "请输入登录账号", trigger: "blur" }],
type: "input", type: "input",
attrs: { attrs: {
placeholder: "请输入登录账号", placeholder: "请输入登录账号",
@@ -98,7 +98,7 @@ const modalConfig: IModalConfig<editRequest> = {
{ {
label: "登录密码", label: "登录密码",
prop: "accountPwd", prop: "accountPwd",
rules: [{ required: true, message: "请输入登录密码", trigger: "blur" }], rules: [{ required: false, message: "请输入登录密码", trigger: "blur" }],
type: "input", type: "input",
attrs: { attrs: {
placeholder: "请输入登录密码", placeholder: "请输入登录密码",
@@ -144,12 +144,6 @@ const modalConfig: IModalConfig<editRequest> = {
sm: 12, sm: 12,
}, },
}, },
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
{ {
label: "是否允许管理端登录", label: "是否允许管理端登录",
prop: "isManage", prop: "isManage",
@@ -170,6 +164,12 @@ const modalConfig: IModalConfig<editRequest> = {
], ],
initialValue: 1, initialValue: 1,
}, },
{
label: "员工权限设置",
prop: "",
type: "title",
slotName: "title",
},
], ],
}; };

View File

@@ -43,8 +43,12 @@
<!-- 新增 --> <!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick"> <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #permission="scope"> <template #formFooter>
<selectPermission></selectPermission> <selectPermission
v-model="selPermissionList"
:list="permissionList"
ref="refSelectPermission"
></selectPermission>
</template> </template>
</page-modal> </page-modal>
@@ -54,8 +58,12 @@
:modal-config="editModalConfig" :modal-config="editModalConfig"
@submit-click="handleSubmitClick" @submit-click="handleSubmitClick"
> >
<template #permission="scope"> <template #formFooter>
<selectPermission></selectPermission> <selectPermission
v-model="selPermissionList"
:list="permissionList"
ref="refSelectPermission"
></selectPermission>
</template> </template>
</page-modal> </page-modal>
</div> </div>
@@ -71,9 +79,10 @@ import searchConfig from "./config/search";
import { returnOptionsLabel } from "./config/config"; import { returnOptionsLabel } from "./config/config";
import RoleApi, { type SysRole } from "@/api/account/role"; import RoleApi, { type SysRole } from "@/api/account/role";
import ShopStaffApi from "@/api/account/shopStaff"; import ShopStaffApi from "@/api/account/shopStaff";
import permissionApi from "@/api/account/permission"; import permissionApi, { type ShopPermission } from "@/api/account/permission";
import selectPermission from "./components/select-permission.vue"; import selectPermission from "./components/select-permission.vue";
permissionApi.getshopPermission(); const refSelectPermission = ref();
const { const {
searchRef, searchRef,
contentRef, contentRef,
@@ -89,9 +98,36 @@ const {
handleFilterChange, handleFilterChange,
} = usePage(); } = usePage();
//店铺权限列表
let permissionList = ref<ShopPermission[]>([]);
//选中的权限列表
let selPermissionList = ref<string[]>([]);
// 数据初始化 // 数据初始化
async function init() { async function init() {
// 覆写添加确定方法
const oldAddSubmitFunc = addModalConfig.formAction;
addModalConfig.formAction = (data) => {
return oldAddSubmitFunc({
...data,
shopPermissionIds: selPermissionList.value.reduce((pre: string[], cur: string) => {
return pre.concat(cur);
}, [] as string[]),
});
};
// 覆写编辑确定方法
const oldeditSubmitFunc = editModalConfig.formAction;
editModalConfig.formAction = (data) => {
return oldeditSubmitFunc({
...data,
shopPermissionIds: selPermissionList.value.reduce((pre: string[], cur: string) => {
return pre.concat(cur);
}, [] as string[]),
});
};
const res = await RoleApi.getList({ page: 1, size: 100 }); const res = await RoleApi.getList({ page: 1, size: 100 });
const permission = await permissionApi.getshopPermission();
permissionList.value = Array.isArray(permission) ? permission : [];
const roleArr = res.records.map((item: SysRole) => { const roleArr = res.records.map((item: SysRole) => {
return { return {
...item, ...item,
@@ -106,12 +142,33 @@ init();
// 新增 // 新增
async function handleAddClick() { async function handleAddClick() {
selPermissionList.value = [];
addModalRef.value?.setModalVisible(); addModalRef.value?.setModalVisible();
} }
// 编辑 // 编辑
async function handleEditClick(row: IObject) { async function handleEditClick(row: IObject) {
selPermissionList.value = [];
editModalRef.value?.handleDisabled(false); editModalRef.value?.handleDisabled(false);
editModalRef.value?.setModalVisible(); editModalRef.value?.setModalVisible();
// 设置权限选中
const perselArr = await permissionApi.getPermission(row.id);
for (let id of perselArr) {
for (let index in permissionList.value) {
const item = permissionList.value[index];
if (item.children) {
for (let child of item.children) {
if (child.id != null && child.id == id) {
if (selPermissionList.value[index]) {
selPermissionList.value[index].push(id);
} else {
selPermissionList.value[index] = [id];
}
}
}
}
}
}
console.log(selPermissionList.value);
// 根据id获取数据进行填充 // 根据id获取数据进行填充
await ShopStaffApi.get(row.id).then((res) => { await ShopStaffApi.get(row.id).then((res) => {
console.log(res); console.log(res);