| | |
| | | |
| | | ```typescript |
| | | // ✅ 正确:组件文件使用 PascalCase |
| | | UserList.vue |
| | | DictTag.vue |
| | | TableAction.vue |
| | | UserList.vue; |
| | | DictTag.vue; |
| | | TableAction.vue; |
| | | |
| | | // ❌ 错误:使用 kebab-case 或其他命名 |
| | | user-list.vue |
| | | dict_tag.vue |
| | | user - list.vue; |
| | | dict_tag.vue; |
| | | ``` |
| | | |
| | | #### 组件定义 |
| | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | // ✅ 正确:统一使用 <script setup lang="ts"> |
| | | defineOptions({ name: 'UserList' }); |
| | | defineOptions({ name: "UserList" }); |
| | | |
| | | // Props 定义 |
| | | interface Props { |
| | |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | userName: '', |
| | | userName: "", |
| | | }); |
| | | |
| | | // Emits 定义 |
| | | interface Emits { |
| | | (e: 'update', value: string): void; |
| | | (e: 'delete', id: number): void; |
| | | (e: "update", value: string): void; |
| | | (e: "delete", id: number): void; |
| | | } |
| | | |
| | | const emit = defineEmits<Emits>(); |
| | |
| | | } |
| | | |
| | | // ✅ 正确:使用 import type 导入类型 |
| | | import type { UserInfo } from '#/api/system/user'; |
| | | import type { UserInfo } from "#/api/system/user"; |
| | | |
| | | // ❌ 错误:普通导入类型 |
| | | import { UserInfo } from '#/api/system/user'; |
| | | import { UserInfo } from "#/api/system/user"; |
| | | ``` |
| | | |
| | | ### 1.2 目录结构规范 |
| | |
| | | ```typescript |
| | | // src/api/system/user/index.ts |
| | | |
| | | import { request } from '#/utils/request'; |
| | | import { request } from "#/utils/request"; |
| | | |
| | | export namespace SystemUserApi { |
| | | /** 用户信息 */ |
| | |
| | | |
| | | /** 获取用户分页列表 */ |
| | | export function getUserPage(params: SystemUserApi.PageParams) { |
| | | return request.get<SystemUserApi.PageResult>('/system/user/page', { params }); |
| | | return request.get<SystemUserApi.PageResult>("/system/user/page", { params }); |
| | | } |
| | | |
| | | /** 获取用户详情 */ |
| | |
| | | |
| | | /** 创建用户 */ |
| | | export function createUser(data: SystemUserApi.User) { |
| | | return request.post('/system/user/create', data); |
| | | return request.post("/system/user/create", data); |
| | | } |
| | | |
| | | /** 更新用户 */ |
| | | export function updateUser(data: SystemUserApi.User) { |
| | | return request.put('/system/user/update', data); |
| | | return request.put("/system/user/update", data); |
| | | } |
| | | |
| | | /** 删除用户 */ |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { SystemUserApi } from '#/api/system/user'; |
| | | import type { VxeTableGridOptions } from "#/adapter/vxe-table"; |
| | | import type { SystemUserApi } from "#/api/system/user"; |
| | | |
| | | import { useVbenVxeGrid, TableAction, ACTION_ICON } from '#/adapter/vxe-table'; |
| | | import { getUserPage } from '#/api/system/user'; |
| | | import { useVbenVxeGrid, TableAction, ACTION_ICON } from "#/adapter/vxe-table"; |
| | | import { getUserPage } from "#/api/system/user"; |
| | | |
| | | // 表格列配置 |
| | | const columns: VxeTableGridOptions['columns'] = [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { field: 'username', title: '用户名', width: 120 }, |
| | | { field: 'nickname', title: '昵称', width: 120 }, |
| | | const columns: VxeTableGridOptions["columns"] = [ |
| | | { type: "checkbox", width: 50 }, |
| | | { field: "username", title: "用户名", width: 120 }, |
| | | { field: "nickname", title: "昵称", width: 120 }, |
| | | { |
| | | field: 'status', |
| | | title: '状态', |
| | | field: "status", |
| | | title: "状态", |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: 'common_status' }, |
| | | name: "CellDict", |
| | | props: { type: "common_status" }, |
| | | }, |
| | | }, |
| | | { field: 'createTime', title: '创建时间', width: 180 }, |
| | | { field: 'action', title: '操作', width: 200, slots: { default: 'actions' } }, |
| | | { field: "createTime", title: "创建时间", width: 180 }, |
| | | { field: "action", title: "操作", width: 200, slots: { default: "actions" } }, |
| | | ]; |
| | | |
| | | // 表格实例 |
| | |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'username', |
| | | label: '用户名', |
| | | component: 'Input', |
| | | fieldName: "username", |
| | | label: "用户名", |
| | | component: "Input", |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '状态', |
| | | component: 'Select', |
| | | fieldName: "status", |
| | | label: "状态", |
| | | component: "Select", |
| | | componentProps: { |
| | | options: getDictOptions('common_status'), |
| | | options: getDictOptions("common_status"), |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns, |
| | | height: 'auto', |
| | | height: "auto", |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | keyField: "id", |
| | | isHover: true, |
| | | }, |
| | | } as VxeTableGridOptions<SystemUserApi.User>, |
| | |
| | | |
| | | #### 表格列渲染器 |
| | | |
| | | | 渲染器 | 用途 | 示例 | |
| | | |--------|------|------| |
| | | | `CellImage` | 单图展示 | `cellRender: { name: 'CellImage' }` | |
| | | | `CellImages` | 多图展示 | `cellRender: { name: 'CellImages' }` | |
| | | | `CellLink` | 链接按钮 | `cellRender: { name: 'CellLink', props: { text: '查看' } }` | |
| | | | `CellTag` | 标签展示 | `cellRender: { name: 'CellTag', props: { color: 'blue' } }` | |
| | | | `CellDict` | 字典标签 | `cellRender: { name: 'CellDict', props: { type: 'common_status' } }` | |
| | | | `CellSwitch` | 开关切换 | `cellRender: { name: 'CellSwitch', attrs: { beforeChange } }` | |
| | | | 渲染器 | 用途 | 示例 | |
| | | | --------------- | -------- | -------------------------------------------------------------------- | |
| | | | `CellImage` | 单图展示 | `cellRender: { name: 'CellImage' }` | |
| | | | `CellImages` | 多图展示 | `cellRender: { name: 'CellImages' }` | |
| | | | `CellLink` | 链接按钮 | `cellRender: { name: 'CellLink', props: { text: '查看' } }` | |
| | | | `CellTag` | 标签展示 | `cellRender: { name: 'CellTag', props: { color: 'blue' } }` | |
| | | | `CellDict` | 字典标签 | `cellRender: { name: 'CellDict', props: { type: 'common_status' } }` | |
| | | | `CellSwitch` | 开关切换 | `cellRender: { name: 'CellSwitch', attrs: { beforeChange } }` | |
| | | | `CellOperation` | 操作按钮 | `cellRender: { name: 'CellOperation', options: ['edit', 'delete'] }` | |
| | | |
| | | ### 2.2 表单弹窗 (useVbenModal) |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import Form from './modules/form.vue'; |
| | | import { useVbenModal } from "#/packages/effects/common-ui/src"; |
| | | import Form from "./modules/form.vue"; |
| | | |
| | | // 弹窗实例 |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | |
| | | ```vue |
| | | <!-- modules/form.vue --> |
| | | <script lang="ts" setup> |
| | | import type { SystemUserApi } from '#/api/system/user'; |
| | | import type { SystemUserApi } from "#/api/system/user"; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createUser, updateUser } from '#/api/system/user'; |
| | | import { computed, ref } from "vue"; |
| | | import { useVbenModal } from "#/packages/effects/common-ui/src"; |
| | | import { useVbenForm } from "#/adapter/form"; |
| | | import { createUser, updateUser } from "#/api/system/user"; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const emit = defineEmits(["success"]); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | |
| | | } else { |
| | | await createUser(values); |
| | | } |
| | | emit('success'); |
| | | emit("success"); |
| | | modalApi.close(); |
| | | }, |
| | | }); |
| | |
| | | const [Form, formApi] = useVbenForm({ |
| | | schema: [ |
| | | { |
| | | fieldName: 'username', |
| | | label: '用户名', |
| | | rules: 'required', |
| | | component: 'Input', |
| | | fieldName: "username", |
| | | label: "用户名", |
| | | rules: "required", |
| | | component: "Input", |
| | | }, |
| | | { |
| | | fieldName: 'nickname', |
| | | label: '昵称', |
| | | rules: 'required', |
| | | component: 'Input', |
| | | fieldName: "nickname", |
| | | label: "昵称", |
| | | rules: "required", |
| | | component: "Input", |
| | | }, |
| | | { |
| | | fieldName: 'email', |
| | | label: '邮箱', |
| | | rules: 'email', |
| | | component: 'Input', |
| | | fieldName: "email", |
| | | label: "邮箱", |
| | | rules: "email", |
| | | component: "Input", |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: '部门', |
| | | component: 'TreeSelect', |
| | | fieldName: "deptId", |
| | | label: "部门", |
| | | component: "TreeSelect", |
| | | }, |
| | | ], |
| | | }); |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import DictTag from '#/components/dict-tag/dict-tag.vue'; |
| | | import DictTag from "#/components/dict-tag/dict-tag.vue"; |
| | | </script> |
| | | |
| | | <template> |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import DictSelect from '#/components/form-create/components/dict-select.vue'; |
| | | import DictSelect from "#/components/form-create/components/dict-select.vue"; |
| | | </script> |
| | | |
| | | <template> |
| | |
| | | #### 字典工具函数 |
| | | |
| | | ```typescript |
| | | import { getDictOptions, getDictLabel, getDictObj } from '#/packages/effects/hooks/src'; |
| | | import { |
| | | getDictOptions, |
| | | getDictLabel, |
| | | getDictObj, |
| | | } from "#/packages/effects/hooks/src"; |
| | | |
| | | // 获取字典选项列表(用于 Select/Radio 等组件) |
| | | const options = getDictOptions('common_status', 'number'); |
| | | const options = getDictOptions("common_status", "number"); |
| | | |
| | | // 获取字典标签文本 |
| | | const label = getDictLabel('common_status', 1); // "开启" |
| | | const label = getDictLabel("common_status", 1); // "开启" |
| | | |
| | | // 获取字典完整对象 |
| | | const dictObj = getDictObj('common_status', 1); |
| | | const dictObj = getDictObj("common_status", 1); |
| | | // { label: '开启', value: 1, colorType: 'success', cssClass: '' } |
| | | ``` |
| | | |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import { TableAction, ACTION_ICON } from '#/adapter/vxe-table'; |
| | | import { TableAction, ACTION_ICON } from "#/adapter/vxe-table"; |
| | | </script> |
| | | |
| | | <template> |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import ImageUpload from '#/components/upload/image-upload.vue'; |
| | | import ImageUpload from "#/components/upload/image-upload.vue"; |
| | | |
| | | const imageUrl = ref(''); |
| | | const imageUrl = ref(""); |
| | | const imageList = ref<string[]>([]); |
| | | </script> |
| | | |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import FileUpload from '#/components/upload/file-upload.vue'; |
| | | import FileUpload from "#/components/upload/file-upload.vue"; |
| | | |
| | | const fileList = ref<string[]>([]); |
| | | </script> |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import Description from '#/components/description/description.vue'; |
| | | import Description from "#/components/description/description.vue"; |
| | | |
| | | const data = { |
| | | username: 'admin', |
| | | nickname: '管理员', |
| | | email: 'admin@example.com', |
| | | username: "admin", |
| | | nickname: "管理员", |
| | | email: "admin@example.com", |
| | | status: 0, |
| | | createTime: '2024-01-01 12:00:00', |
| | | createTime: "2024-01-01 12:00:00", |
| | | }; |
| | | |
| | | const schema = [ |
| | | { field: 'username', label: '用户名' }, |
| | | { field: 'nickname', label: '昵称' }, |
| | | { field: 'email', label: '邮箱' }, |
| | | { field: "username", label: "用户名" }, |
| | | { field: "nickname", label: "昵称" }, |
| | | { field: "email", label: "邮箱" }, |
| | | { |
| | | field: 'status', |
| | | label: '状态', |
| | | render: (value) => h(DictTag, { type: 'common_status', value }), |
| | | field: "status", |
| | | label: "状态", |
| | | render: (value) => h(DictTag, { type: "common_status", value }), |
| | | }, |
| | | { field: 'createTime', label: '创建时间' }, |
| | | { field: "createTime", label: "创建时间" }, |
| | | ]; |
| | | </script> |
| | | |
| | |
| | | |
| | | #### 常用 Store |
| | | |
| | | | Store | 用途 | 导入路径 | |
| | | |-------|------|----------| |
| | | | `useUserStore` | 用户信息 | `#/packages/stores/src` | |
| | | | Store | 用途 | 导入路径 | |
| | | | ---------------- | ---------- | ----------------------- | |
| | | | `useUserStore` | 用户信息 | `#/packages/stores/src` | |
| | | | `useAccessStore` | 权限/Token | `#/packages/stores/src` | |
| | | | `useDictStore` | 数据字典 | `#/packages/stores/src` | |
| | | | `useDictStore` | 数据字典 | `#/packages/stores/src` | |
| | | |
| | | #### 示例 |
| | | |
| | | ```typescript |
| | | import { useUserStore, useAccessStore } from '#/packages/stores/src'; |
| | | import { useUserStore, useAccessStore } from "#/packages/stores/src"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const accessStore = useAccessStore(); |
| | |
| | | |
| | | // 检查权限 |
| | | const { hasAccessByCodes } = useAccess(); |
| | | const canEdit = hasAccessByCodes(['system:user:update']); |
| | | const canEdit = hasAccessByCodes(["system:user:update"]); |
| | | ``` |
| | | |
| | | --- |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import { useAccess } from '#/packages/effects/access/src'; |
| | | import { useAccess } from "#/packages/effects/access/src"; |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | </script> |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import { $t } from '#/locales'; |
| | | import { $t } from "#/locales"; |
| | | </script> |
| | | |
| | | <template> |
| | | <Button>{{ $t('common.save') }}</Button> |
| | | <span>{{ $t('ui.actionMessage.deleteSuccess', ['用户']) }}</span> |
| | | <Button>{{ $t("common.save") }}</Button> |
| | | <span>{{ $t("ui.actionMessage.deleteSuccess", ["用户"]) }}</span> |
| | | </template> |
| | | ``` |
| | | |
| | |
| | | ### 6.1 usePagination - 分页 |
| | | |
| | | ```typescript |
| | | import { usePagination } from '#/packages/effects/hooks/src'; |
| | | import { usePagination } from "#/packages/effects/hooks/src"; |
| | | |
| | | const { page, pageSize, total, setPage, setTotal } = usePagination(); |
| | | ``` |
| | |
| | | ### 6.2 useTabs - 标签页操作 |
| | | |
| | | ```typescript |
| | | import { useTabs } from '#/packages/effects/hooks/src'; |
| | | import { useTabs } from "#/packages/effects/hooks/src"; |
| | | |
| | | const { closeOtherTabs, closeTab, refreshTab } = useTabs(); |
| | | |
| | |
| | | ### 6.3 useWatermark - 水印 |
| | | |
| | | ```typescript |
| | | import { useWatermark } from '#/packages/effects/hooks/src'; |
| | | import { useWatermark } from "#/packages/effects/hooks/src"; |
| | | |
| | | const { updateWatermark, destroyWatermark } = useWatermark(); |
| | | |
| | | // 设置水印 |
| | | await updateWatermark({ |
| | | content: '用户名', |
| | | content: "用户名", |
| | | }); |
| | | |
| | | // 销毁水印 |
| | |
| | | |
| | | ```typescript |
| | | import { |
| | | formatDateTime, // 日期格式化 |
| | | formatDate, // 日期格式化(不含时间) |
| | | formatDateTime, // 日期格式化 |
| | | formatDate, // 日期格式化(不含时间) |
| | | downloadFileFromBlobPart, // 文件下载 |
| | | isEmpty, // 判空 |
| | | isFunction, // 判断函数 |
| | | isString, // 判断字符串 |
| | | } from '#/packages/utils/src'; |
| | | isEmpty, // 判空 |
| | | isFunction, // 判断函数 |
| | | isString, // 判断字符串 |
| | | } from "#/packages/utils/src"; |
| | | |
| | | // 日期格式化 |
| | | formatDateTime('2024-01-01T12:00:00'); // "2024-01-01 12:00:00" |
| | | formatDateTime("2024-01-01T12:00:00"); // "2024-01-01 12:00:00" |
| | | |
| | | // 文件下载 |
| | | downloadFileFromBlobPart({ |
| | | fileName: '用户列表.xlsx', |
| | | fileName: "用户列表.xlsx", |
| | | source: blobData, |
| | | }); |
| | | |
| | |
| | | |
| | | ```typescript |
| | | import { |
| | | erpNumberFormatter, // 数字格式化 |
| | | fenToYuan, // 分转元 |
| | | formatFileSize, // 文件大小格式化 |
| | | } from '#/packages/utils/src'; |
| | | erpNumberFormatter, // 数字格式化 |
| | | fenToYuan, // 分转元 |
| | | formatFileSize, // 文件大小格式化 |
| | | } from "#/packages/utils/src"; |
| | | |
| | | // 保留两位小数 |
| | | erpNumberFormatter(1234.567, 2); // "1,234.57" |
| | |
| | | |
| | | ```vue |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { SystemUserApi } from '#/api/system/user'; |
| | | import type { VxeTableGridOptions } from "#/adapter/vxe-table"; |
| | | import type { SystemUserApi } from "#/api/system/user"; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { ref } from "vue"; |
| | | |
| | | import { confirm, Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '#/packages/utils/src'; |
| | | import { confirm, Page, useVbenModal } from "#/packages/effects/common-ui/src"; |
| | | import { getDictOptions } from "#/packages/effects/hooks/src"; |
| | | import { downloadFileFromBlobPart, isEmpty } from "#/packages/utils/src"; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | import { message } from "ant-design-vue"; |
| | | |
| | | import { useVbenVxeGrid, TableAction, ACTION_ICON } from '#/adapter/vxe-table'; |
| | | import { useVbenVxeGrid, TableAction, ACTION_ICON } from "#/adapter/vxe-table"; |
| | | import { |
| | | getUserPage, |
| | | deleteUser, |
| | | createUser, |
| | | updateUser, |
| | | exportUser, |
| | | } from '#/api/system/user'; |
| | | import { $t } from '#/locales'; |
| | | } from "#/api/system/user"; |
| | | import { $t } from "#/locales"; |
| | | |
| | | import Form from './modules/form.vue'; |
| | | import Form from "./modules/form.vue"; |
| | | |
| | | // ========== 弹窗定义 ========== |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'username', |
| | | label: '用户名', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请输入用户名' }, |
| | | fieldName: "username", |
| | | label: "用户名", |
| | | component: "Input", |
| | | componentProps: { placeholder: "请输入用户名" }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '状态', |
| | | component: 'Select', |
| | | fieldName: "status", |
| | | label: "状态", |
| | | component: "Select", |
| | | componentProps: { |
| | | options: getDictOptions('common_status', 'number'), |
| | | placeholder: '请选择状态', |
| | | options: getDictOptions("common_status", "number"), |
| | | placeholder: "请选择状态", |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns: [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { field: 'username', title: '用户名', width: 120 }, |
| | | { field: 'nickname', title: '昵称', width: 120 }, |
| | | { type: "checkbox", width: 50 }, |
| | | { field: "username", title: "用户名", width: 120 }, |
| | | { field: "nickname", title: "昵称", width: 120 }, |
| | | { |
| | | field: 'status', |
| | | title: '状态', |
| | | field: "status", |
| | | title: "状态", |
| | | width: 100, |
| | | cellRender: { name: 'CellDict', props: { type: 'common_status' } }, |
| | | cellRender: { name: "CellDict", props: { type: "common_status" } }, |
| | | }, |
| | | { field: 'createTime', title: '创建时间', width: 180 }, |
| | | { field: 'action', title: '操作', width: 180, slots: { default: 'actions' } }, |
| | | { field: "createTime", title: "创建时间", width: 180 }, |
| | | { |
| | | field: "action", |
| | | title: "操作", |
| | | width: 180, |
| | | slots: { default: "actions" }, |
| | | }, |
| | | ], |
| | | height: 'auto', |
| | | height: "auto", |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | rowConfig: { keyField: "id", isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<SystemUserApi.User>, |
| | | gridEvents: { |
| | |
| | | |
| | | async function handleExport() { |
| | | const data = await exportUser(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: '用户.xls', source: data }); |
| | | downloadFileFromBlobPart({ fileName: "用户.xls", source: data }); |
| | | } |
| | | |
| | | function handleCreate() { |
| | |
| | | |
| | | async function handleDelete(row: SystemUserApi.User) { |
| | | await deleteUser(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.username])); |
| | | message.success($t("ui.actionMessage.deleteSuccess", [row.username])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | async function handleDeleteBatch() { |
| | | await confirm($t('ui.actionMessage.deleteBatchConfirm')); |
| | | await confirm($t("ui.actionMessage.deleteBatchConfirm")); |
| | | // ... 批量删除逻辑 |
| | | } |
| | | </script> |