ソースを参照

refactor(公告管理): 将业务逻辑提取为mixin并优化样式结构

yz 2 週間 前
コミット
7817b190fb

+ 139 - 0
src/views/announcement/index.scss

@@ -0,0 +1,139 @@
+.detail-content {
+  .detail-info {
+    margin-bottom: 20px;
+    padding: 16px;
+    background-color: #f5f7fa;
+    border-radius: 4px;
+    
+    p {
+      margin: 8px 0;
+      line-height: 1.6;
+      
+      strong {
+        color: #303133;
+        margin-right: 8px;
+      }
+    }
+  }
+  
+  .detail-body {
+    padding: 16px;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    background-color: #fff;
+    min-height: 200px;
+    
+    // 富文本内容样式
+    ::v-deep {
+      img {
+        max-width: 100%;
+        height: auto;
+      }
+      
+      table {
+        border-collapse: collapse;
+        width: 100%;
+        
+        td, th {
+          border: 1px solid #ddd;
+          padding: 8px;
+          text-align: left;
+        }
+        
+        th {
+          background-color: #f2f2f2;
+        }
+      }
+      
+      blockquote {
+        border-left: 4px solid #409eff;
+        padding-left: 16px;
+        margin: 16px 0;
+        color: #666;
+        background-color: #f9f9f9;
+      }
+      
+      pre {
+        background-color: #f4f4f4;
+        padding: 12px;
+        border-radius: 4px;
+        overflow-x: auto;
+        
+        code {
+          background-color: transparent;
+          padding: 0;
+        }
+      }
+      
+      code {
+        background-color: #f4f4f4;
+        padding: 2px 4px;
+        border-radius: 2px;
+        font-family: 'Courier New', monospace;
+      }
+    }
+  }
+}
+
+.dialog-footer {
+  text-align: right;
+  
+  .el-button {
+    margin-left: 8px;
+  }
+}
+
+// 表格样式优化
+.avue-crud {
+  .el-tag {
+    margin: 2px;
+  }
+  
+  // 状态标签样式
+  .el-tag--success {
+    background-color: #f0f9ff;
+    border-color: #67c23a;
+    color: #67c23a;
+  }
+  
+  .el-tag--warning {
+    background-color: #fdf6ec;
+    border-color: #e6a23c;
+    color: #e6a23c;
+  }
+  
+  .el-tag--danger {
+    background-color: #fef0f0;
+    border-color: #f56c6c;
+    color: #f56c6c;
+  }
+  
+  .el-tag--info {
+    background-color: #f4f4f5;
+    border-color: #909399;
+    color: #909399;
+  }
+  
+  .el-tag--primary {
+    background-color: #ecf5ff;
+    border-color: #409eff;
+    color: #409eff;
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .detail-content {
+    .detail-info {
+      padding: 12px;
+      
+      p {
+        font-size: 14px;
+      }
+    }
+    
+    .detail-body {
+      padding: 12px;
+    }
+  }
+}

+ 5 - 636
src/views/announcement/index.vue

@@ -78,97 +78,8 @@
     </basic-container>
 </template>
 
-<script >
-import { getList, update, add, getAnnouncement, getCategoryList } from "@/api/announcement";
-import { mapGetters } from "vuex";
-import {
-    ROLE_OPTIONS,
-    STATUS_OPTIONS,
-    getRoleLabel,
-    getRoleTagType,
-    getStatusLabel,
-    getStatusTagType,
-    calculateRolesMask,
-    parseRolesMask,
-    getVisibleRolesText,
-    getVisibleRolesTextArray
-} from '@/views/announcement/constants';
-
-/**
- * 角色类型枚举
- * @typedef {1|2|4} RoleType
- * - 1: 工厂
- * - 2: 经销商
- * - 4: 零售商
- */
-
-/**
- * 可见角色掩码类型 (位运算)
- * @typedef {number} VisibleRolesMask
- * 支持的值:1(工厂) | 2(经销商) | 4(零售商) 及其组合
- * 例如:3(工厂+经销商), 5(工厂+零售商), 6(经销商+零售商), 7(全部)
- */
-
-/**
- * 角色选项类型定义
- * @typedef {Object} RoleOption
- * @property {string} label - 角色显示名称
- * @property {RoleType} value - 角色值(位运算)
- */
-
-/**
- * 公告状态枚举类型
- * @typedef {0|1|2} AnnouncementStatus
- * - 0: 草稿
- * - 1: 已发布
- * - 2: 已下架
- */
-
-/**
- * 公告数据类型定义
- * @typedef {Object} NoticeItem
- * @property {string} id - 公告ID
- * @property {string} title - 公告标题
- * @property {string} content - 公告内容
- * @property {string} categoryId - 分类ID
- * @property {string} categoryName - 分类名称
- * @property {number} orgId - 组织ID
- * @property {string} orgCode - 组织编码
- * @property {string} orgName - 组织名称
- * @property {VisibleRolesMask} visibleRoles - 可见角色掩码(位运算:1工厂 2经销商 4零售商)
- * @property {Object|null} brandScope - 品牌范围
- * @property {Object|null} customerBlacklist - 客户黑名单
- * @property {string} remark - 备注
- * @property {string} createTime - 创建时间
- * @property {string} updateTime - 更新时间
- * @property {AnnouncementStatus} status - 状态 0草稿 1已发布 2已下架
- * @property {number} isDeleted - 是否删除
- */
-
-/**
- * 分类选项类型定义
- * @typedef {Object} CategoryOption
- * @property {string} id - 分类ID
- * @property {string} name - 分类名称
- * @property {string} value - 选项值
- * @property {string} label - 选项标签
- */
-
-/**
- * 分页信息类型定义
- * @typedef {Object} PageInfo
- * @property {number} pageSize - 每页大小
- * @property {number} currentPage - 当前页码
- * @property {number} total - 总记录数
- */
-
-/**
- * 查询参数类型定义
- * @typedef {Object} QueryParams
- * @property {string} [title] - 公告标题
- * @property {string} [categoryId] - 分类ID
- * @property {string} [content] - 公告内容
- */
+<script>
+import AnnouncementIndexMixin from './mixins/announcementIndex.js';
 
 /**
  * 公告管理组件
@@ -176,552 +87,10 @@ import {
  */
 export default {
     name: 'NoticeIndex',
-    data() {
-        return {
-            /** @type {NoticeItem} 表单数据 */
-            form: {},
-            /** @type {QueryParams} 查询参数 */
-            query: {},
-            /** @type {boolean} 加载状态 */
-            loading: true,
-            /** @type {boolean} 详情对话框显示状态 */
-            detailVisible: false,
-            /** @type {NoticeItem} 当前查看的详情数据 */
-            currentDetail: {},
-            /** @type {PageInfo} 分页信息 */
-            page: {
-                pageSize: 10,
-                currentPage: 1,
-                total: 0
-            },
-            /** @type {NoticeItem[]} 选中的数据列表 */
-            selectionList: [],
-            /** @type {CategoryOption[]} 分类选项列表 */
-            categoryOptions: [],
-            /** @type {RoleOption[]} 角色选项列表 */
-            roleOptions: [
-                { label: "工厂", value: 1 },
-                { label: "经销商", value: 2 },
-                { label: "零售商", value: 4 }
-            ],
-            /** @type {Object} 表格配置选项 */
-            option: {
-                height: 'auto',
-                calcHeight: 30,
-                dialogWidth: 1000,
-                labelWidth: 120,
-                tip: false,
-                searchShow: true,
-                searchMenuSpan: 6,
-                border: true,
-                index: true,
-                viewBtn: true,
-                selection: true,
-                excelBtn: false,
-                columnBtn: false,
-                delBtn: false,
-                dialogClickModal: false,
-                column: [
-                    {
-                        label: "公告标题",
-                        prop: "title",
-                        span: 12,
-                        search: true,
-                        overHidden: true,
-                        rules: [{
-                            required: true,
-                            message: "请输入公告标题",
-                            trigger: "blur"
-                        }]
-                    },
-                    {
-                        label: "分类",
-                        prop: "categoryId",
-                        type: "select",
-                        dicData: [],
-                        props: {
-                            label: "name",
-                            value: "id"
-                        },
-                        slot: true,
-                        search: true,
-                        span: 12,
-                        rules: [{
-                            required: true,
-                            message: "请选择分类",
-                            trigger: "change"
-                        }]
-                    },
-                    {
-                        label: "组织名称",
-                        prop: "orgName",
-                        span: 12,
-                        overHidden: true,
-                        rules: [{
-                            required: true,
-                            message: "请输入组织名称",
-                            trigger: "blur"
-                        }]
-                    },
-                    {
-                        label: "组织ID",
-                        prop: "orgId",
-                        span: 12,
-                        type: "number",
-                        rules: [{
-                            required: true,
-                            message: "请输入组织ID",
-                            trigger: "blur"
-                        }]
-                    },
-                    {
-                        label: "组织编码",
-                        prop: "orgCode",
-                        span: 12,
-                        rules: [{
-                            required: true,
-                            message: "请输入组织编码",
-                            trigger: "blur"
-                        },{
-                            pattern: /^[A-Za-z0-9_]+$/,
-                            message: "组织编码只能包含大写字母、数字和下划线",
-                            trigger: "blur"
-                        }]
-                    },
-                    {
-                        label: "可见角色",
-                        prop: "visibleRoles",
-                        type: "select",
-                        multiple: true,
-                        dicData: ROLE_OPTIONS,
-                        slot: true,
-                        span: 12,
-                        props: {
-                            label: "label",
-                            value: "value"
-                        },
-                        rules: [{
-                            required: true,
-                            message: "请选择可见角色",
-                            trigger: "change"
-                        }]
-                    },
-                    {
-                        label: "状态",
-                        prop: "status",
-                        type: "select",
-                        dicData: STATUS_OPTIONS,
-                        slot: true,
-                        addDisplay: true,
-                        editDisplay: true,
-                        width: 80
-                    },
-                    {
-                        label: "创建时间",
-                        prop: "createTime",
-                        type: "datetime",
-                        format: "yyyy-MM-dd HH:mm:ss",
-                        valueFormat: "yyyy-MM-dd HH:mm:ss",
-                        addDisplay: false,
-                        editDisplay: false,
-                        overHidden: true,
-                        width: 150
-                    },
-                    {
-                        label: "分类名称",
-                        prop: "categoryName",
-                        hide: true,
-                        addDisplay: false,
-                        editDisplay: false
-                    },
-                    {
-                        label: "备注",
-                        prop: "remark",
-                        type: "textarea",
-                        span: 24,
-                        minRows: 3,
-                        hide: true
-                    },
-                    {
-                        label: "公告内容",
-                        prop: "content",
-                        component: 'AvueUeditor',
-                        options: {
-                            action: '/api/blade-resource/oss/endpoint/put-file',
-                            props: {
-                                res: "data",
-                                url: "link",
-                            }
-                        },
-                        showColumn: false,
-                        hide: true,
-                        minRows: 6,
-                        span: 24,
-                        rules: [{
-                            required: true,
-                            message: "请输入公告内容",
-                            trigger: "blur"
-                        }]
-                    },
-                    {
-                        label: "品牌范围",
-                        prop: "brandScope",
-                        type: "json",
-                        hide: true,
-                        span: 24
-                    },
-                    {
-                        label: "客户黑名单",
-                        prop: "customerBlacklist",
-                        type: "json",
-                        hide: true,
-                        span: 24
-                    }
-                ]
-            },
-            /** @type {NoticeItem[]} 表格数据 */
-            data: []
-        };
-    },
-    computed: {
-        ...mapGetters(["permission", "userInfo"]),
-        /**
-         * 权限列表
-         * @returns {Object} 权限配置对象
-         */
-        permissionList() {
-            return {
-                // addBtn: this.vaildData(this.permission.announcement_add, false),
-                // viewBtn: this.vaildData(this.permission.announcement_view, false),
-                // delBtn: false,
-                // editBtn: this.vaildData(this.permission.announcement_edit, false)
-                addBtn: true,
-                viewBtn: true,
-                delBtn: false,
-                editBtn: true,
-            };
-        },
-        /**
-         * 选中的ID字符串
-         * @returns {string} 逗号分隔的ID字符串
-         */
-        ids() {
-            const ids = [];
-            this.selectionList.forEach(ele => {
-                ids.push(ele.id);
-            });
-            return ids.join(",");
-        }
-    },
-    created() {
-        this.loadCategoryOptions();
-    },
-    methods: {
-        calculateRolesMask,
-        parseRolesMask,
-        getVisibleRolesText,
-        getVisibleRolesTextArray,
-        getRoleLabel,
-        getRoleTagType,
-        getStatusTagType,
-        getStatusLabel,
-        getStatusText: getStatusLabel,
-        /**
-         * 加载分类选项
-         * @async
-         * @returns {Promise<void>}
-         */
-        async loadCategoryOptions() {
-            try {
-                const res = await getCategoryList();
-                const categoryData = res.data.data || [];
-
-                this.categoryOptions = categoryData
-                    .filter(item => item.status === 1 && item.isDeleted === 0)
-                    .map(item => ({
-                        id: item.id,
-                        name: item.name,
-                        value: item.id,
-                        label: item.name,
-                        orgId: item.orgId,
-                        orgName: item.orgName,
-                        sortOrder: item.sortOrder || 0
-                    }))
-                    .sort((a, b) => a.sortOrder - b.sortOrder);
-
-                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
-                if (categoryColumn) {
-                    categoryColumn.dicData = this.categoryOptions;
-                }
-            } catch (error) {
-                console.error('加载分类选项失败:', error);
-                // 使用默认分类
-                this.categoryOptions = [
-                    { id: '1', name: '系统公告', value: '1', label: '系统公告' },
-                    { id: '2', name: '部门公告', value: '2', label: '部门公告' }
-                ];
-                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
-                if (categoryColumn) {
-                    categoryColumn.dicData = this.categoryOptions;
-                }
-            }
-        },
-        /**
-         * 查看详情
-         * @async
-         * @returns {Promise<void>}
-         */
-        async handleDetail() {
-            if (this.selectionList.length !== 1) {
-                this.$message.warning("请选择一条数据查看详情");
-                return;
-            }
-            try {
-                const res = await getAnnouncement(this.selectionList[0].id);
-                this.currentDetail = res.data.data;
-                this.detailVisible = true;
-            } catch (error) {
-                console.error('获取详情失败:', error);
-                this.$message.error('获取详情失败');
-            }
-        },
-        /**
-         * 保存行数据
-         * @async
-         * @param {NoticeItem} row - 行数据
-         * @param {Function} done - 完成回调
-         * @param {Function} loading - 加载回调
-         * @returns {Promise<void>}
-         */
-        async rowSave(row, done, loading) {
-            try {
-                // 计算角色掩码值
-                const rolesMask = Array.isArray(row.visibleRoles)
-                    ? this.calculateRolesMask(row.visibleRoles)
-                    : (row.visibleRoles || 7); // 默认所有角色可见
-
-                // 设置默认值
-                const formData = {
-                    ...row,
-                    orgId: this.userInfo.orgId || 1,
-                    orgCode: this.userInfo.orgCode || 'ORG_0001',
-                    orgName: this.userInfo.orgName || '默认组织',
-                    brandScope: row.brandScope || {},
-                    customerBlacklist: row.customerBlacklist || {},
-                    remark: row.remark || '',
-                    visibleRoles: rolesMask,
-                    status: row.status !== undefined ? row.status : 0, // 默认草稿状态
-                };
-
-                // 设置分类名称
-                const selectedCategory = this.categoryOptions.find(cat => cat.id === row.categoryId);
-                if (selectedCategory) {
-                    formData.categoryName = selectedCategory.name;
-                }
-
-                await add(formData);
-                await this.onLoad(this.page);
-                this.$message({
-                    type: "success",
-                    message: "操作成功!"
-                });
-                done();
-            } catch (error) {
-                console.error('保存失败:', error);
-                this.$message.error('保存失败');
-                loading();
-            }
-        },
-        /**
-         * 更新行数据
-         * @async
-         * @param {NoticeItem} row - 行数据
-         * @param {number} index - 行索引
-         * @param {Function} done - 完成回调
-         * @param {Function} loading - 加载回调
-         * @returns {Promise<void>}
-         */
-        async rowUpdate(row, index, done, loading) {
-            try {
-                // 计算角色掩码值
-                const rolesMask = Array.isArray(row.visibleRoles)
-                    ? this.calculateRolesMask(row.visibleRoles)
-                    : row.visibleRoles;
-
-                // 设置分类名称
-                const selectedCategory = this.categoryOptions.find(cat => cat.id === row.categoryId);
-                if (selectedCategory) {
-                    row.categoryName = selectedCategory.name;
-                }
-
-                // 确保必要字段存在
-                const formData = {
-                    ...row,
-                    brandScope: row.brandScope || {},
-                    customerBlacklist: row.customerBlacklist || {},
-                    remark: row.remark || '',
-                    visibleRoles: rolesMask
-                };
-
-                await update(formData);
-                this.onLoad(this.page);
-                this.$message({
-                    type: "success",
-                    message: "操作成功!"
-                });
-                done();
-            } catch (error) {
-                console.error('更新失败:', error);
-                this.$message.error('更新失败');
-                loading();
-            }
-        },
-        /**
-         * 重置搜索
-         * @returns {void}
-         */
-        searchReset() {
-            this.query = {};
-            this.onLoad(this.page);
-        },
-        /**
-         * 搜索变化
-         * @param {QueryParams} params - 搜索参数
-         * @param {Function} done - 完成回调
-         * @returns {void}
-         */
-        searchChange(params, done) {
-            this.query = params;
-            this.page.currentPage = 1;
-            this.onLoad(this.page, params);
-            done();
-        },
-        /**
-         * 选择变化
-         * @param {NoticeItem[]} list - 选中的数据列表
-         * @returns {void}
-         */
-        selectionChange(list) {
-            this.selectionList = list;
-        },
-        /**
-         * 清空选择
-         * @returns {void}
-         */
-        selectionClear() {
-            this.selectionList = [];
-            this.$refs.crud.toggleSelection();
-        },
-        /**
-         * 打开前回调
-         * @async
-         * @param {Function} done - 完成回调
-         * @param {string} type - 操作类型
-         * @returns {Promise<void>}
-         */
-        async beforeOpen(done, type) {
-            if (["edit", "view"].includes(type)) {
-                try {
-                    const res = await getAnnouncement(this.form.id);
-                    const formData = res.data.data;
-                    // 将掩码值转换为数值数组格式供表单使用
-                    const roleObjects = this.parseRolesMask(formData.visibleRoles);
-                    formData.visibleRoles = roleObjects.map(role => role.value);
-                    this.form = formData;
-                } catch (error) {
-                    console.error('获取详情失败:', error);
-                }
-            } else if (type === "add") {
-                // 新增时设置默认值
-                this.form = {
-                    orgId: this.userInfo.orgId || '',
-                    orgCode: this.userInfo.orgCode || '',
-                    orgName: this.userInfo.orgName || '',
-                    visibleRoles: [], // 默认经销商
-                    brandScope: {},
-                    customerBlacklist: {},
-                    remark: ''
-                };
-            }
-            done();
-        },
-        /**
-         * 当前页变化
-         * @param {number} currentPage - 当前页码
-         * @returns {void}
-         */
-        currentChange(currentPage) {
-            this.page.currentPage = currentPage;
-        },
-        /**
-         * 页大小变化
-         * @param {number} pageSize - 页大小
-         * @returns {void}
-         */
-        sizeChange(pageSize) {
-            this.page.pageSize = pageSize;
-        },
-        /**
-         * 刷新变化
-         * @returns {void}
-         */
-        refreshChange() {
-            this.onLoad(this.page, this.query);
-        },
-        /**
-         * 加载数据
-         * @async
-         * @param {PageInfo} page - 分页信息
-         * @param {QueryParams} [params={}] - 查询参数
-         * @returns {Promise<void>}
-         */
-        async onLoad(page, params = {}) {
-            this.loading = true;
-            try {
-                const queryParams = {
-                    ...params,
-                    ...this.query
-                };
-
-                const res = await getList(page.currentPage, page.pageSize, queryParams);
-                const data = res.data.data;
-                this.page.total = data.total;
-                this.data = data.records;
-                this.loading = false;
-                this.selectionClear();
-            } catch (error) {
-                console.error('加载数据失败:', error);
-                this.$message.error('加载数据失败');
-                this.loading = false;
-            }
-        }
-    }
+    mixins: [AnnouncementIndexMixin]
 };
-
 </script>
 
-<style lang="scss" scoped >
-.detail-content {
-    padding: 20px;
-}
-
-.detail-info {
-    margin: 20px 0;
-    padding: 15px;
-    background-color: #f5f5f5;
-    border-radius: 4px;
-}
-
-.detail-info p {
-    margin: 8px 0;
-}
-
-.detail-body {
-    margin-top: 20px;
-    padding: 15px;
-    border: 1px solid #e4e7ed;
-    border-radius: 4px;
-    min-height: 200px;
-}
-
+<style lang="scss" scoped>
+@import './index.scss';
 </style>

+ 665 - 0
src/views/announcement/mixins/announcementIndex.js

@@ -0,0 +1,665 @@
+import { getList, update, add, getAnnouncement, getCategoryList } from "@/api/announcement";
+import { mapGetters } from "vuex";
+import {
+    ROLE_OPTIONS,
+    STATUS_OPTIONS,
+    getRoleLabel,
+    getRoleTagType,
+    getStatusLabel,
+    getStatusTagType,
+    calculateRolesMask,
+    parseRolesMask,
+    getVisibleRolesText,
+    getVisibleRolesTextArray
+} from '@/views/announcement/constants';
+
+/**
+ * 角色类型枚举
+ * @typedef {1|2|4} RoleType
+ * - 1: 工厂
+ * - 2: 经销商
+ * - 4: 零售商
+ */
+
+/**
+ * 可见角色掩码类型 (位运算)
+ * @typedef {number} VisibleRolesMask
+ * 支持的值:1(工厂) | 2(经销商) | 4(零售商) 及其组合
+ * 例如:3(工厂+经销商), 5(工厂+零售商), 6(经销商+零售商), 7(全部)
+ */
+
+/**
+ * 角色选项类型定义
+ * @typedef {Object} RoleOption
+ * @property {string} label - 角色显示名称
+ * @property {RoleType} value - 角色值(位运算)
+ */
+
+/**
+ * 公告状态枚举类型
+ * @typedef {0|1|2} AnnouncementStatus
+ * - 0: 草稿
+ * - 1: 已发布
+ * - 2: 已下架
+ */
+
+/**
+ * 公告数据类型定义
+ * @typedef {Object} NoticeItem
+ * @property {string} id - 公告ID
+ * @property {string} title - 公告标题
+ * @property {string} content - 公告内容
+ * @property {string} categoryId - 分类ID
+ * @property {string} categoryName - 分类名称
+ * @property {number} orgId - 组织ID
+ * @property {string} orgCode - 组织编码
+ * @property {string} orgName - 组织名称
+ * @property {VisibleRolesMask} visibleRoles - 可见角色掩码(位运算:1工厂 2经销商 4零售商)
+ * @property {Object|null} brandScope - 品牌范围
+ * @property {Object|null} customerBlacklist - 客户黑名单
+ * @property {string} remark - 备注
+ * @property {string} createTime - 创建时间
+ * @property {string} updateTime - 更新时间
+ * @property {AnnouncementStatus} status - 状态 0草稿 1已发布 2已下架
+ * @property {number} isDeleted - 是否删除
+ */
+
+/**
+ * 分类选项类型定义
+ * @typedef {Object} CategoryOption
+ * @property {string} id - 分类ID
+ * @property {string} name - 分类名称
+ * @property {string} value - 选项值
+ * @property {string} label - 选项标签
+ */
+
+/**
+ * 分页信息类型定义
+ * @typedef {Object} PageInfo
+ * @property {number} pageSize - 每页大小
+ * @property {number} currentPage - 当前页码
+ * @property {number} total - 总记录数
+ */
+
+/**
+ * 查询参数类型定义
+ * @typedef {Object} QueryParams
+ * @property {string} [title] - 公告标题
+ * @property {string} [categoryId] - 分类ID
+ * @property {string} [content] - 公告内容
+ */
+
+/**
+ * 表格配置列定义
+ * @typedef {Object} TableColumn
+ * @property {string} label - 列标签
+ * @property {string} prop - 属性名
+ * @property {string} [type] - 输入类型
+ * @property {number} [span] - 栅格占位
+ * @property {boolean} [search] - 是否可搜索
+ * @property {boolean} [overHidden] - 是否超出隐藏
+ * @property {Array<Object>} [rules] - 验证规则
+ * @property {Array<Object>} [dicData] - 字典数据
+ * @property {Object} [props] - 属性配置
+ * @property {boolean} [slot] - 是否使用插槽
+ * @property {boolean} [addDisplay] - 新增时是否显示
+ * @property {boolean} [editDisplay] - 编辑时是否显示
+ * @property {boolean} [hide] - 是否隐藏
+ * @property {number} [width] - 列宽度
+ * @property {boolean} [multiple] - 是否多选
+ * @property {string} [format] - 格式化
+ * @property {string} [valueFormat] - 值格式化
+ * @property {boolean} [showColumn] - 是否显示列
+ * @property {number} [minRows] - 最小行数
+ * @property {string} [component] - 组件名称
+ * @property {Object} [options] - 组件选项
+ */
+
+/**
+ * 表格配置选项
+ * @typedef {Object} TableOption
+ * @property {string} height - 表格高度
+ * @property {number} calcHeight - 计算高度
+ * @property {number} dialogWidth - 对话框宽度
+ * @property {number} labelWidth - 标签宽度
+ * @property {boolean} tip - 是否显示提示
+ * @property {boolean} searchShow - 是否显示搜索
+ * @property {number} searchMenuSpan - 搜索菜单占位
+ * @property {boolean} border - 是否显示边框
+ * @property {boolean} index - 是否显示序号
+ * @property {boolean} viewBtn - 是否显示查看按钮
+ * @property {boolean} selection - 是否显示选择框
+ * @property {boolean} excelBtn - 是否显示导出按钮
+ * @property {boolean} columnBtn - 是否显示列设置按钮
+ * @property {boolean} delBtn - 是否显示删除按钮
+ * @property {boolean} dialogClickModal - 对话框点击遮罩是否关闭
+ * @property {Array<TableColumn>} column - 列配置
+ */
+
+/**
+ * 公告管理混入
+ * @mixin AnnouncementIndexMixin
+ */
+export default {
+    name: 'AnnouncementIndexMixin',
+
+    data() {
+        return {
+            /** @type {NoticeItem} 表单数据 */
+            form: {},
+            /** @type {QueryParams} 查询参数 */
+            query: {},
+            /** @type {boolean} 加载状态 */
+            loading: true,
+            /** @type {boolean} 详情对话框显示状态 */
+            detailVisible: false,
+            /** @type {NoticeItem} 当前查看的详情数据 */
+            currentDetail: {},
+            /** @type {PageInfo} 分页信息 */
+            page: {
+                pageSize: 10,
+                currentPage: 1,
+                total: 0
+            },
+            /** @type {Array<NoticeItem>} 选中的数据列表 */
+            selectionList: [],
+            /** @type {Array<CategoryOption>} 分类选项列表 */
+            categoryOptions: [],
+            /** @type {Array<RoleOption>} 角色选项列表 */
+            roleOptions: [
+                { label: "工厂", value: 1 },
+                { label: "经销商", value: 2 },
+                { label: "零售商", value: 4 }
+            ],
+            /** @type {TableOption} 表格配置选项 */
+            option: {
+                height: 'auto',
+                calcHeight: 30,
+                dialogWidth: 1000,
+                labelWidth: 120,
+                tip: false,
+                searchShow: true,
+                searchMenuSpan: 6,
+                border: true,
+                index: true,
+                viewBtn: true,
+                selection: true,
+                excelBtn: false,
+                columnBtn: false,
+                delBtn: false,
+                dialogClickModal: false,
+                column: [
+                    {
+                        label: "公告标题",
+                        prop: "title",
+                        span: 12,
+                        search: true,
+                        overHidden: true,
+                        rules: [{
+                            required: true,
+                            message: "请输入公告标题",
+                            trigger: "blur"
+                        }]
+                    },
+                    {
+                        label: "分类",
+                        prop: "categoryId",
+                        type: "select",
+                        dicData: [],
+                        props: {
+                            label: "name",
+                            value: "id"
+                        },
+                        slot: true,
+                        search: true,
+                        span: 12,
+                        rules: [{
+                            required: true,
+                            message: "请选择分类",
+                            trigger: "change"
+                        }]
+                    },
+                    {
+                        label: "组织名称",
+                        prop: "orgName",
+                        span: 12,
+                        overHidden: true,
+                        rules: [{
+                            required: true,
+                            message: "请输入组织名称",
+                            trigger: "blur"
+                        }]
+                    },
+                    {
+                        label: "组织ID",
+                        prop: "orgId",
+                        span: 12,
+                        type: "number",
+                        rules: [{
+                            required: true,
+                            message: "请输入组织ID",
+                            trigger: "blur"
+                        }]
+                    },
+                    {
+                        label: "组织编码",
+                        prop: "orgCode",
+                        span: 12,
+                        rules: [{
+                            required: true,
+                            message: "请输入组织编码",
+                            trigger: "blur"
+                        },{
+                            pattern: /^[A-Za-z0-9_]+$/,
+                            message: "组织编码只能包含大写字母、数字和下划线",
+                            trigger: "blur"
+                        }]
+                    },
+                    {
+                        label: "可见角色",
+                        prop: "visibleRoles",
+                        type: "select",
+                        multiple: true,
+                        dicData: ROLE_OPTIONS,
+                        slot: true,
+                        span: 12,
+                        props: {
+                            label: "label",
+                            value: "value"
+                        },
+                        rules: [{
+                            required: true,
+                            message: "请选择可见角色",
+                            trigger: "change"
+                        }]
+                    },
+                    {
+                        label: "状态",
+                        prop: "status",
+                        type: "select",
+                        dicData: STATUS_OPTIONS,
+                        slot: true,
+                        addDisplay: true,
+                        editDisplay: true,
+                        // width: 80
+                    },
+                    {
+                        label: "创建时间",
+                        prop: "createTime",
+                        type: "datetime",
+                        format: "yyyy-MM-dd HH:mm:ss",
+                        valueFormat: "yyyy-MM-dd HH:mm:ss",
+                        addDisplay: false,
+                        editDisplay: false,
+                        overHidden: true,
+                        width: 150
+                    },
+                    {
+                        label: "分类名称",
+                        prop: "categoryName",
+                        hide: true,
+                        addDisplay: false,
+                        editDisplay: false
+                    },
+                    {
+                        label: "备注",
+                        prop: "remark",
+                        type: "textarea",
+                        span: 24,
+                        minRows: 3,
+                        hide: true
+                    },
+                    {
+                        label: "公告内容",
+                        prop: "content",
+                        component: 'AvueUeditor',
+                        options: {
+                            action: '/api/blade-resource/oss/endpoint/put-file',
+                            props: {
+                                res: "data",
+                                url: "link",
+                            }
+                        },
+                        showColumn: false,
+                        hide: true,
+                        minRows: 6,
+                        span: 24,
+                        rules: [{
+                            required: true,
+                            message: "请输入公告内容",
+                            trigger: "blur"
+                        }]
+                    },
+                    {
+                        label: "品牌范围",
+                        prop: "brandScope",
+                        type: "json",
+                        hide: true,
+                        span: 24
+                    },
+                    {
+                        label: "客户黑名单",
+                        prop: "customerBlacklist",
+                        type: "json",
+                        hide: true,
+                        span: 24
+                    }
+                ]
+            },
+            /** @type {Array<NoticeItem>} 表格数据 */
+            data: []
+        };
+    },
+
+    computed: {
+        ...mapGetters(["permission", "userInfo"]),
+
+        /**
+         * 权限列表
+         * @returns {Object} 权限配置对象
+         */
+        permissionList() {
+            return {
+                addBtn: true,
+                viewBtn: true,
+                delBtn: false,
+                editBtn: true,
+            };
+        },
+
+        /**
+         * 选中的ID字符串
+         * @returns {string} 逗号分隔的ID字符串
+         */
+        ids() {
+            const ids = [];
+            this.selectionList.forEach(ele => {
+                ids.push(ele.id);
+            });
+            return ids.join(",");
+        }
+    },
+
+    created() {
+        this.loadCategoryOptions();
+    },
+
+    methods: {
+        // 导入常量工具函数
+        calculateRolesMask,
+        parseRolesMask,
+        getVisibleRolesText,
+        getVisibleRolesTextArray,
+        getRoleLabel,
+        getRoleTagType,
+        getStatusTagType,
+        getStatusLabel,
+        getStatusText: getStatusLabel,
+
+        /**
+         * 加载分类选项
+         * @async
+         * @returns {Promise<void>}
+         */
+        async loadCategoryOptions() {
+            try {
+                const res = await getCategoryList();
+                const categoryData = res.data.data || [];
+
+                this.categoryOptions = categoryData
+                    .filter(item => item.status === 1 && item.isDeleted === 0)
+                    .map(item => ({
+                        id: item.id,
+                        name: item.name,
+                        value: item.id,
+                        label: item.name,
+                        orgId: item.orgId,
+                        orgName: item.orgName,
+                        sortOrder: item.sortOrder || 0
+                    }))
+                    .sort((a, b) => a.sortOrder - b.sortOrder);
+
+                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
+                if (categoryColumn) {
+                    categoryColumn.dicData = this.categoryOptions;
+                }
+            } catch (error) {
+                console.error('加载分类选项失败:', error);
+                // 使用默认分类
+                this.categoryOptions = [
+                    { id: '1', name: '系统公告', value: '1', label: '系统公告' },
+                    { id: '2', name: '部门公告', value: '2', label: '部门公告' }
+                ];
+                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
+                if (categoryColumn) {
+                    categoryColumn.dicData = this.categoryOptions;
+                }
+            }
+        },
+
+        /**
+         * 查看详情
+         * @async
+         * @returns {Promise<void>}
+         */
+        async handleDetail() {
+            if (this.selectionList.length !== 1) {
+                this.$message.warning("请选择一条数据查看详情");
+                return;
+            }
+            try {
+                const res = await getAnnouncement(this.selectionList[0].id);
+                this.currentDetail = res.data.data;
+                this.detailVisible = true;
+            } catch (error) {
+                console.error('获取详情失败:', error);
+                this.$message.error('获取详情失败');
+            }
+        },
+
+        /**
+         * 保存行数据
+         * @async
+         * @param {NoticeItem} row - 行数据
+         * @param {Function} done - 完成回调
+         * @param {Function} loading - 加载回调
+         * @returns {Promise<void>}
+         */
+        async rowSave(row, done, loading) {
+            try {
+                // 计算角色掩码值
+                const rolesMask = Array.isArray(row.visibleRoles)
+                    ? this.calculateRolesMask(row.visibleRoles)
+                    : (row.visibleRoles || 7); // 默认所有角色可见
+
+                // 设置默认值
+                const formData = {
+                    ...row,
+                    orgId: this.userInfo.orgId || 1,
+                    orgCode: this.userInfo.orgCode || 'ORG_0001',
+                    orgName: this.userInfo.orgName || '默认组织',
+                    brandScope: row.brandScope || {},
+                    customerBlacklist: row.customerBlacklist || {},
+                    remark: row.remark || '',
+                    visibleRoles: rolesMask,
+                    status: row.status !== undefined ? row.status : 0, // 默认草稿状态
+                };
+
+                // 设置分类名称
+                const selectedCategory = this.categoryOptions.find(cat => cat.id === row.categoryId);
+                if (selectedCategory) {
+                    formData.categoryName = selectedCategory.name;
+                }
+
+                await add(formData);
+                await this.onLoad(this.page);
+                this.$message({
+                    type: "success",
+                    message: "操作成功!"
+                });
+                done();
+            } catch (error) {
+                console.error('保存失败:', error);
+                this.$message.error('保存失败');
+                loading();
+            }
+        },
+
+        /**
+         * 更新行数据
+         * @async
+         * @param {NoticeItem} row - 行数据
+         * @param {number} index - 行索引
+         * @param {Function} done - 完成回调
+         * @param {Function} loading - 加载回调
+         * @returns {Promise<void>}
+         */
+        async rowUpdate(row, index, done, loading) {
+            try {
+                // 计算角色掩码值
+                const rolesMask = Array.isArray(row.visibleRoles)
+                    ? this.calculateRolesMask(row.visibleRoles)
+                    : row.visibleRoles;
+
+                // 设置分类名称
+                const selectedCategory = this.categoryOptions.find(cat => cat.id === row.categoryId);
+                if (selectedCategory) {
+                    row.categoryName = selectedCategory.name;
+                }
+
+                // 确保必要字段存在
+                const formData = {
+                    ...row,
+                    brandScope: row.brandScope || {},
+                    customerBlacklist: row.customerBlacklist || {},
+                    remark: row.remark || '',
+                    visibleRoles: rolesMask
+                };
+
+                await update(formData);
+                await this.onLoad(this.page);
+                this.$message({
+                    type: "success",
+                    message: "操作成功!"
+                });
+                done();
+            } catch (error) {
+                console.error('更新失败:', error);
+                this.$message.error('更新失败');
+                loading();
+            }
+        },
+
+        /**
+         * 搜索变化事件
+         * @param {QueryParams} params - 搜索参数
+         * @param {Function} done - 完成回调
+         */
+        searchChange(params, done) {
+            this.query = params;
+            this.onLoad(this.page, params);
+            done();
+        },
+
+        /**
+         * 搜索重置事件
+         */
+        searchReset() {
+            this.query = {};
+            this.onLoad(this.page);
+        },
+
+        /**
+         * 选择变化事件
+         * @param {Array<NoticeItem>} list - 选中的数据列表
+         */
+        selectionChange(list) {
+            this.selectionList = list;
+        },
+
+        /**
+         * 当前页变化事件
+         * @param {number} currentPage - 当前页码
+         */
+        currentChange(currentPage) {
+            this.page.currentPage = currentPage;
+        },
+
+        /**
+         * 页大小变化事件
+         * @param {number} pageSize - 页大小
+         */
+        sizeChange(pageSize) {
+            this.page.pageSize = pageSize;
+        },
+
+        /**
+         * 刷新变化事件
+         */
+        refreshChange() {
+            this.onLoad(this.page, this.query);
+        },
+
+        /**
+         * 打开前回调
+         * @async
+         * @param {Function} done - 完成回调
+         * @param {string} type - 操作类型
+         * @returns {Promise<void>}
+         */
+        async beforeOpen(done, type) {
+            if (["edit", "view"].includes(type)) {
+                try {
+                    const res = await getAnnouncement(this.form.id);
+                    const formData = res.data.data;
+                    // 将掩码值转换为数值数组格式供表单使用
+                    const roleObjects = this.parseRolesMask(formData.visibleRoles);
+                    formData.visibleRoles = roleObjects.map(role => role.value);
+                    this.form = formData;
+                } catch (error) {
+                    console.error('获取详情失败:', error);
+                }
+            } else if (type === "add") {
+                // 新增时设置默认值
+                this.form = {
+                    orgId: this.userInfo.orgId || '',
+                    orgCode: this.userInfo.orgCode || '',
+                    orgName: this.userInfo.orgName || '',
+                    visibleRoles: [], // 默认经销商
+                    brandScope: {},
+                    customerBlacklist: {},
+                    remark: ''
+                };
+            }
+            done();
+        },
+
+        /**
+         * 加载数据
+         * @async
+         * @param {PageInfo} page - 分页信息
+         * @param {QueryParams} params - 查询参数
+         * @returns {Promise<void>}
+         */
+        async onLoad(page, params = {}) {
+            this.loading = true;
+            try {
+                const query = {
+                    ...params,
+                    current: page.currentPage,
+                    size: page.pageSize
+                };
+
+                const res = await getList(page.currentPage, page.pageSize, query);
+                const data = res.data.data;
+                this.page.total = data.total;
+                this.data = data.records;
+            } catch (error) {
+                console.error('加载数据失败:', error);
+                this.$message.error('加载数据失败');
+            } finally {
+                this.loading = false;
+            }
+        }
+    }
+};