Compare commits
7 Commits
ba27975609
...
07a27fa13d
Author | SHA1 | Date |
---|---|---|
|
07a27fa13d | |
|
977615baed | |
|
a2a615d18d | |
|
1d82d1a5be | |
|
6a14db1ee3 | |
|
fb862b7267 | |
|
877fa5f1db |
|
@ -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>
|
||||
|
|
|
@ -188,3 +188,8 @@ export interface QyUserLoginDTO {
|
|||
/** 角色名称 */
|
||||
roleName?: string;
|
||||
}
|
||||
|
||||
/** 获取动态菜单 */
|
||||
export const getPermissions = () => {
|
||||
return http.request<ResponseData<string[]>>("get", "/getPermissions");
|
||||
};
|
|
@ -12,6 +12,12 @@ export interface SearchReturnApprovalQuery extends BasePageQuery {
|
|||
* wechat-微信支付 | balance-余额支付
|
||||
*/
|
||||
paymentMethod?: string;
|
||||
/**
|
||||
* 审批类型
|
||||
* @remarks
|
||||
* 0为借还柜 1为固资通
|
||||
*/
|
||||
approvalType: number;
|
||||
}
|
||||
|
||||
/** 退货审批DTO */
|
||||
|
|
|
@ -25,6 +25,8 @@ export interface ShopDTO {
|
|||
mode?: number;
|
||||
/** 借呗支付(1-正常使用 0-禁止使用) */
|
||||
balanceEnable?: number;
|
||||
/** 机柜数量 */
|
||||
cabinetCount?: number;
|
||||
}
|
||||
|
||||
/** 新增商店命令 */
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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],
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
@ -96,3 +98,13 @@ const handleConfig = async (row: MqttServerDTO) => {
|
|||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gateway-config-container {
|
||||
padding: 12px;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -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">
|
||||
|
@ -96,3 +98,12 @@ const handleConfig = async (row: ShopDTO) => {
|
|||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.shop-config-container {
|
||||
padding: 12px;
|
||||
background-color: #fff!important;
|
||||
}
|
||||
.pagination {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听dataScope变化,反向更新menuIds
|
||||
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>
|
||||
|
|
Loading…
Reference in New Issue