Compare commits

..

7 Commits

Author SHA1 Message Date
dzq 07a27fa13d fix(角色管理): 修复数据权限更新逻辑并移除调试日志
修复 updateDataScope 函数的数据权限更新逻辑,增加 updateAll 参数控制更新范围
优化菜单选择变化时的数据权限处理,增加读写权限的自动管理
移除所有调试用的 console.log 语句
2025-06-21 08:16:04 +08:00
dzq 977615baed feat(角色管理): 添加数据权限控制功能
- 新增数据权限字段并与菜单权限联动
- 重构菜单权限界面为左右布局
- 添加数据权限与菜单权限的双向绑定逻辑
- 优化菜单选项数据结构,增加数据权限字段
2025-06-20 08:41:37 +08:00
dzq a2a615d18d feat(权限控制): 实现按钮级权限控制功能
添加按钮权限控制功能,包括:
1. 新增权限存储模块和权限获取API
2. 在App.vue初始化时获取权限
3. 在多个页面组件中添加权限检查逻辑
4. 移除角色管理页面中不必要的分类显示
5. 在店铺表单中添加二维码和链接复制功能
2025-06-18 16:57:33 +08:00
dzq 1d82d1a5be feat(shop): 添加机柜数量显示及优化配置弹窗样式
- 在ShopDTO接口中添加cabinetCount字段
- 在商店卡片中显示机柜数量
- 将配置弹窗从dialog改为drawer并优化样式
- 为网关配置弹窗应用相同样式优化
2025-06-17 15:10:51 +08:00
dzq 6a14db1ee3 fix(shop): 修复支付方法显示问题并优化卡片布局
修复当前支付方法计算中未正确处理数字类型的问题
合并运行模式显示项并调整卡片图片宽度和内边距
2025-06-17 09:40:10 +08:00
dzq fb862b7267 feat(支付): 添加支付方式映射及展示逻辑
新增支付方式选项配置和模式到支付方式的映射
在店铺表单和卡片中展示当前模式支持的支付方式标签
移除原有的借呗支付选择控件,改为自动根据模式显示支付方式
2025-06-16 15:36:04 +08:00
dzq 877fa5f1db feat(审批): 在退货审批查询接口和页面中添加审批类型字段
添加审批类型字段以区分借还柜和固资通的审批流程,默认值为0(借还柜)
2025-06-14 11:41:21 +08:00
16 changed files with 358 additions and 82 deletions

View File

@ -1,11 +1,16 @@
<script setup lang="ts">
import { computed } from "vue";
import { computed, onMounted } from "vue";
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
import { ReDialog } from "@/components/ReDialog";
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
const currentLocale = computed(() => zhCn);
const btnPermissionStore = useBtnPermissionStore();
onMounted(async () => {
await btnPermissionStore.fetchPermissions();
})
</script>
<template>

View File

@ -187,4 +187,9 @@ export interface QyUserLoginDTO {
roleId?: number;
/** 角色名称 */
roleName?: string;
}
}
/** 获取动态菜单 */
export const getPermissions = () => {
return http.request<ResponseData<string[]>>("get", "/getPermissions");
};

View File

@ -12,6 +12,12 @@ export interface SearchReturnApprovalQuery extends BasePageQuery {
* wechat- | balance-
*/
paymentMethod?: string;
/**
*
* @remarks
* 0 1
*/
approvalType: number;
}
/** 退货审批DTO */

View File

@ -25,6 +25,8 @@ export interface ShopDTO {
mode?: number;
/** 借呗支付1-正常使用 0-禁止使用) */
balanceEnable?: number;
/** 机柜数量 */
cabinetCount?: number;
}
/** 新增商店命令 */

View File

@ -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)
}

14
src/utils/maps/payment.ts Normal file
View File

@ -0,0 +1,14 @@
export const paymentMethodOptions = [
{ label: '微信支付', value: 0, type: 'primary' },
{ label: '借呗支付', value: 1, type: 'success' },
{ label: '要呗支付', value: 2, type: 'info' },
{ label: '余额支付', value: 3, type: 'warning' },
];
export const modeToPaymentMethodMap = {
0: [0, 3],
1: [0, 1],
2: [0, 1],
3: [0, 3],
4: [2],
};

