Browse Source

refactor(公告模块): 优化类型定义和类型转换逻辑

yz 1 month ago
parent
commit
d35055f061

+ 18 - 5
src/api/types/announcement.d.ts

@@ -29,7 +29,7 @@ export type VisibleRolesMask = number;
  * 客户黑名单项类型
  */
 export interface CustomerBlacklistItem {
-  id: string;
+  id: number;
   Customer_NAME: string;
   Customer_CODE: string;
 }
@@ -119,7 +119,7 @@ export interface NoticeFormData {
 }
 
 /**
- * 公告列表项类型定义
+ * 公告记录类型定义
  */
 export interface NoticeRecord {
   id: string;
@@ -137,9 +137,9 @@ export interface NoticeRecord {
   orgId: number;
   orgCode: string;
   orgName: string;
-  visibleRoles: VisibleRolesMask;
-  brandScope: BrandScope | null;
-  customerBlacklist: Array<CustomerBlacklistItem> | null;
+  visibleRoles: string; // API返回字符串格式的角色掩码
+  brandScope: string; // API返回JSON字符串格式
+  customerBlacklist: string; // API返回JSON字符串格式
   remark: string;
 }
 
@@ -162,6 +162,19 @@ export interface BrandOption {
 }
 
 /**
+ * 分类选项类型
+ */
+export interface CategoryOption {
+  id: number;
+  name: string;
+  value: number;
+  label: string;
+  orgId?: number;
+  orgName?: string;
+  sortOrder?: number;
+}
+
+/**
  * API响应类型定义
  */
 export type CategoryListResponse = Promise<ApiResponse<Array<CategoryItem>>>;

+ 2 - 1
src/views/announcement/constants.js

@@ -228,7 +228,8 @@ export const parseRolesMask = (rolesMask) => {
     }
 
     // 转换为数字类型
-    let numericMask;
+    /** @type {number} */
+    let numericMask = 0;
     if (typeof rolesMask === 'string') {
         numericMask = parseInt(rolesMask, 10);
         if (isNaN(numericMask)) {

+ 95 - 44
src/views/announcement/mixins/announcementIndex.js

@@ -1,3 +1,13 @@
+// @ts-check
+
+/**
+ * @this {import('@/views/announcement/types').AnnouncementComponent & Vue}
+ */
+
+/**
+ * @typedef {import('@/views/announcement/types').NoticeRecord} NoticeRecord
+ */
+
 import { getList, update, add, getAnnouncement, getCategoryList } from "@/api/announcement";
 import { getCustomerList } from "@/api/common/index";
 import { mapGetters } from "vuex";
@@ -38,12 +48,12 @@ import {
 
 /**
  * 公告数据项类型定义
- * @typedef {import('@/api/announcement').NoticeRecord} NoticeItem
+ * @typedef {import('@/api/types/announcement').NoticeRecord} NoticeItem
  */
 
 /**
  * 分类选项类型定义
- * @typedef {import('@/api/announcement').CategoryOption} CategoryOption
+ * @typedef {import('@/api/types/announcement').CategoryOption} CategoryOption
  */
 
 /**
@@ -56,7 +66,7 @@ import {
 
 /**
  * 查询参数类型定义
- * @typedef {import('@/api/announcement').NoticeQueryParams} QueryParams
+ * @typedef {import('@/api/types/announcement').NoticeQueryParams} QueryParams
  */
 
 /**
@@ -110,14 +120,10 @@ import {
  * 公告管理混入
  * @mixin AnnouncementIndexMixin
  */
-/**
- * 客户记录类型定义
- * @typedef {import('@/api/common/customer').CustomerRecord} CustomerRecord
- */
 
 /**
  * 客户黑名单选项类型定义
- * @typedef {import('@/api/announcement').CustomerBlacklistItem} CustomerBlacklistOption
+ * @typedef {import('@/api/types/announcement').CustomerBlacklistItem} CustomerBlacklistOption
  */
 
 export default {
@@ -125,15 +131,15 @@ export default {
 
     data() {
         return {
-            /** @type {NoticeItem} 表单数据 */
+            /** @type {Partial<NoticeRecord>} 表单数据 */
             form: {},
-            /** @type {QueryParams} 查询参数 */
+            /** @type {Partial<QueryParams>} 查询参数 */
             query: {},
             /** @type {boolean} 加载状态 */
             loading: true,
             /** @type {boolean} 详情对话框显示状态 */
             detailVisible: false,
-            /** @type {NoticeItem} 当前查看的详情数据 */
+            /** @type {Partial<NoticeRecord>} 当前查看的详情数据 */
             currentDetail: {},
             /** @type {PageInfo} 分页信息 */
             page: {
@@ -149,7 +155,7 @@ export default {
             roleOptions: ROLE_OPTIONS,
             /** @type {Array<CustomerBlacklistOption>} 客户黑名单选项列表 */
             customerBlacklistOptions: [],
-            /** @type {Array<CustomerRecord>} 当前客户黑名单 */
+            /** @type {Array<import("@/api/types/announcement").CustomerBlacklistItem>} 当前客户黑名单 */
             currentCustomerBlacklist: [],
             /** @type {boolean} 客户选项加载状态 */
             customerOptionsLoading: false,
@@ -338,6 +344,7 @@ export default {
 
         /**
          * 权限列表
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @returns {Object} 权限配置对象
          */
         permissionList() {
@@ -351,17 +358,22 @@ export default {
 
         /**
          * 选中的ID字符串
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @returns {string} 逗号分隔的ID字符串
          */
         ids() {
+            /** @type {Array<string>} */
             const ids = [];
-            this.selectionList.forEach(ele => {
+            this.selectionList.forEach((/** @type {NoticeItem} */ ele) => {
                 ids.push(ele.id);
             });
             return ids.join(",");
         }
     },
 
+    /**
+     * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
+     */
     created() {
         this.loadCategoryOptions();
     },
@@ -381,6 +393,7 @@ export default {
         /**
          * 加载分类选项
          * @async
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @returns {Promise<void>}
          * @throws {Error} 当加载分类选项失败时抛出错误
          */
@@ -404,7 +417,7 @@ export default {
                     .sort((a, b) => a.sortOrder - b.sortOrder);
 
                 // 更新表格列配置中的字典数据
-                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
+                const categoryColumn = this.option.column.find((/** @type {TableColumn} */ col) => col.prop === 'categoryId');
                 if (categoryColumn) {
                     categoryColumn.dicData = this.categoryOptions;
                 }
@@ -419,7 +432,7 @@ export default {
                     { id: 2, name: '部门公告', value: 2, label: '部门公告', sortOrder: 1 }
                 ];
 
-                const categoryColumn = this.option.column.find(col => col.prop === 'categoryId');
+                const categoryColumn = this.option.column.find((/** @type {TableColumn} */ col) => col.prop === 'categoryId');
                 if (categoryColumn) {
                     categoryColumn.dicData = this.categoryOptions;
                 }
@@ -429,6 +442,7 @@ export default {
         /**
          * 查看详情
          * @async
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @returns {Promise<void>}
          * @throws {Error} 当获取详情失败时抛出错误
          */
@@ -452,6 +466,7 @@ export default {
         /**
          * 保存行数据
          * @async
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {NoticeItem} row - 行数据
          * @param {Function} done - 完成回调
          * @param {Function} loading - 加载回调
@@ -475,14 +490,20 @@ export default {
 
                 // 计算角色掩码值
                 /** @type {VisibleRolesMask} */
-                const rolesMask = Array.isArray(row.visibleRoles)
-                    ? this.calculateRolesMask(row.visibleRoles)
-                    : (row.visibleRoles || 7); // 默认所有角色可见
+                let rolesMask;
+                if (Array.isArray(row.visibleRoles)) {
+                    // 如果是数组,需要检查是RoleType[]还是RoleOption[]
+                    const roleValues = row.visibleRoles.map(role => 
+                        typeof role === 'object' ? role.value : role
+                    );
+                    rolesMask = this.calculateRolesMask(roleValues);
+                } else {
+                    rolesMask = row.visibleRoles || 7; // 默认所有角色可见
+                }
 
                 // 处理客户黑名单数据
-                /** @type {Array<CustomerRecord>} */
-                const filteredCustomerBlacklist = this.currentCustomerBlacklist.filter(customer => {
-                    const selectedIds = (row.customerBlacklist || []).map(item => item.id);
+                const filteredCustomerBlacklist = this.currentCustomerBlacklist.filter((/** @type {CustomerBlacklistOption} */ customer) => {
+                    const selectedIds = (row.customerBlacklist || []).map((/** @type {CustomerBlacklistOption} */ item) => item.id);
                     return selectedIds.includes(customer.id);
                 });
 
@@ -493,8 +514,9 @@ export default {
                     orgId: this.userInfo?.orgId || row.orgId,
                     orgCode: this.userInfo?.orgCode || row.orgCode,
                     orgName: this.userInfo?.orgName || row.orgName,
-                    brandScope: row.brandScope || {},
+                    brandScope: row.brandScope || undefined,
                     customerBlacklist: filteredCustomerBlacklist.map(customer => ({
+                        ...customer,
                         ID: customer.id,
                         CODE: customer.Customer_CODE,
                         NAME: customer.Customer_NAME
@@ -505,7 +527,7 @@ export default {
                 };
 
                 // 设置分类名称
-                const selectedCategory = this.categoryOptions.find(cat => cat.id == row.categoryId);
+                const selectedCategory = this.categoryOptions.find((/** @type {CategoryOption} */ cat) => String(cat.id) == row.categoryId);
                 if (selectedCategory) {
                     formData.categoryName = selectedCategory.name;
                 }
@@ -527,6 +549,7 @@ export default {
         /**
          * 更新行数据
          * @async
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {NoticeItem} row - 行数据
          * @param {number} index - 行索引
          * @param {Function} done - 完成回调
@@ -557,20 +580,27 @@ export default {
 
                 // 计算角色掩码值
                 /** @type {VisibleRolesMask} */
-                const rolesMask = Array.isArray(row.visibleRoles)
-                    ? this.calculateRolesMask(row.visibleRoles)
-                    : row.visibleRoles;
+                let rolesMask;
+                if (Array.isArray(row.visibleRoles)) {
+                    // 如果是数组,需要检查是RoleType[]还是RoleOption[]
+                    const roleValues = row.visibleRoles.map(role => 
+                        typeof role === 'object' ? role.value : role
+                    );
+                    rolesMask = this.calculateRolesMask(roleValues);
+                } else {
+                    rolesMask = row.visibleRoles;
+                }
 
                 // 设置分类名称
-                const selectedCategory = this.categoryOptions.find(cat => cat.id == row.categoryId);
+                const selectedCategory = this.categoryOptions.find((/** @type {CategoryOption} */ cat) => String(cat.id) == row.categoryId);
                 if (selectedCategory) {
                     row.categoryName = selectedCategory.name;
                 }
 
                 // 处理客户黑名单数据
-                /** @type {Array<CustomerRecord>} */
-                const filteredCustomerBlacklist = this.currentCustomerBlacklist.filter(customer => {
-                    const selectedIds = (row.customerBlacklist || []).map(item => item.id);
+                
+                const filteredCustomerBlacklist = this.currentCustomerBlacklist.filter((/** @type {CustomerBlacklistOption} */ customer) => {
+                    const selectedIds = (row.customerBlacklist || []).map((/** @type {CustomerBlacklistOption} */ item) => item.id);
                     return selectedIds.includes(customer.id);
                 });
 
@@ -578,8 +608,9 @@ export default {
                 /** @type {import('@/api/announcement').NoticeFormData} */
                 const formData = {
                     ...row,
-                    brandScope: row.brandScope || {},
+                    brandScope: row.brandScope || undefined,
                     customerBlacklist: filteredCustomerBlacklist.map(customer => ({
+                        ...customer,
                         ID: customer.id,
                         CODE: customer.Customer_CODE,
                         NAME: customer.Customer_NAME
@@ -604,6 +635,7 @@ export default {
 
         /**
          * 搜索变化事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {QueryParams} params - 搜索参数
          * @param {Function} done - 完成回调
          */
@@ -616,6 +648,7 @@ export default {
 
         /**
          * 搜索重置事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          */
         searchReset() {
             /** @type {QueryParams} */
@@ -625,6 +658,7 @@ export default {
 
         /**
          * 选择变化事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {Array<NoticeItem>} list - 选中的数据列表
          */
         selectionChange(list) {
@@ -634,6 +668,7 @@ export default {
 
         /**
          * 当前页变化事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {number} currentPage - 当前页码(从1开始)
          */
         currentChange(currentPage) {
@@ -644,6 +679,7 @@ export default {
 
         /**
          * 页大小变化事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {number} pageSize - 页大小
          */
         sizeChange(pageSize) {
@@ -654,6 +690,7 @@ export default {
 
         /**
          * 刷新变化事件
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          */
         refreshChange() {
             this.onLoad(this.page, this.query);
@@ -661,6 +698,7 @@ export default {
 
         /**
          * 打开前回调
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @async
          * @param {Function} done - 完成回调
          * @param {string} type - 操作类型(add/edit/view)
@@ -681,9 +719,13 @@ export default {
                     const formData = response.data?.data || {};
 
                     // 将掩码值转换为数值数组格式供表单使用
-                    const roleObjects = this.parseRolesMask(formData.visibleRoles || 0);
-                    formData.visibleRoles = roleObjects.map(role => role.value);
-
+                    if (typeof formData.visibleRoles === 'number') {
+                        const roleObjects = this.parseRolesMask(formData.visibleRoles || 0);
+                        formData.visibleRoles = roleObjects.map(role => role.value);
+                    } else {
+                        formData.visibleRoles = [];
+                    }
+                    
                     // 确保客户黑名单是数组格式
                     formData.customerBlacklist = Array.isArray(formData.customerBlacklist)
                         ? formData.customerBlacklist
@@ -694,22 +736,23 @@ export default {
                         /** @type {Array<CustomerBlacklistOption>} */
                         this.customerBlacklistOptions = [...formData.customerBlacklist];
                     }
+                    if (formData.customerBlacklist == null) {
+                        formData.customerBlacklist = [];
+                    }
 
-                    /** @type {NoticeItem} */
-                    this.form = formData;
+                    this.form = { ...formData };
                 } catch (error) {
                     console.error('获取详情失败:', error);
                     this.$message.error('获取详情失败,请稍后重试');
                 }
             } else if (type === "add") {
                 // 新增时设置默认值
-                /** @type {NoticeItem} */
                 this.form = {
                     orgId: this.userInfo?.orgId || '',
                     orgCode: this.userInfo?.orgCode || '',
                     orgName: this.userInfo?.orgName || '',
                     visibleRoles: [ROLE_TYPES.DEALER], // 默认经销商
-                    brandScope: {},
+                    brandScope: { brandIds: [], brandNames: [] },
                     customerBlacklist: [],
                     remark: '',
                     status: ANNOUNCEMENT_STATUS.DRAFT
@@ -723,6 +766,7 @@ export default {
 
         /**
          * 加载数据
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @async
          * @param {PageInfo} page - 分页信息
          * @param {QueryParams} [params={}] - 查询参数
@@ -769,6 +813,7 @@ export default {
         /**
          * 远程搜索客户数据
          * @async
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {string} query - 搜索关键词
          * @returns {Promise<void>}
          * @throws {Error} 当搜索客户失败时抛出错误
@@ -787,13 +832,17 @@ export default {
                 });
 
                 if (response.data && response.data.success && response.data.data) {
-                    /** @type {Array<CustomerRecord>} */
                     const customers = response.data.data.records || [];
-                    this.currentCustomerBlacklist = customers;
+                    this.currentCustomerBlacklist = customers.map(c => ({
+                        id: c.id ? parseInt(c.id) : 0,
+                        Customer_NAME: c.Customer_NAME,
+                        Customer_CODE: c.Customer_CODE,
+                        Customer_ShortName: c.Customer_ShortName
+                    }))
+
 
-                    /** @type {Array<CustomerBlacklistOption>} */
                     this.customerBlacklistOptions = customers.map(customer => ({
-                        id: customer.id,
+                        id: customer.id ? parseInt(customer.id) : 0,
                         Customer_NAME: customer.Customer_NAME,
                         Customer_CODE: customer.Customer_CODE,
                         Customer_ShortName: customer.Customer_ShortName
@@ -814,6 +863,7 @@ export default {
 
         /**
          * 客户黑名单选择变化处理
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {Array<CustomerBlacklistOption>} selectedCustomers - 选中的客户列表
          */
         handleCustomerBlacklistChange(selectedCustomers) {
@@ -828,6 +878,7 @@ export default {
 
         /**
          * 获取客户显示名称
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          * @param {CustomerBlacklistOption} customer - 客户对象
          * @returns {string} 显示名称
          */
@@ -842,11 +893,11 @@ export default {
 
         /**
          * 清空客户搜索结果
+         * @this {import('@/views/announcement/types').AnnouncementComponent & import('vue').default}
          */
         clearCustomerOptions() {
-            /** @type {Array<CustomerBlacklistOption>} */
             this.customerBlacklistOptions = [];
-            /** @type {Array<CustomerRecord>} */
+            
             this.currentCustomerBlacklist = [];
         },
 

+ 225 - 4
src/views/announcement/types.d.ts

@@ -1,6 +1,227 @@
 
-interface CustomerBlackListType {
-  ID: number
-  CODE: string
-  NAME: string
+import Vue from 'vue';
+import '@/types/global';
+import {
+  NoticeRecord,
+  NoticeQueryParams,
+  CustomerBlacklistItem
+} from '@/api/types/announcement';
+
+import {NoticeRecord} from '@/api/types/announcement';
+import { CategoryOption } from '@/api/types/announcement';
+
+/**
+ * 分类选项类型
+ */
+export type CategoryOption = CategoryOption;
+import {
+  RoleType,
+  VisibleRolesMask,
+  RoleOption,
+  AnnouncementStatus
+} from '@/views/announcement/constants';
+import Category from './category/category.vue';
+
+/**
+ * 验证规则类型定义
+ */
+export interface ValidationRule {
+  required?: boolean;
+  message?: string;
+  trigger?: string | string[];
+  validator?: (rule: any, value: any, callback: (error?: Error) => void) => void;
+}
+
+/**
+ * 品牌范围类型定义
+ */
+export interface BrandScope {
+  brandIds: (string | number)[];
+  brandNames: string[];
+}
+
+// 删除重复的NoticeRecord接口定义,直接使用API中的定义
+
+/**
+ * 客户黑名单类型(兼容旧版本)
+ */
+export interface CustomerBlacklistItem {
+  ID: number;
+  CODE: string;
+  NAME: string;
+  id?: number;
+  code?: string;
+  name?: string;
+}
+
+/**
+ * 分页信息类型定义
+ */
+export interface PageInfo {
+  pageSize: number;
+  currentPage: number;
+  total: number;
+}
+
+/**
+ * 表格配置列定义
+ */
+export interface TableColumn {
+  label: string;
+  prop: string;
+  type?: string;
+  span?: number;
+  search?: boolean;
+  overHidden?: boolean;
+  rules?: Array<ValidationRule>;
+  dicData?: Array<any>;
+  props?: Record<string, any>;
+  slot?: boolean;
+  addDisplay?: boolean;
+  editDisplay?: boolean;
+  hide?: boolean;
+  width?: number;
+  multiple?: boolean;
+  format?: string;
+  valueFormat?: string;
+  showColumn?: boolean;
+  minRows?: number;
+  component?: string;
+  options?: Record<string, any>;
+}
+
+/**
+ * 表格配置选项
+ */
+export interface TableOption {
+  height: string;
+  calcHeight: number;
+  dialogWidth: number;
+  labelWidth: number;
+  tip: boolean;
+  searchShow: boolean;
+  searchMenuSpan: number;
+  border: boolean;
+  index: boolean;
+  viewBtn: boolean;
+  selection: boolean;
+  excelBtn: boolean;
+  columnBtn: boolean;
+  delBtn: boolean;
+  dialogClickModal: boolean;
+  column: Array<TableColumn>;
+}
+
+/**
+ * 前端表单数据类型定义
+ */
+export interface FormData {
+  id?: string;
+  title?: string;
+  content?: string;
+  categoryId?: string;
+  categoryName?: string;
+  orgId?: number;
+  orgCode?: string;
+  orgName?: string;
+  visibleRoles?: RoleType[] | VisibleRolesMask | RoleOption[];
+  brandScope?: BrandScope | null;
+  customerBlacklist?: CustomerBlacklistItem[];
+  remark?: string;
+  status?: number;
+  createUser?: string;
+  createDept?: string;
+  createTime?: string;
+  updateUser?: string;
+  updateTime?: string;
+  isDeleted?: number;
+}
+
+/**
+ * 公告组件数据类型定义
+ */
+export interface AnnouncementComponentData {
+  // Data properties
+  form: Partial<NoticeRecord>;
+  query: Partial<NoticeQueryParams>;
+  loading: boolean;
+  detailVisible: boolean;
+  currentDetail: Partial<NoticeRecord>;
+  page: PageInfo;
+  selectionList: Array<NoticeRecord>;
+  categoryOptions: Array<CategoryOption>;
+  roleOptions: Array<RoleOption>;
+  customerBlacklistOptions: Array<CustomerBlacklistItem>;
+  currentCustomerBlacklist: Array<CustomerBlacklistItem>;
+  customerOptionsLoading: boolean;
+  option: TableOption;
+  data: Array<NoticeRecord>;
+}
+
+/**
+ * 公告组件计算属性类型定义
+ */
+export interface AnnouncementComponentComputed {
+  // Computed properties
+  permissionList: {
+    addBtn: boolean;
+    viewBtn: boolean;
+    delBtn: boolean;
+    editBtn: boolean;
+  };
+  ids: string;
+  permission: any;
+  userInfo: any;
+}
+
+/**
+ * 公告组件方法类型定义
+ */
+export interface AnnouncementComponentMethods {
+  // Methods
+  calculateRolesMask(selectedRoles: Array<RoleType>): VisibleRolesMask;
+  parseRolesMask(rolesMask: string | number): Array<RoleOption>;
+  getVisibleRolesText(visibleRoles: VisibleRolesMask): string;
+  getVisibleRolesTextArray(visibleRoles: VisibleRolesMask): Array<string>;
+  getRoleLabel(roleValue: RoleType): string;
+  getRoleTagType(roleValue: RoleType): string;
+  getStatusTagType(statusValue: AnnouncementStatus): string;
+  getStatusLabel(statusValue: AnnouncementStatus): string;
+  getStatusText(statusValue: AnnouncementStatus): string;
+  loadCategoryOptions(): Promise<void>;
+  handleDetail(): Promise<void>;
+  rowSave(row: Partial<NoticeRecord>, done: Function, loading: Function): Promise<void>;
+  rowUpdate(row: Partial<NoticeRecord>, index: number, done: Function, loading: Function): Promise<void>;
+  searchReset(): void;
+  searchChange(params?: any, done?: Function): void;
+  selectionChange(list: Array<NoticeRecord>): void;
+  currentChange(currentPage: number): void;
+  sizeChange(pageSize: number): void;
+  refreshChange(): void;
+  onLoad(page: PageInfo, params?: Partial<NoticeQueryParams>): Promise<void>;
+  beforeOpen(done: Function, type: string): Promise<void>;
+  remoteSearchCustomers(query: string): Promise<void>;
+  handleCustomerBlacklistChange(selectedCustomers: any): void;
+  getCustomerDisplayName(customer: any): string;
+  clearCustomerOptions(): void;
+}
+
+/**
+ * 公告组件完整类型定义
+ */
+export interface AnnouncementComponent extends AnnouncementComponentData, AnnouncementComponentComputed, AnnouncementComponentMethods {
+  // Vue实例属性
+  $refs: {
+    [key: string]: Vue | Element | Vue[] | Element[] | any;
+  };
+  $emit: (event: string, ...args: any[]) => void;
+  $message: {
+    success(message: string): void;
+    error(message: string): void;
+    warning(message: string): void;
+    info(message: string): void;
+  };
+  $nextTick(): Promise<void>;
+  $set: (target: any, key: string | number, value: any) => any;
+  $delete: (target: any, key: string | number) => void;
 }