feat(权限控制): 实现按钮级权限控制功能
添加按钮权限控制功能,包括: 1. 新增权限存储模块和权限获取API 2. 在App.vue初始化时获取权限 3. 在多个页面组件中添加权限检查逻辑 4. 移除角色管理页面中不必要的分类显示 5. 在店铺表单中添加二维码和链接复制功能
This commit is contained in:
parent
1d82d1a5be
commit
a2a615d18d
|
@ -1,11 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||||
import { ReDialog } from "@/components/ReDialog";
|
import { ReDialog } from "@/components/ReDialog";
|
||||||
|
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
|
||||||
|
|
||||||
const currentLocale = computed(() => zhCn);
|
const currentLocale = computed(() => zhCn);
|
||||||
|
const btnPermissionStore = useBtnPermissionStore();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await btnPermissionStore.fetchPermissions();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -188,3 +188,8 @@ export interface QyUserLoginDTO {
|
||||||
/** 角色名称 */
|
/** 角色名称 */
|
||||||
roleName?: string;
|
roleName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取动态菜单 */
|
||||||
|
export const getPermissions = () => {
|
||||||
|
return http.request<ResponseData<string[]>>("get", "/getPermissions");
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { store } from "@/store";
|
||||||
|
import { getPermissions } from "@/api/common/login";
|
||||||
|
|
||||||
|
export const useBtnPermissionStore = defineStore("btnPermission", () => {
|
||||||
|
const btnPermissions = ref<string[]>([]);
|
||||||
|
|
||||||
|
const fetchPermissions = async () => {
|
||||||
|
const res = await getPermissions();
|
||||||
|
btnPermissions.value = res.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPermission = (permission: string) => {
|
||||||
|
return btnPermissions.value.includes(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
btnPermissions,
|
||||||
|
fetchPermissions,
|
||||||
|
hasPermission,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 用于在 setup 外使用 store
|
||||||
|
*/
|
||||||
|
export function useBtnPermissionStoreOutside() {
|
||||||
|
return useBtnPermissionStore(store)
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import { paymentMethodOptions, modeToPaymentMethodMap } from "@/utils/maps/payme
|
||||||
import { addShop, updateShop, ShopDTO, UpdateShopCommand, AddShopCommand } from "@/api/shop/shop";
|
import { addShop, updateShop, ShopDTO, UpdateShopCommand, AddShopCommand } from "@/api/shop/shop";
|
||||||
import { useWxStore } from "@/store/modules/wx";
|
import { useWxStore } from "@/store/modules/wx";
|
||||||
import Upload from "@iconify-icons/ep/upload";
|
import Upload from "@iconify-icons/ep/upload";
|
||||||
|
import ReQrcode from "@/components/ReQrcode";
|
||||||
|
import { copyTextToClipboard } from "@pureadmin/utils";
|
||||||
const { VITE_APP_BASE_API } = import.meta.env;
|
const { VITE_APP_BASE_API } = import.meta.env;
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -120,6 +122,16 @@ const currentPaymentMethods = computed(() => {
|
||||||
return option ? { label: option.label, type: option.type } : null;
|
return option ? { label: option.label, type: option.type } : null;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const copyLink = () => {
|
||||||
|
if (!formData.value.shopId) {
|
||||||
|
ElMessage.warning("店铺ID不存在,无法复制链接");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = `http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth?shopId=${formData.value.shopId}`;
|
||||||
|
const success = copyTextToClipboard(url);
|
||||||
|
success ? ElMessage.success('链接复制成功') : ElMessage.error('复制失败,请手动复制');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -175,15 +187,23 @@ const currentPaymentMethods = computed(() => {
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="支付方式">
|
<el-form-item label="支付方式">
|
||||||
<el-tag
|
<el-tag v-for="method in currentPaymentMethods" :key="method.label" :type="method.type"
|
||||||
v-for="method in currentPaymentMethods"
|
style="margin-right: 5px;">
|
||||||
:key="method.label"
|
|
||||||
:type="method.type"
|
|
||||||
style="margin-right: 5px;"
|
|
||||||
>
|
|
||||||
{{ method.label }}
|
{{ method.label }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="机柜数量">
|
||||||
|
{{ props.row.cabinetCount || 0 }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="二维码">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<ReQrcode :text="`http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth?shopId=${formData.shopId}`"
|
||||||
|
:options="{ width: 150 }" :width="150"/>
|
||||||
|
<el-button type="primary" size="small" @click="copyLink" style="margin-left: 10px;">
|
||||||
|
复制链接
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="handleClose" style="margin-right: 5px;">取消</el-button>
|
<el-button @click="handleClose" style="margin-right: 5px;">取消</el-button>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { getGoodsInfo } from "@/api/shop/goods";
|
||||||
import EditCabinetDrawer from "./edit-cabinet-drawer.vue";
|
import EditCabinetDrawer from "./edit-cabinet-drawer.vue";
|
||||||
import { CabinetMainboardDTO, deleteMainboard, getMainboardList, updateMainboard } from "@/api/cabinet/mainboards";
|
import { CabinetMainboardDTO, deleteMainboard, getMainboardList, updateMainboard } from "@/api/cabinet/mainboards";
|
||||||
import { getShopById, ShopDTO, getModeText } from "@/api/shop/shop";
|
import { getShopById, ShopDTO, getModeText } from "@/api/shop/shop";
|
||||||
|
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SmartCabinetDetail"
|
name: "SmartCabinetDetail"
|
||||||
|
@ -30,6 +31,7 @@ defineOptions({
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const { hasPermission } = useBtnPermissionStore();
|
||||||
const currentCell = ref<CabinetCellDTO>();
|
const currentCell = ref<CabinetCellDTO>();
|
||||||
const cabinetInfo = ref<SmartCabinetDTO>({
|
const cabinetInfo = ref<SmartCabinetDTO>({
|
||||||
cabinetName: "",
|
cabinetName: "",
|
||||||
|
@ -308,7 +310,7 @@ onMounted(() => {
|
||||||
|
|
||||||
<div class="info-details" v-if="activeTab === 'basic'">
|
<div class="info-details" v-if="activeTab === 'basic'">
|
||||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 16px;">
|
<div style="display: flex; justify-content: flex-end; margin-bottom: 16px;">
|
||||||
<el-button type="primary" link :icon="useRenderIcon(EditPen)" @click="editCabinetDrawerVisible = true">
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="primary" link :icon="useRenderIcon(EditPen)" @click="editCabinetDrawerVisible = true">
|
||||||
编辑柜体
|
编辑柜体
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -318,11 +320,11 @@ onMounted(() => {
|
||||||
<el-descriptions-item label="柜体格式">{{ CabinetImgMap[cabinetInfo.templateNo]?.name || '-'
|
<el-descriptions-item label="柜体格式">{{ CabinetImgMap[cabinetInfo.templateNo]?.name || '-'
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="柜体地址">{{ cabinetInfo.shopName || '-' }}
|
<el-descriptions-item label="柜体地址">{{ cabinetInfo.shopName || '-' }}
|
||||||
<el-button type="success" link @click="shopConfigVisible = true">
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="success" link @click="shopConfigVisible = true">
|
||||||
配置
|
配置
|
||||||
</el-button></el-descriptions-item>
|
</el-button></el-descriptions-item>
|
||||||
<el-descriptions-item label="柜体网关">{{ cabinetInfo.mqttServerId || '-' }}
|
<el-descriptions-item label="柜体网关">{{ cabinetInfo.mqttServerId || '-' }}
|
||||||
<el-button type="warning" link @click="gatewayConfigVisible = true">
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="warning" link @click="gatewayConfigVisible = true">
|
||||||
配置
|
配置
|
||||||
</el-button></el-descriptions-item>
|
</el-button></el-descriptions-item>
|
||||||
<el-descriptions-item label="借呗支付">{{ getBalanceEnableText(shopInfo.balanceEnable)
|
<el-descriptions-item label="借呗支付">{{ getBalanceEnableText(shopInfo.balanceEnable)
|
||||||
|
@ -378,11 +380,13 @@ onMounted(() => {
|
||||||
<span class="line-number">{{ item.cellNo }}</span>
|
<span class="line-number">{{ item.cellNo }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<el-button v-if="!item.goodsId" link type="success"
|
<el-button v-if="!item.goodsId" :disabled="!hasPermission('shop:cabinet:write')"
|
||||||
|
link type="success"
|
||||||
@click="handleConfigure(item)" class="cell-btn">
|
@click="handleConfigure(item)" class="cell-btn">
|
||||||
商品配置
|
商品配置
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="item.goodsId" link type="primary"
|
<el-button v-if="item.goodsId" :disabled="!hasPermission('shop:cabinet:write')"
|
||||||
|
link type="primary"
|
||||||
@click="handleEditCell(item)" class="cell-btn">
|
@click="handleEditCell(item)" class="cell-btn">
|
||||||
{{ item.goodsName }}
|
{{ item.goodsName }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
@ -410,10 +414,10 @@ onMounted(() => {
|
||||||
<el-table-column label="锁控板序号" prop="lockControlNo" width="120" />
|
<el-table-column label="锁控板序号" prop="lockControlNo" width="120" />
|
||||||
<el-table-column label="操作" width="150" fixed="right">
|
<el-table-column label="操作" width="150" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="primary" link :icon="useRenderIcon(EditPen)" @click="handleEditMainboard(row)">
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="primary" link :icon="useRenderIcon(EditPen)" @click="handleEditMainboard(row)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="danger" link :icon="useRenderIcon(Delete)" @click="handleDeleteMainboard(row)">
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="danger" link :icon="useRenderIcon(Delete)" @click="handleDeleteMainboard(row)">
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -434,7 +438,8 @@ onMounted(() => {
|
||||||
@refresh="fetchCabinetDetail" />
|
@refresh="fetchCabinetDetail" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<el-drawer v-model="goodsConfigVisible" title="配置商品" size="50%" direction="rtl">
|
<el-drawer v-model="goodsConfigVisible" title="配置商品" size="50%" direction="rtl">
|
||||||
<CabinetGoodsConfigModal v-model="goodsConfigVisible" :cell-id="currentCellId" :belong-type="shopInfo.mode == 4 ? 1 : 0" @refresh="fetchCellList" />
|
<CabinetGoodsConfigModal v-model="goodsConfigVisible" :cell-id="currentCellId"
|
||||||
|
:belong-type="shopInfo.mode == 4 ? 1 : 0" @refresh="fetchCellList" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<el-drawer v-model="configuredGoodsVisible" title="管理商品" size="30%" direction="rtl">
|
<el-drawer v-model="configuredGoodsVisible" title="管理商品" size="30%" direction="rtl">
|
||||||
<ConfiguredGoodsModal v-model="configuredGoodsVisible" :cell-id="currentCell?.cellId"
|
<ConfiguredGoodsModal v-model="configuredGoodsVisible" :cell-id="currentCell?.cellId"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||||
import SmartCabinetCardFormModal from "./smart-cabinet-card-form-modal.vue";
|
import SmartCabinetCardFormModal from "./smart-cabinet-card-form-modal.vue";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
import { getModeText } from "@/api/shop/shop";
|
import { getModeText } from "@/api/shop/shop";
|
||||||
|
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
|
||||||
const { VITE_PUBLIC_IMG_PATH: IMG_PATH } = import.meta.env;
|
const { VITE_PUBLIC_IMG_PATH: IMG_PATH } = import.meta.env;
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
@ -21,6 +22,7 @@ defineOptions({
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { hasPermission } = useBtnPermissionStore();
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const modalVisible = ref(false);
|
const modalVisible = ref(false);
|
||||||
const searchFormParams = ref({
|
const searchFormParams = ref({
|
||||||
|
@ -115,7 +117,7 @@ onMounted(() => {
|
||||||
<el-form-item class="space-item">
|
<el-form-item class="space-item">
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :icon="useRenderIcon(AddFill)" @click="modalVisible = true"
|
<el-button v-if="hasPermission('shop:cabinet:write')" type="primary" :icon="useRenderIcon(AddFill)" @click="modalVisible = true"
|
||||||
style="margin-right: 10px;">
|
style="margin-right: 10px;">
|
||||||
新增设备
|
新增设备
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { ElMessage } from "element-plus";
|
||||||
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
|
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
|
||||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||||
import GoodsEditModal from "./goods-edit-modal.vue";
|
import GoodsEditModal from "./goods-edit-modal.vue";
|
||||||
|
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
|
||||||
|
|
||||||
const handleEdit = (row: GoodsDTO) => {
|
const handleEdit = (row: GoodsDTO) => {
|
||||||
goodsInfo.value = row;
|
goodsInfo.value = row;
|
||||||
|
@ -21,6 +22,8 @@ defineOptions({
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { hasPermission } = useBtnPermissionStore();
|
||||||
const goodsInfo = ref<GoodsDTO>({
|
const goodsInfo = ref<GoodsDTO>({
|
||||||
goodsId: 0,
|
goodsId: 0,
|
||||||
goodsName: "",
|
goodsName: "",
|
||||||
|
@ -118,7 +121,7 @@ watch(goodsId, () => {
|
||||||
<el-tab-pane label="基本信息" name="basic"></el-tab-pane>
|
<el-tab-pane label="基本信息" name="basic"></el-tab-pane>
|
||||||
<el-tab-pane label="购买记录" name="order"></el-tab-pane>
|
<el-tab-pane label="购买记录" name="order"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<el-button v-if="goodsInfo.belongType == 0" type="primary" @click="handleEdit(goodsInfo)"
|
<el-button v-if="goodsInfo.belongType == 0 && hasPermission('shop:goods:write')" type="primary" @click="handleEdit(goodsInfo)"
|
||||||
style="margin-bottom: 12px" :size="'default'">
|
style="margin-bottom: 12px" :size="'default'">
|
||||||
编辑商品
|
编辑商品
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { deleteGoodsApi } from "@/api/shop/goods";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { useWxStore } from "@/store/modules/wx";
|
import { useWxStore } from "@/store/modules/wx";
|
||||||
|
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "ShopGoods"
|
name: "ShopGoods"
|
||||||
|
@ -24,6 +25,7 @@ defineOptions({
|
||||||
|
|
||||||
const wxStore = useWxStore();
|
const wxStore = useWxStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { hasPermission } = useBtnPermissionStore();
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const modalVisible = ref(false);
|
const modalVisible = ref(false);
|
||||||
|
@ -184,7 +186,7 @@ const handleViewDetail = (row: GoodsDTO) => {
|
||||||
<el-form-item class="space-item">
|
<el-form-item class="space-item">
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :icon="useRenderIcon(AddFill)" @click="modalVisible = true"
|
<el-button v-if="hasPermission('shop:goods:write')" type="primary" :icon="useRenderIcon(AddFill)" @click="modalVisible = true"
|
||||||
style="margin-right: 10px;">
|
style="margin-right: 10px;">
|
||||||
新增商品
|
新增商品
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
|
@ -229,7 +229,7 @@ onSearch().then(() => {
|
||||||
</template>
|
</template>
|
||||||
<el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
|
<el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
|
||||||
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
|
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
|
||||||
<span class="menu-category">{{ menu.categoryName }}:</span>
|
<!-- <span class="menu-category">{{ menu.categoryName }}:</span> -->
|
||||||
<el-checkbox v-for="item in menu.items" :key="item.id" :label="item.id" class="menu-checkbox">
|
<el-checkbox v-for="item in menu.items" :key="item.id" :label="item.id" class="menu-checkbox">
|
||||||
{{ item.menuName }}
|
{{ item.menuName }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
|
|
Loading…
Reference in New Issue