View File

@ -12,6 +12,7 @@ import View from "@iconify-icons/ep/view";
import AddFill from "@iconify-icons/ri/add-circle-line";
import ShopFormModal from "./shop-form-modal.vue";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { paymentMethodOptions, modeToPaymentMethodMap } from "@/utils/maps/payment";
const { VITE_PUBLIC_IMG_PATH: IMG_PATH } = import.meta.env;
defineOptions({
@ -87,6 +88,15 @@ function getBalanceType(enable: number): "" | "success" | "warning" | "info" | "
onMounted(() => {
getList();
});
function getPaymentMethods(mode: number): Array<{ label: string; type: string }> {
if (!mode) return [];
const methodValues = modeToPaymentMethodMap[mode] || [];
return methodValues.map(value => {
const option = paymentMethodOptions.find(item => item.value === value);
return option ? { label: option.label, type: option.type } : null;
}).filter(Boolean) as Array<{ label: string; type: string }>;
}
</script>
<template>
@ -119,8 +129,8 @@ onMounted(() => {
<el-card class="cabinet-card" :body-style="{ padding: '8px 10px' }">
<div class="card-content">
<img v-if="item.coverImg" :src="item.coverImg" alt="" class="cabinet-image">
<svg v-if="!item.coverImg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 100" width="160" height="100"
class="cabinet-image">
<svg v-if="!item.coverImg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 100" width="160"
height="100" class="cabinet-image">
<!-- 背景浅灰色填充模拟图片容器 -->
<rect width="100%" height="100%" fill="#f8f8f8" rx="4" ry="4" /> <!-- 可选圆角适配卡片风格 -->
<!-- 图片图标简化的图片符号矩形+交叉线直观关联图片 -->
@ -137,8 +147,17 @@ onMounted(() => {
<el-descriptions class="cabinet-info" :column="1">
<!-- <el-descriptions-item class="type">模式{{ item.belongType === 0 ? '借还模式' : '固资模式' }}</el-descriptions-item> -->
<el-descriptions-item class="mode">运行模式<el-tag :type="getModeType(item.mode)">{{ getModeText(item.mode) }}</el-tag></el-descriptions-item>
<el-descriptions-item class="balance">借呗支付<el-tag :type="getBalanceType(item.balanceEnable)">{{ getBalanceEnableText(item.balanceEnable) }}</el-tag></el-descriptions-item>
<el-descriptions-item class="mode">运行模式<el-tag :type="getModeType(item.mode)">{{
getModeText(item.mode) }}</el-tag></el-descriptions-item>
<el-descriptions-item class="mode" >机柜数量{{ item.cabinetCount }}</el-descriptions-item>
<!-- <el-descriptions-item class="payment-methods">支付方式
</el-descriptions-item>
<el-descriptions-item v-for="method in getPaymentMethods(item.mode)" class="payment-methods">
<el-tag :key="method.label"
:type="method.type as '' | 'success' | 'info' | 'warning' | 'danger'" style="margin-right: 5px;">
{{ method.label }}
</el-tag>
</el-descriptions-item> -->
</el-descriptions>
</div>
<el-divider class="divider" />
@ -179,7 +198,7 @@ onMounted(() => {
justify-content: space-between;
.cabinet-image {
width: 50%;
width: 40%;
height: 130px;
padding: 0 3px;
object-fit: contain;

View File

@ -1,9 +1,12 @@
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import { ref, watch, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import { paymentMethodOptions, modeToPaymentMethodMap } from "@/utils/maps/payment";
import { addShop, updateShop, ShopDTO, UpdateShopCommand, AddShopCommand } from "@/api/shop/shop";
import { useWxStore } from "@/store/modules/wx";
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 props = defineProps({
@ -40,6 +43,8 @@ const rules = {
coverImg: [{ required: true, message: "请上传封面图", trigger: "change" }]
};
watch(
() => props.visible,
val => {
@ -108,6 +113,25 @@ const beforeAvatarUpload = (rawFile) => {
}
return true;
};
const currentPaymentMethods = computed(() => {
if (typeof formData.value.mode !== 'number') return [];
const methodValues = modeToPaymentMethodMap[formData.value.mode] || [];
return methodValues.map(value => {
const option = paymentMethodOptions.find(item => item.value === value);
return option ? { label: option.label, type: option.type } : null;
}).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>
<template>
@ -124,8 +148,8 @@ const beforeAvatarUpload = (rawFile) => {
<el-upload class="avatar-uploader" :action="VITE_APP_BASE_API + '/file/upload'" :show-file-list="false"
:on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
<img v-if="formData.coverImg" :src="formData.coverImg" class="avatar" />
<svg v-if="!formData.coverImg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 100" width="160" height="100"
class="cabinet-image">
<svg v-if="!formData.coverImg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 100" width="160"
height="100" class="cabinet-image">
<!-- 背景浅灰色填充模拟图片容器 -->
<rect width="100%" height="100%" fill="#f8f8f8" rx="4" ry="4" /> <!-- 可选圆角适配卡片风格 -->
<!-- 图片图标简化的图片符号矩形+交叉线直观关联图片 -->
@ -156,11 +180,29 @@ const beforeAvatarUpload = (rawFile) => {
<el-option label="耗材模式" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="借呗支付" prop="balanceEnable">
<!-- <el-form-item label="借呗支付" prop="balanceEnable">
<el-select v-model="formData.balanceEnable" placeholder="请选择借呗支付状态">
<el-option label="正常使用" :value="1" />
<el-option label="禁止使用" :value="0" />
</el-select>
</el-form-item> -->
<el-form-item label="支付方式">
<el-tag v-for="method in currentPaymentMethods" :key="method.label" :type="method.type"
style="margin-right: 5px;">
{{ method.label }}
</el-tag>
</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>
<template #footer>

View File

@ -23,6 +23,7 @@ import { getGoodsInfo } from "@/api/shop/goods";
import EditCabinetDrawer from "./edit-cabinet-drawer.vue";
import { CabinetMainboardDTO, deleteMainboard, getMainboardList, updateMainboard } from "@/api/cabinet/mainboards";
import { getShopById, ShopDTO, getModeText } from "@/api/shop/shop";
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
defineOptions({
name: "SmartCabinetDetail"
@ -30,6 +31,7 @@ defineOptions({
const router = useRouter();
const route = useRoute();
const { hasPermission } = useBtnPermissionStore();
const currentCell = ref<CabinetCellDTO>();
const cabinetInfo = ref<SmartCabinetDTO>({
cabinetName: "",
@ -308,7 +310,7 @@ onMounted(() => {
<div class="info-details" v-if="activeTab === 'basic'">
<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>
</div>
@ -318,11 +320,11 @@ onMounted(() => {
<el-descriptions-item label="柜体格式">{{ CabinetImgMap[cabinetInfo.templateNo]?.name || '-'
}}</el-descriptions-item>
<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-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-descriptions-item label="借呗支付">{{ getBalanceEnableText(shopInfo.balanceEnable)
@ -378,11 +380,13 @@ onMounted(() => {
<span class="line-number">{{ item.cellNo }}</span>
</div>
<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">
商品配置
</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">
{{ item.goodsName }}
</el-button>
@ -410,10 +414,10 @@ onMounted(() => {
<el-table-column label="锁控板序号" prop="lockControlNo" width="120" />
<el-table-column label="操作" width="150" fixed="right">
<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 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>
</template>
@ -434,7 +438,8 @@ onMounted(() => {
@refresh="fetchCabinetDetail" />
</el-drawer>
<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 v-model="configuredGoodsVisible" title="管理商品" size="30%" direction="rtl">
<ConfiguredGoodsModal v-model="configuredGoodsVisible" :cell-id="currentCell?.cellId"

View File

@ -14,6 +14,7 @@ import AddFill from "@iconify-icons/ri/add-circle-line";
import SmartCabinetCardFormModal from "./smart-cabinet-card-form-modal.vue";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { getModeText } from "@/api/shop/shop";
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
const { VITE_PUBLIC_IMG_PATH: IMG_PATH } = import.meta.env;
defineOptions({
@ -21,6 +22,7 @@ defineOptions({
});
const router = useRouter();
const { hasPermission } = useBtnPermissionStore();
const formRef = ref();
const modalVisible = ref(false);
const searchFormParams = ref({
@ -115,7 +117,7 @@ onMounted(() => {
<el-form-item class="space-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;">
新增设备
</el-button>

View File

@ -1,20 +1,22 @@
<template>
<el-dialog v-model="visible" title="MQTT网关配置" width="800px">
<el-table :data="mqttList" v-loading="loading" border>
<el-table-column prop="mqttServerId" label="服务ID" width="100" />
<el-table-column prop="serverUrl" label="服务地址" />
<el-table-column prop="username" label="用户名" width="120" />
<el-table-column prop="publishTopic" label="发布主题" />
<el-table-column label="操作" width="80" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleConfig(row)">配置</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="onSizeChange" @current-change="onCurrentChange" />
</el-dialog>
<el-drawer v-model="visible" title="MQTT网关配置" size="30%" direction="rtl">
<div class="gateway-config-container">
<el-table :data="mqttList" v-loading="loading" border>
<el-table-column prop="mqttServerId" label="服务ID" width="100" />
<el-table-column prop="serverUrl" label="服务地址" />
<el-table-column prop="username" label="用户名" width="120" />
<el-table-column prop="publishTopic" label="发布主题" />
<el-table-column label="操作" width="80" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleConfig(row)">配置</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="onSizeChange" @current-change="onCurrentChange" class="pagination" />
</div>
</el-drawer>
</template>
<script setup lang="ts">
@ -95,4 +97,14 @@ const handleConfig = async (row: MqttServerDTO) => {
console.error('配置失败', error);
}
};
</script>
</script>
<style lang="scss" scoped>
.gateway-config-container {
padding: 12px;
background-color: #fff !important;
}
.pagination {
margin-top: 10px;
}
</style>

View File

@ -1,18 +1,20 @@
<template>
<el-dialog v-model="visible" title="地址配置" width="800px">
<el-table :data="shopList" v-loading="loading" border>
<el-table-column prop="shopId" label="地址ID" width="100" />
<el-table-column prop="shopName" label="地址名称" />
<el-table-column label="操作" width="80" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleConfig(row)">配置</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="onSizeChange" @current-change="onCurrentChange" />
</el-dialog>
<el-drawer v-model="visible" title="地址配置" size="30%" direction="rtl">
<div class="shop-config-container">
<el-table :data="shopList" v-loading="loading" border>
<el-table-column prop="shopId" label="地址ID" width="100" />
<el-table-column prop="shopName" label="地址名称" />
<el-table-column label="操作" width="80" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleConfig(row)">配置</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="onSizeChange" @current-change="onCurrentChange" class="pagination" />
</div>
</el-drawer>
</template>
<script setup lang="ts">
@ -95,4 +97,13 @@ const handleConfig = async (row: ShopDTO) => {
console.error('配置失败', error);
}
};
</script>
</script>
<style lang="scss" scoped>
.shop-config-container {
padding: 12px;
background-color: #fff!important;
}
.pagination {
margin-top: 10px;
}
</style>

View File

@ -23,7 +23,8 @@ const searchFormParams = ref<SearchReturnApprovalQuery>({
goodsId: null,
status: null,
approvalTime: null,
paymentMethod: null
paymentMethod: null,
approvalType: 0
});
//
@ -151,6 +152,12 @@ getList();
<el-option label="已驳回" :value="3" />
</el-select>
</el-form-item>
<el-form-item prop="approvalType">
<el-select v-model="searchFormParams.approvalType" placeholder="请选择审批类型" clearable class="!w-[180px]">
<el-option label="借还柜" :value="0" />
<el-option label="固资通" :value="1" />
</el-select>
</el-form-item>
<el-form-item prop="paymentMethod">
<el-select v-model="searchFormParams.paymentMethod" placeholder="请选择支付方式" clearable class="!w-[180px]">
<el-option label="微信支付" value="wechat" />

View File

@ -7,6 +7,7 @@ import { ElMessage } from "element-plus";
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
import EditPen from '@iconify-icons/ep/edit-pen';
import GoodsEditModal from "./goods-edit-modal.vue";
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
const handleEdit = (row: GoodsDTO) => {
goodsInfo.value = row;
@ -21,6 +22,8 @@ defineOptions({
const router = useRouter();
const route = useRoute();
const { hasPermission } = useBtnPermissionStore();
const goodsInfo = ref<GoodsDTO>({
goodsId: 0,
goodsName: "",
@ -118,7 +121,7 @@ watch(goodsId, () => {
<el-tab-pane label="基本信息" name="basic"></el-tab-pane>
<el-tab-pane label="购买记录" name="order"></el-tab-pane>
</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'">
编辑商品
</el-button>

View File

@ -17,6 +17,7 @@ import { deleteGoodsApi } from "@/api/shop/goods";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { useRouter } from "vue-router";
import { useWxStore } from "@/store/modules/wx";
import { useBtnPermissionStore } from "@/store/modules/btnPermission";
defineOptions({
name: "ShopGoods"
@ -24,6 +25,7 @@ defineOptions({
const wxStore = useWxStore();
const router = useRouter();
const { hasPermission } = useBtnPermissionStore();
const formRef = ref();
const tableRef = ref();
const modalVisible = ref(false);
@ -184,7 +186,7 @@ const handleViewDetail = (row: GoodsDTO) => {
<el-form-item class="space-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;">
新增商品
</el-button>

View File

@ -34,6 +34,8 @@ const {
const opType = ref<"add" | "update">("add");
const opRow = ref<RoleDTO>();
const dataScope = ref<Record<number, string>>({});
(window as any).dataScope = dataScope;
const formData = reactive<AddRoleCommand | UpdateRoleCommand>({
roleId: 0,
@ -79,7 +81,7 @@ const rules: FormRules = {
};
watch(activeTab, async (val) => {
console.log("activeTab", val); // key
// console.log("activeTab", val); // key
opRow.value = dataList.value.find(item => item.roleKey == val);
if (!opRow.value) {
return;
@ -87,6 +89,7 @@ watch(activeTab, async (val) => {
await getRoleInfo("update", opRow.value);
Object.assign(formData, opRow.value);
formData.menuIds = opRow.value.selectedMenuList;
updateDataScope(true);
});
async function getRoleInfo(type: "add" | "update", row?: RoleDTO) {
try {
@ -103,7 +106,8 @@ async function getRoleInfo(type: "add" | "update", row?: RoleDTO) {
opType.value = type;
opRow.value = row;
}
const processedMenuOptions = ref<{ categoryName: string; items: MenuDTO[] }[]>([]);
const processedMenuOptions = ref<{ id:number, categoryName: string; items: MenuDTO[]; dataScope: string }[]>([]);
(window as any).processedMenuOptions = processedMenuOptions;
//
watch(menuTree, (val) => {
@ -127,8 +131,9 @@ watch(menuTree, (val) => {
//
if (leaves.length) {
categories.push({
categoryName: menuOption.menuName, // 使
items: leaves //
id: menuOption.id,
categoryName: menuOption.menuName,
items: leaves
});
}
@ -144,21 +149,91 @@ watch(menuTree, (val) => {
};
//
console.log("遍历菜单树 val", val);
// console.log(" val", val);
val.forEach(menuOption => {
collectMenus(menuOption);
})
console.log("categories", categories); // 便
// console.log("categories", categories); // 便
//
processedMenuOptions.value = categories;
});
//
const updateDataScope = (updateAll: boolean) => {
if (updateAll) {
dataScope.value = {};
}
// console.log("updateDataScope formData.menuIds", formData.menuIds);
processedMenuOptions.value.forEach(category => {
//
const hasSelectedMenu = category.items.some(item =>
item.menuName !== '读写' && formData.menuIds.includes(item.id)
);
//
const hasWriteSelectedMenu = category.items.some(item =>
item.menuName == '读写' && formData.menuIds.includes(item.id)
);
if (hasSelectedMenu && !hasWriteSelectedMenu) {
//
dataScope.value[category.id] = dataScope.value[category.id] || `${category.id}-0`;
} else if (hasSelectedMenu && hasWriteSelectedMenu) {
//
dataScope.value[category.id] = dataScope.value[category.id] || `${category.id}-1`;
} else {
//
dataScope.value[category.id] = null;
if (hasWriteSelectedMenu) {
const writeReadItem = category.items.find(item => item.menuName === '读写');
formData.menuIds = formData.menuIds.filter(id => id !== writeReadItem.id);
}
}
});
};
// dataScopemenuIds
watch(
dataScope,
(newDataScope) => {
Object.entries(newDataScope).forEach(([categoryId, scopeValue]) => {
if (!scopeValue) {
return;
}
const [id, permission] = scopeValue.split('-');
const category = processedMenuOptions.value.find(cat => cat.id === Number(id));
if (!category) return;
const writeReadItem = category.items.find(item => item.menuName === '读写');
if (!writeReadItem) return;
if (permission === '1') {
if (!formData.menuIds.includes(writeReadItem.id)) {
formData.menuIds.push(writeReadItem.id);
}
} else {
formData.menuIds = formData.menuIds.filter(id => id !== writeReadItem.id);
}
});
},
{ deep: true }
);
//
watch(() => formData.menuIds,
() => {
updateDataScope(false);
},
{ deep: true }
);
async function handleConfirm() {
try {
await formRef.value?.validate();
loading.value = true;
console.log("opType", opType.value);
formData.dataScope = Object.values(dataScope.value).join(',');
// console.log("opType", opType.value);
if (opType.value === 'add') {
await addRoleApi(formData as AddRoleCommand);
} else {
@ -216,27 +291,42 @@ onSearch().then(() => {
statusList[item].label }}</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item prop="menuIds" class="form-input">
<template #label>
<div class="menu-label">
<span>菜单权限</span>
<!-- <el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
<el-checkbox v-model="formData.menuIds" label="all" class="menu-checkbox">
全部
</el-checkbox>
</el-checkbox-group> -->
<div style="display: flex;">
<div class="form-input-data">
<div class="menu-container">
<div class="menu-label">
<label class="el-form-item__label">数据权限</label>
</div>
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
<div v-if="menu.items.some(item => item.menuName === '读写')">
<el-radio-group v-model="dataScope[menu.id]">
<el-radio :key="`${menu.id}-0`" :label="`${menu.id}-0`">只读</el-radio>
<el-radio :key="`${menu.id}-1`" :label="`${menu.id}-1`">读写</el-radio>
</el-radio-group>
</div>
<div v-else class="empty-placeholder"></div>
<div class="empty-box"></div>
<el-divider class="divider" />
</template>
</div>
</template>
<el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
<span class="menu-category">{{ menu.categoryName }}</span>
<el-checkbox v-for="item in menu.items" :key="item.id" :label="item.id" class="menu-checkbox">
{{ item.menuName }}
</el-checkbox>
<el-divider class="divider" />
</template>
</el-checkbox-group>
</el-form-item>
</div>
<div prop="menuIds" class="form-input" style="flex: 1;">
<div class="menu-container">
<div class="menu-label">
<label class="el-form-item__label">菜单权限</label>
</div>
<el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
<el-checkbox v-for="item in menu.items.filter(item => item.menuName != '读写')" :key="item.id" :label="item.id" class="menu-checkbox">
{{ item.menuName }}
</el-checkbox>
<el-divider class="divider" />
</template>
</el-checkbox-group>
</div>
</div>
</div>
<!-- <el-form-item prop="remark" label="备注" style="margin-bottom: 0">
<el-input type="textarea" class="form-input" v-model="formData.remark" />
</el-form-item> -->
@ -303,6 +393,13 @@ onSearch().then(() => {
flex: 1;
}
.form-input-data {
margin-left: 50px;
.el-radio {
margin-right: 12px;
}
}
.form-input {
width: 40%;
}
@ -316,4 +413,17 @@ onSearch().then(() => {
margin-top: 20px;
text-align: right;
}
.menu-container {
border-radius: 4px;
}
.menu-label {
padding-bottom: 8px;
margin-bottom: 10px;
border-bottom: 1px solid #e4e7ed;
}
.empty-placeholder {
height: 32px;
}
</style>