浏览代码

个人消息和系统公告模块。

jiaxiaoqiang 9 月之前
父节点
当前提交
9adaa3f9e8

+ 1 - 1
public/version.json

@@ -1,3 +1,3 @@
 {
-  "version": "1.9"
+  "version": "2.6"
 }

+ 18 - 0
src/api/message/index.ts

@@ -0,0 +1,18 @@
+import request from "@/utils/request";
+import { AxiosPromise } from "axios";
+
+/**
+ * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
+ */
+export function getUserMessageList(type: string): AxiosPromise {
+  return request({
+    url: "/api/v1/sys/message/page",
+    method: "post",
+    data: {
+      pageNo: 1,
+      pageSize: 10,
+      messageId: "10",
+      type: type,
+    },
+  });
+}

+ 0 - 23
src/components/CommonTable/configs/product.ts

@@ -1,23 +0,0 @@
-export const productUrl = "/api/v1/plan/order";
-
-export const productOption = {
-  column: [
-    {
-      label: "这是配置One",
-      prop: "materialCode",
-      search: true,
-    },
-  ],
-};
-
-export const productUrl2 = "22222";
-
-export const productOption2 = {
-  column: [
-    {
-      label: "哈哈哈哈",
-      prop: "materialCode",
-      search: true,
-    },
-  ],
-};

+ 51 - 0
src/components/CommonTable/configs/tableConfig.ts

@@ -0,0 +1,51 @@
+import { useDictionaryStore } from "@/store";
+
+const { dicts } = useDictionaryStore();
+
+export const tableConfig = {
+  USERS: {
+    url: "/api/v1/syn/user/info",
+    column: [
+      {
+        label: "员工编码",
+        prop: "employeeCode",
+        search: true,
+      },
+      {
+        label: "用户id",
+        prop: "id",
+        /*display:false,*/
+      },
+      {
+        label: "用户名",
+        prop: "userName",
+        search: true,
+        width: "120",
+      },
+      {
+        label: "用户电话",
+        prop: "phone",
+        search: true,
+      },
+
+      {
+        label: "所属机构",
+        prop: "deptName",
+        overHidden: true,
+        disabled: true,
+      },
+      /* {
+        label: "所属机构",
+        prop: "institution",
+        filterable: true,
+        type: "select",
+        width: 100,
+        overHidden: true,
+        dicUrl: dictDataUtil.dept_list_url,
+        dicMethod: "post",
+        props: { label: "deptName", value: "id" },
+        disabled: true,
+      },*/
+    ],
+  },
+};

+ 165 - 83
src/components/CommonTable/index.vue

@@ -1,122 +1,204 @@
 <template>
   <el-dialog
     v-model="isShowTable"
+    :before-close="handleClose"
     :title="tableTitle"
     width="1200"
-    :before-close="handleClose"
   >
-    <div>{{ form }}</div>
-    <div>{{ search }}</div>
     <avue-crud
       ref="crudRef"
-      v-model:search="search"
       v-model="form"
+      v-model:page="page"
+      v-model:search="search"
       :data="data"
       :option="option"
-      v-model:page="page"
+      @row-click="rowClick"
       @row-save="createRow"
       @row-update="updateRow"
       @row-del="deleteRow"
+      @search-change="searchChange"
+      @search-reset="resetChange"
+      @size-change="dataList"
+      @current-change="dataList"
       @selection-change="selectionChange"
-    >
-      <template #menu-left="{ size }">
-        <el-button
-          :disabled="toDeleteIds.length < 1"
-          type="danger"
-          icon="el-icon-delete"
-          :size="size"
-          @click="multipleDelete"
-          >删除</el-button
-        >
-      </template>
-    </avue-crud>
-    <!-- <template #footer>
+    />
+
+    <template #footer>
       <div class="dialog-footer">
-        <el-button @click="dialogVisible = false">Cancel</el-button>
-        <el-button type="primary" @click="dialogVisible = false">
-          Confirm
-        </el-button>
+        <el-button @click="handleClose">取消</el-button>
+        <el-button type="primary" @click="onSelected"> 确定</el-button>
       </div>
-    </template> -->
+    </template>
   </el-dialog>
 </template>
 <script setup>
 import { ref, getCurrentInstance } from "vue";
 import { useCrud } from "@/hooks/userCrud";
-import ButtonPermKeys from "@/common/configs/buttonPermission";
+import { tableConfig } from "./configs/tableConfig";
 
-import {
-  productUrl,
-  productOption,
-  productUrl2,
-  productOption2,
-} from "./configs/product";
+const props = defineProps({
+  tableTitle: {
+    default: "",
+    type: String,
+  },
+  tableType: {
+    default: "",
+    type: String,
+  },
+  multiple: {
+    default: false,
+    type: Boolean,
+  },
+  multipleRow: {
+    default: false,
+    type: Boolean,
+  },
+  // 用于多选的返回值
+  multipleKey: {
+    default: "id",
+    type: String,
+  },
+});
 
-import { useCommonStoreHook } from "@/store";
-const { isShowTable, tableType, tableTitle } = toRefs(useCommonStoreHook());
-// 传入一个url,后面不带/
-const { url, form, data, option, search, page, toDeleteIds, Methords, Utils } =
-  useCrud({
-    src: "/api/v1/plan/order",
+const isShowTable = ref(false);
+const startSelect = async (param) => {
+  if (param) {
+    commonConfig.value.params = param;
+  }
+  dataList();
+  isShowTable.value = true;
+};
+
+/**
+ * propName 要跟字典配置的key一致
+ * dictData 字典数据, 注意props的value和label
+ * */
+const refreshDictData = (propName, dictData, keyName = "value") => {
+  nextTick(() => {
+    if (dictData && dictData.length > 0 && crudRef.value) {
+      crudRef.value.updateDic(propName, dictData);
+      search.value[propName] = dictData[0][keyName];
+    }
   });
-const { dataList, createRow, updateRow, deleteRow } = Methords; //增删改查
+};
+
+/**
+ * op 对象的字段要跟option实际字段一直 才能进行合并操作
+ */
+const mergeOption = (op) => {
+  for (const key of Object.keys(op)) {
+    option.value[key] = op[key];
+  }
+};
+
+// 传入一个url,后面不带/
+const {
+  url,
+  form,
+  data,
+  option,
+  search,
+  page,
+  toDeleteIds,
+  selectedRows,
+  Methords,
+  Utils,
+  commonConfig,
+} = useCrud({
+  src: tableConfig[props.tableType].url,
+  multipleSelectKey: props.multipleKey,
+});
+const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } =
+  Methords; //增删改查
 const { selectionChange, multipleDelete } = Methords; //选中和批量删除事件
-const { checkBtnPerm } = Utils; //按钮权限等工具
-// checkBtnPerm(ButtonPermKeys.PLAN.BTNS.order_add) :permission="permission"
-// const permission = reactive({
-//   delBtn: checkPerm(buttonPermission.PLAN.BTNS.order_del),
-//   addBtn: checkPerm(buttonPermission.PLAN.BTNS.order_add),
-//   editBtn: checkPerm(buttonPermission.PLAN.BTNS.order_edit),
-//   menu: true,
-// });
 
 const crudRef = ref(null); //crudRef.value 获取avue-crud对象
 
-// 设置表格列或者其他自定义的option
-// option.value = Object.assign(option.value, {
-//   selection: true,
-//   column: [
-//     {
-//       label: "产品",
-//       prop: "materialCode",
-//       search: true,
-//     },
-//     {
-//       label: "年龄",
-//       prop: "materialModel",
-//       search: true,
-//     },
-//     {
-//       label: "性别",
-//       prop: "orderCode",
-//       search: true,
-//     },
-//     {
-//       label: "身高",
-//       prop: "projectCode",
-//       search: true,
-//     },
-//   ],
-// });
-// option.value = Object.assign(option.value, {
-//   selection: true,
-// });
+const commonTableEmits = defineEmits(["selectedSure", "selectMultipleSure"]);
 
+const calculateColumnWidth = (column) => {
+  if (!column.label) {
+    return column.minWidth;
+  }
+  // 字宽度
+  const fontSize = 17;
+  let width = fontSize * (column.label.length + 2);
+  // 如果开启过滤
+  if (column.filters) {
+    width += 12;
+  }
+  // 如果开启排序
+  if (column.sortable) {
+    width += 24;
+  }
+  return width;
+};
+
+onMounted(() => {
+  url.value = tableConfig[props.tableType].url;
+  option.value = Object.assign(option.value, {
+    menu: false,
+    highlightCurrentRow: true,
+    selection: props.multiple,
+    addBtn: false,
+    height: "500",
+    overHidden: true,
+    indexWidth: "120px",
+    column: tableConfig[props.tableType].column,
+  });
+  // 列处理
+  if (option.value.column) {
+    for (let col of option.value.column) {
+      col.minWidth = calculateColumnWidth(col);
+    }
+  }
+  // dataList();
+});
 watch(
-  () => tableType.value,
-  (type) => {
-    if (type == 1) {
-      url.value = productUrl;
-      option.value = Object.assign(option.value, productOption);
-    } else {
-      url.value = productUrl2;
-      option.value = Object.assign(option.value, productOption2);
+  () => props.tableType,
+  () => {
+    url.value = tableConfig[props.tableType].url;
+    option.value = Object.assign(option.value, {
+      menu: false,
+      highlightCurrentRow: true,
+      selection: props.multiple,
+      addBtn: false,
+      height: "500",
+      overHidden: true,
+      column: tableConfig[props.tableType].column,
+    });
+    // 列处理
+    if (option.value.column) {
+      for (let col of option.value.column) {
+        col.minWidth = calculateColumnWidth(col);
+      }
     }
-    dataList();
+    //dataList();
   }
 );
 
+const selectRowValue = ref({});
+const rowClick = (row) => {
+  selectRowValue.value = row;
+};
+
 const handleClose = () => {
   isShowTable.value = false;
 };
+
+const onSelected = () => {
+  if (props.multiple) {
+    commonTableEmits("selectMultipleSure", toDeleteIds.value);
+  } else {
+    if(props.multipleRow){
+      commonTableEmits("selectedSure", selectedRows.value);
+    }else {
+      commonTableEmits("selectedSure", selectRowValue.value);
+    }
+  }
+
+  isShowTable.value = false;
+};
+
+defineExpose({ startSelect, refreshDictData, mergeOption });
 </script>

+ 146 - 22
src/hooks/userCrud.ts

@@ -1,12 +1,14 @@
 import request from "@/utils/request";
 import { PageOption } from "@smallwei/avue";
-import { ElMessageBox, ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
 import { useUserStoreHook } from "@/store/modules/user";
-import { checkPerm } from "@/directive/permission";
 
 interface UseCrudConfig {
   // 模块的url,用来进行增删改查
   src?: string;
+
+  dataListUrl?: string;
+
   // 需要操作的数据
   row?: any;
   // done用于结束操作
@@ -15,60 +17,70 @@ interface UseCrudConfig {
   index?: number;
   // 用于中断操作
   loading?: () => void;
-  // 查询参数 一般用search的值就可以了
+  // 额外查询参数 一般用search的值就可以了
   params?: object;
   // 是否是编辑,如果是编辑调用更新,否则调用新增
   isEdit?: boolean;
+  //   用于多选选中后添加进入,toDeleteIds中的key值。
+  multipleSelectKey?: string;
+  // todo 这个值是什么
+  pageStr?: string;
 }
 
 export const useCrud = (config?: UseCrudConfig) => {
   const url = ref(config?.src);
-
+  const pageStr = ref(config?.pageStr ? config?.pageStr : "yes");
+  const commonConfig = ref(config);
   /** 表格配置属性 */
   const option = ref({
     searchIcon: true,
+    // searchSpan: 4,
     searchIndex: 3, //searchIcon是否启用功能按钮, searchIndex配置收缩展示的个数,默认为2个
     index: true, //是否显示第几项
+    indexLabel: "序号",
+    indexWidth: "55px",
     refreshBtn: false,
     border: true,
     viewBtn: true,
+    tip: false, //选中的提示
   });
   const data = ref<any>([]); //表格数据
+  const lines = ref<any>([]); //产线
   const form = ref({}); //新增或者编辑弹出的表单绑定值
   /** 表格顶部搜索的表单的变量 v-model */
   const search = ref({});
 
   /** 表格的分页数据 v-model */
   const page = ref<PageOption>({
-    total: 12220,
+    total: 0,
     currentPage: 1,
     pageSize: 10,
   });
   /** 配置项结构 v-model */
   // defaults ?: AvueCrudDefaults;
-
+  // 多选返回的数组,命名是因为后来增加了很多功能,现在这个数组里面的值是根据multipleSelectKey来决定
   const toDeleteIds = ref<Array<string>>([]);
-
+  // 多选返回的数组,返回每一行的row数据
+  const selectedRows = ref<any[]>([]);
   const save = async (config?: UseCrudConfig) => {
-    const path = config?.isEdit ? "/update" : "/add";
     try {
-      const res = await request({
+      const path = config?.isEdit ? "/update" : "/add";
+
+      const res = (await request({
         url: `${url.value}${path}`,
         method: "post",
         data: form.value,
-      });
-      if (res?.data?.code == 200) {
+      })) as any;
+      if (res?.code == 200) {
         Methords.dataList();
         config?.done && config?.done();
+        ElMessage.success(res?.msg ?? "");
       } else {
-        ElMessage.error(res?.data?.msg ?? "");
+        config?.loading && config?.loading();
+        ElMessage.error(res?.msg ?? "");
       }
     } catch (err) {
-      ElMessage.error("Oops, this is a error message.");
-      // config?.loading && config?.loading();
-    } finally {
-      ElMessage.error("Oops, this is a error message.");
-      // config?.done && config?.done();
+      config?.loading && config?.loading();
     }
   };
 
@@ -95,17 +107,27 @@ export const useCrud = (config?: UseCrudConfig) => {
       handleSearchData();
       try {
         const res = await request({
-          url: `${url.value}/page`,
+          url:
+            pageStr.value == "no"
+              ? commonConfig.value?.dataListUrl ?? `${url.value}`
+              : commonConfig.value?.dataListUrl ?? `${url.value}/page`,
           method: "post",
           data: {
             pageNo: page.value.currentPage,
             pageSize: page.value.pageSize,
             ...search.value,
+            ...(commonConfig.value?.params ?? {}),
           },
         });
         if (res?.data) {
-          data.value = res?.data?.records || [];
-          page.value.total = res?.data?.totalCount || 0;
+          if (res?.data instanceof Array) {
+            data.value = res?.data || [];
+            page.value.total = res?.data?.length || 0;
+          } else {
+            data.value = res?.data?.records || [];
+            lines.value = res?.data?.productLines || [];
+            page.value.total = res?.data?.totalCount || 0;
+          }
         }
         config?.done && config?.done();
       } catch (err) {
@@ -114,7 +136,68 @@ export const useCrud = (config?: UseCrudConfig) => {
         config?.done && config?.done();
       }
     },
+    dataEditList: async (config?: UseCrudConfig) => {
+      handleSearchData();
+      try {
+        const res = await request({
+          url: pageStr.value == "no" ? `${url.value}` : `${url.value}/page`,
+          method: "post",
+          data: {
+            pageNo: page.value.currentPage,
+            pageSize: page.value.pageSize,
+            ...search.value,
+            ...(commonConfig.value?.params ?? {}),
+          },
+        });
+        if (res?.data) {
+          if (res?.data instanceof Array) {
+            data.value = res?.data || [];
+            page.value.total = res?.data?.length || 0;
+          } else {
+            data.value = res?.data?.records || [];
+            for (let i = 0; i < data.value.length; i++) {
+              data.value[i].$cellEdit = true;
+              if (
+                data.value[i].children != undefined &&
+                data.value[i].children != null &&
+                data.value[i].children.length > 0
+              ) {
+                for (let j = 0; j < data.value[i].children.length; j++) {
+                  data.value[i].children[j].$cellEdit = true;
+                }
+              }
+            }
+            page.value.total = res?.data?.totalCount || 0;
+          }
+        }
+        config?.done && config?.done();
+      } catch (err) {
+        config?.loading && config?.loading();
+      } finally {
+        config?.done && config?.done();
+      }
+    },
+    dataNoPageList: async (config?: UseCrudConfig) => {
+      handleSearchData();
+      try {
+        const res = await request({
+          url: `${url.value}/list`,
+          method: "post",
+          data: {
+            ...search.value,
+          },
+        });
 
+        if (res?.data) {
+          data.value = res?.data || [];
+        }
+        config?.done && config?.done();
+      } catch (err) {
+        config?.loading && config?.loading();
+      } finally {
+        config?.done && config?.done();
+      }
+    },
     createRow: (row: any, done: () => void, loading: () => void) => {
       save({ row: row, done: done, loading: loading });
     },
@@ -140,11 +223,15 @@ export const useCrud = (config?: UseCrudConfig) => {
         cancelButtonText: "取消",
         type: "warning",
       }).then(async () => {
+        if (row.children && row.children.length > 0) {
+          ElMessage.error("请先解绑下级关系");
+          return;
+        }
         try {
           const res = await request({
             url: `${url.value}/del`,
             method: "post",
-            data: { id: row.id ?? "" },
+            data: row,
           });
           Methords.dataList();
           config?.done && config?.done();
@@ -159,8 +246,12 @@ export const useCrud = (config?: UseCrudConfig) => {
     // 设置selection: true,后监听选中改变事件,将Id存入数组
     selectionChange: (rows?: any[]) => {
       toDeleteIds.value = [];
+      selectedRows.value = [];
       rows?.forEach((element) => {
-        toDeleteIds.value.push(element.id);
+        toDeleteIds.value.push(
+          element[commonConfig.value?.multipleSelectKey ?? "id"]
+        );
+        selectedRows.value.push(element);
       });
     },
 
@@ -190,6 +281,35 @@ export const useCrud = (config?: UseCrudConfig) => {
     },
 
     /**
+     *  表格拖拽后批量保存
+     * */
+    multipleUpdate: async () => {
+      try {
+        // 由于数据带有$开头的属性,所以需要处理下,改为只传id和sortNum。
+        const dtosArray: { id: string; sortNum: number }[] = [];
+        for (let i = 0; i < data.value.length; i++) {
+          let cur = page.value.currentPage ?? 1;
+          cur = cur - 1;
+          const size = page.value.pageSize ?? 10;
+          let sortNum = cur * size;
+          sortNum = sortNum + i;
+
+          dtosArray.push({ id: data.value[i].id, sortNum: sortNum });
+        }
+        const res = await request({
+          url: `${url.value}/batch-update`,
+          method: "post",
+          data: dtosArray,
+        });
+        Methords.dataList();
+        config?.done && config?.done();
+      } catch (err) {
+        config?.loading && config?.loading();
+      } finally {
+        config?.done && config?.done();
+      }
+    },
+    /**
      * 点击搜索按钮触发
      */
     searchChange: async (params: any, done: () => void) => {
@@ -261,6 +381,7 @@ export const useCrud = (config?: UseCrudConfig) => {
      * 根据搜索项导出数据
      */
     exportData: async (urlStr: string) => {
+      handleSearchData();
       const response = await request({
         url: urlStr,
         method: "post",
@@ -275,11 +396,14 @@ export const useCrud = (config?: UseCrudConfig) => {
     url,
     option,
     data,
+    lines,
     form,
     search,
     page,
     toDeleteIds,
+    selectedRows,
     Methords,
     Utils,
+    commonConfig,
   };
 };

+ 6 - 0
src/router/index.ts

@@ -44,6 +44,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         name: "users",
         meta: { hidden: true, back: true, title: "用户权限管理" },
       },
+      {
+        path: "/messagesList/:type",
+        component: () => import("@/views/messages/index.vue"),
+        name: "messagesList",
+        meta: { hidden: true, back: true, title: "信息中心" },
+      },
     ],
   },
   {

+ 7 - 8
src/views/main/main.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-row :gutter="20">
-      <el-col :span="16">
+      <el-col :span="24">
         <Systems />
         <el-row :gutter="20">
           <el-col :span="20">
@@ -15,19 +15,19 @@
           </el-col>
         </el-row>
       </el-col>
-      <el-col :span="8">
-        <Task />
-      </el-col>
+      <!--      <el-col :span="8">-->
+      <!--        <Task />-->
+      <!--      </el-col>-->
     </el-row>
     <el-row :gutter="20">
       <el-col :span="8">
-        <Message />
+        <!--        <Message />-->
       </el-col>
       <el-col :span="8">
-        <Message />
+        <Message message-type="0" title="系统公告" />
       </el-col>
       <el-col :span="8">
-        <Message />
+        <Message message-type="2" />
       </el-col>
     </el-row>
   </div>
@@ -37,7 +37,6 @@
 import Systems from "@/views/systems/systems.vue";
 import ScreenEntry from "@/views/main/screenEntry.vue";
 import Set from "@/views/main/set.vue";
-import Task from "@/views/main/task.vue";
 import Message from "@/views/main/message.vue";
 import { useUserStore } from "@/store";
 

+ 35 - 31
src/views/main/message.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="common-box" style="margin-top: 20px">
-    <TopTitle icon="laba" title="公告通知" />
-    <div class="list-box">
+    <TopTitle :title="title" icon="laba" />
+    <div class="list-box" @click="gotoList">
       <el-scrollbar>
         <div v-for="(item, index) in dataList" :key="index" class="line">
-          <div class="title">{{ item.title }}1</div>
-          <div class="date">{{ item.date }}2</div>
+          <div class="title">{{ item.title }}</div>
+          <div class="date">{{ item.created }}</div>
         </div>
       </el-scrollbar>
     </div>
@@ -13,51 +13,55 @@
 </template>
 
 <script lang="ts" setup>
-const dataList = ref<any[]>([
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-  {},
-]);
+import { getUserMessageList } from "@/api/message";
+
+const props = defineProps({
+  // 消息类型 0 系统公告 1 工位消息 2-具体某些人员消息
+  messageType: {
+    type: String,
+    default: "0",
+  },
+  title: {
+    type: String,
+    default: "个人消息",
+  },
+});
+
+const dataList = ref<any[]>();
+
+onMounted(() => {
+  getUserMessageList(props.messageType).then((res) => {
+    dataList.value = res.data.records ?? [];
+  });
+});
+
+const router = useRouter();
+const gotoList = () => {
+  router.push({ name: "messagesList", params: { type: props.messageType } });
+};
 </script>
 
 <style lang="scss" scoped>
 .list-box {
   margin-top: 5px;
-  height: 300px;
+  height: calc(100vh - 750px);
   padding: 0 20px;
 
   .line {
     border-bottom: 1px solid #eee;
-    line-height: 25px;
+    line-height: 40px;
     display: flex;
     justify-content: space-between;
 
     .title {
-      font-size: 14px;
+      font-size: 16px;
       color: #1a1a1a;
       text-align: left;
     }
 
     .date {
-      font-size: 12px;
-      color: #666666;
+      font-size: 14px;
+      color: #888888;
       text-align: right;
     }
   }

+ 4 - 4
src/views/main/set.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="common-box" style="margin-top: 20px; height: 273px">
+  <div class="common-box" style="margin-top: 20px; height: 263px">
     <div class="sys-container">
       <div
         v-for="(item, index) in sysData"
@@ -74,19 +74,19 @@ const gotoUserPage = () => {
 
 .sys-item {
   min-width: 150px;
-  padding: 15px;
+  padding: 10px;
   display: flex;
   flex-direction: column;
   align-items: center;
 
   .sys-item-img {
-    width: 60px;
+    width: 50px;
   }
 
   .sys-item-title {
     font-size: 18px;
     font-weight: bold;
-    margin-bottom: 10px;
+    margin-bottom: 5px;
     cursor: pointer;
   }
 }

+ 183 - 0
src/views/messages/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="mainContentBox">
+    <avue-crud
+      ref="crudRef"
+      v-model="form"
+      v-model:page="page"
+      v-model:search="search"
+      :data="data"
+      :option="option"
+      @row-save="createRow"
+      @row-update="updateRow"
+      @row-del="deleteRow"
+      @search-change="searchChange"
+      @search-reset="resetChange"
+      @size-change="dataList"
+      @current-change="dataList"
+      @selection-change="selectionChange"
+    >
+      <template #menu-left="{ size }">
+        <el-button
+          :disabled="toDeleteIds.length < 1"
+          :size="size"
+          icon="el-icon-delete"
+          type="danger"
+          @click="multipleDelete"
+          >删除
+        </el-button>
+      </template>
+    </avue-crud>
+
+    <CommonTable
+      ref="userTableRef"
+      :multiple="true"
+      tableTitle="物料"
+      tableType="USERS"
+      @select-multiple-sure="onSelectedFinish"
+    />
+  </div>
+</template>
+<script setup>
+import { ref } from "vue";
+import { useCrud } from "@/hooks/userCrud";
+import { useDictionaryStore } from "@/store";
+
+const userTableRef = ref(null);
+
+// 数据字典相关
+const { dicts } = useDictionaryStore();
+
+// 传入一个url,后面不带/
+const { form, data, option, search, page, toDeleteIds, Methords, Utils } =
+  useCrud({
+    src: "/api/v1/sys/message",
+  });
+const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } =
+  Methords; //增删改查
+const { selectionChange, multipleDelete } = Methords; //选中和批量删除事件
+
+const crudRef = ref(null); //crudRef.value 获取avue-crud对象
+
+const messageType = ref("");
+const route = useRoute();
+onMounted(() => {
+  search.value.messageId = "10";
+  search.value.type = messageType.value = route.params.type;
+
+  console.log(option.value.column);
+  option.value.column.forEach((item) => {
+    if (item.prop === "type") {
+      item.value = messageType.value;
+    }
+    if (item.prop === "receiveUsers") {
+      item.addDisplay = messageType.value === "2";
+    }
+  });
+  dataList();
+});
+
+// 设置表格列或者其他自定义的option
+option.value = Object.assign(option.value, {
+  selection: true,
+  editBtn: false,
+  column: [
+    {
+      label: "创建人",
+      prop: "creator",
+      search: true,
+      addDisplay: false,
+    },
+    {
+      label: "消息标题",
+      prop: "title",
+      search: true,
+    },
+    {
+      label: "用户id ",
+      prop: "receiveUsers",
+      overHidden: true,
+      click: ({ value, column }) => {
+        userTableRef.value.startSelect();
+      },
+    },
+
+    {
+      label: "创建时间",
+      prop: "created",
+      addDisplay: false,
+      width: 180,
+    },
+
+    {
+      label: "已读",
+      prop: "readState",
+      search: false,
+      type: "radio", //类型为单选框
+      dicData: [
+        {
+          label: "否",
+          value: "0",
+        },
+        {
+          label: "是",
+          value: "1",
+        },
+      ],
+      value: "0",
+      addDisplay: false,
+    },
+    {
+      label: "发送状态",
+      prop: "state",
+      search: false,
+      type: "radio",
+      dicData: [
+        {
+          label: "暂存",
+          value: "0",
+        },
+        {
+          label: "直接发送",
+          value: "1",
+        },
+      ],
+      value: "1",
+    },
+    {
+      label: "msgId",
+      prop: "msgId",
+      addDisplay: false,
+      value: "10",
+    },
+    {
+      label: "消息类型",
+      prop: "type",
+      search: false,
+      type: "radio",
+      dicData: [
+        {
+          label: "系统公告",
+          value: "0",
+        },
+        {
+          label: "个人消息",
+          value: "2",
+        },
+      ],
+      value: "0",
+    },
+    {
+      label: "消息内容",
+      prop: "content",
+      search: true,
+      type: "textarea",
+      overHidden: true,
+    },
+  ],
+});
+
+const onSelectedFinish = (selectedRowIds) => {
+  form.value.receiveUsers = selectedRowIds;
+  console.log(selectedRowIds, form.value);
+};
+</script>

+ 12 - 0
src/views/systems/systems.vue

@@ -15,6 +15,7 @@
           size="60"
         />
         <h3 class="sys-item-title">{{ item.menuName }}</h3>
+        <!--        <div class="unclickable"></div>-->
       </div>
     </div>
   </div>
@@ -52,6 +53,7 @@ const openInNewTab = (url: string) => {
   display: flex;
   flex-direction: column;
   align-items: center;
+  position: relative;
 
   .sys-item-img {
     width: 60px;
@@ -62,6 +64,16 @@ const openInNewTab = (url: string) => {
     font-weight: bold;
     margin-bottom: 10px;
   }
+
+  .unclickable {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(128, 128, 128, 0.5); /* 灰色蒙层 */
+    pointer-events: none; /* 使蒙层不影响点击事件 */
+  }
 }
 
 .sys-item:hover {