diff --git a/apps/web-tdesign/src/components/cron-tab/cron-tab.vue b/apps/web-tdesign/src/components/cron-tab/cron-tab.vue new file mode 100644 index 000000000..d6dfaef44 --- /dev/null +++ b/apps/web-tdesign/src/components/cron-tab/cron-tab.vue @@ -0,0 +1,966 @@ + + + + + + + 每分钟 + 每小时 + 每天零点 + 每月一号零点 + 每月最后一天零点 + 每周星期日零点 + + {{ item.text }} + + 自定义 + + + + + + + + + + + 秒 + {{ value_second }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + + + + + - + + + + + 秒开始,每 + + 秒执行一次 + + + + + + + + + + + + + 分钟 + {{ value_minute }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + + + + + - + + + + + 分钟开始,每 + + 分钟执行一次 + + + + + + + + + + + + + 小时 + {{ value_hour }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + + + + + - + + + + + 小时开始,每 + + 小时执行一次 + + + + + + + + + + + + + 日 + {{ value_day }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + 本月最后一天 + 不指定 + + + + + - + + + + + 号开始,每 + + 天执行一次 + + + + + + + + + + + + + 月 + {{ value_month }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + + + + + - + + + + + 月开始,每 + + 月执行一次 + + + + + + + + + + + + + 周 + {{ value_week }} + + + + + + 任意值 + 范围 + 间隔 + 指定 + 本月最后一周 + 不指定 + + + + + + + - + + + + + + 第 + + 周的星期 + + + + 执行一次 + + + + + + + + + + + + + + + + + + 年 + {{ value_year }} + + + + + + 忽略 + 任意值 + 范围 + 间隔 + 指定 + + + + + - + + + + + 年开始,每 + + 年执行一次 + + + + + + + + + + + + + 取 消 + 确 认 + + + + + diff --git a/apps/web-tdesign/src/components/cron-tab/index.ts b/apps/web-tdesign/src/components/cron-tab/index.ts new file mode 100644 index 000000000..8f4baae59 --- /dev/null +++ b/apps/web-tdesign/src/components/cron-tab/index.ts @@ -0,0 +1 @@ +export { default as CronTab } from './cron-tab.vue'; diff --git a/apps/web-tdesign/src/components/cron-tab/types.ts b/apps/web-tdesign/src/components/cron-tab/types.ts new file mode 100644 index 000000000..2adf942b7 --- /dev/null +++ b/apps/web-tdesign/src/components/cron-tab/types.ts @@ -0,0 +1,266 @@ +export interface ShortcutsType { + text: string; + value: string; +} + +export interface CronRange { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronLoop { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronItem { + type: string; + range: CronRange; + loop: CronLoop; + appoint: string[]; + last?: string; +} + +export interface CronValue { + second: CronItem; + minute: CronItem; + hour: CronItem; + day: CronItem; + month: CronItem; + week: CronItem & { last: string }; + year: CronItem; +} + +export interface WeekOption { + value: string; + label: string; +} + +export interface CronData { + second: string[]; + minute: string[]; + hour: string[]; + day: string[]; + month: string[]; + week: WeekOption[]; + year: number[]; +} + +const getYear = (): number[] => { + const v: number[] = []; + const y = new Date().getFullYear(); + for (let i = 0; i < 11; i++) { + v.push(y + i); + } + return v; +}; + +export const CronValueDefault: CronValue = { + second: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + minute: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + hour: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + day: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + month: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + week: { + type: '5', + range: { + start: '2', + end: '3', + }, + loop: { + start: 0, + end: '2', + }, + last: '2', + appoint: [], + }, + year: { + type: '-1', + range: { + start: getYear()[0], + end: getYear()[1], + }, + loop: { + start: getYear()[0], + end: 1, + }, + appoint: [], + }, +}; + +export const CronDataDefault: CronData = { + second: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + minute: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + hour: [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + ], + day: [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '30', + '31', + ], + month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], + week: [ + { + value: '1', + label: '周日', + }, + { + value: '2', + label: '周一', + }, + { + value: '3', + label: '周二', + }, + { + value: '4', + label: '周三', + }, + { + value: '5', + label: '周四', + }, + { + value: '6', + label: '周五', + }, + { + value: '7', + label: '周六', + }, + ], + year: getYear(), +}; diff --git a/apps/web-tdesign/src/components/cropper/cropper-avatar.vue b/apps/web-tdesign/src/components/cropper/cropper-avatar.vue new file mode 100644 index 000000000..5acb23fdc --- /dev/null +++ b/apps/web-tdesign/src/components/cropper/cropper-avatar.vue @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + {{ btnText ? btnText : $t('ui.cropper.selectImage') }} + + + + + diff --git a/apps/web-tdesign/src/components/cropper/cropper-modal.vue b/apps/web-tdesign/src/components/cropper/cropper-modal.vue new file mode 100644 index 000000000..35e827a23 --- /dev/null +++ b/apps/web-tdesign/src/components/cropper/cropper-modal.vue @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/components/cropper/cropper.vue b/apps/web-tdesign/src/components/cropper/cropper.vue new file mode 100644 index 000000000..cc8836c9f --- /dev/null +++ b/apps/web-tdesign/src/components/cropper/cropper.vue @@ -0,0 +1,171 @@ + + + + + + + + + diff --git a/apps/web-tdesign/src/components/cropper/index.ts b/apps/web-tdesign/src/components/cropper/index.ts new file mode 100644 index 000000000..43fd89ff3 --- /dev/null +++ b/apps/web-tdesign/src/components/cropper/index.ts @@ -0,0 +1,3 @@ +export { default as CropperAvatar } from './cropper-avatar.vue'; +export { default as CropperImage } from './cropper.vue'; +export type { CropperType } from './typing'; diff --git a/apps/web-tdesign/src/components/cropper/typing.ts b/apps/web-tdesign/src/components/cropper/typing.ts new file mode 100644 index 000000000..8293079b3 --- /dev/null +++ b/apps/web-tdesign/src/components/cropper/typing.ts @@ -0,0 +1,68 @@ +import type Cropper from 'cropperjs'; +import type { ButtonProps } from 'tdesign-vue-next'; + +import type { CSSProperties } from 'vue'; + +export interface apiFunParams { + file: Blob; + filename: string; + name: string; +} + +export interface CropendResult { + imgBase64: string; + imgInfo: Cropper.Data; +} + +export interface CropperProps { + src?: string; + alt?: string; + circled?: boolean; + realTimePreview?: boolean; + height?: number | string; + crossorigin?: '' | 'anonymous' | 'use-credentials' | undefined; + imageStyle?: CSSProperties; + options?: Cropper.Options; +} + +export interface CropperAvatarProps { + width?: number | string; + value?: string; + showBtn?: boolean; + btnProps?: ButtonProps; + btnText?: string; + uploadApi?: (params: apiFunParams) => Promise; + size?: number; +} + +export interface CropperModalProps { + circled?: boolean; + uploadApi?: (params: apiFunParams) => Promise; + src?: string; + size?: number; +} + +export const defaultOptions: Cropper.Options = { + aspectRatio: 1, + zoomable: true, + zoomOnTouch: true, + zoomOnWheel: true, + cropBoxMovable: true, + cropBoxResizable: true, + toggleDragModeOnDblclick: true, + autoCrop: true, + background: true, + highlight: true, + center: true, + responsive: true, + restore: true, + checkCrossOrigin: true, + checkOrientation: true, + scalable: true, + modal: true, + guides: true, + movable: true, + rotatable: true, +}; + +export type { Cropper as CropperType }; diff --git a/apps/web-tdesign/src/components/description/description.vue b/apps/web-tdesign/src/components/description/description.vue new file mode 100644 index 000000000..5cfc75d65 --- /dev/null +++ b/apps/web-tdesign/src/components/description/description.vue @@ -0,0 +1,195 @@ + diff --git a/apps/web-tdesign/src/components/description/index.ts b/apps/web-tdesign/src/components/description/index.ts new file mode 100644 index 000000000..a707c4865 --- /dev/null +++ b/apps/web-tdesign/src/components/description/index.ts @@ -0,0 +1,3 @@ +export { default as Description } from './description.vue'; +export * from './typing'; +export { useDescription } from './use-description'; diff --git a/apps/web-tdesign/src/components/description/typing.ts b/apps/web-tdesign/src/components/description/typing.ts new file mode 100644 index 000000000..d73a067c5 --- /dev/null +++ b/apps/web-tdesign/src/components/description/typing.ts @@ -0,0 +1,45 @@ +import type { DescriptionsProps as TdDescriptionsProps } from 'tdesign-vue-next'; +import type { JSX } from 'vue/jsx-runtime'; + +import type { CSSProperties, VNode } from 'vue'; + +import type { Recordable } from '@vben/types'; + +export interface DescriptionItemSchema { + labelMinWidth?: number; + contentMinWidth?: number; + // 自定义标签样式 + labelStyle?: CSSProperties; + // 对应 data 中的字段名 + field: string; + // 内容的描述 + label: JSX.Element | string | VNode; + // 包含列的数量 + span?: number; + // 是否显示 + show?: (...arg: any) => boolean; + // 插槽名称 + slot?: string; + // 自定义需要展示的内容 + render?: ( + val: any, + data?: Recordable, + ) => Element | JSX.Element | number | string | undefined | VNode; +} + +export interface DescriptionProps extends TdDescriptionsProps { + // 是否包含卡片组件 + useCard?: boolean; + // 描述项配置 + schema: DescriptionItemSchema[]; + // 数据 + data: Recordable; + // 标题 + title?: string; + // 是否包含边框 + bordered?: boolean; +} + +export interface DescInstance { + setDescProps(descProps: Partial): void; +} diff --git a/apps/web-tdesign/src/components/description/use-description.ts b/apps/web-tdesign/src/components/description/use-description.ts new file mode 100644 index 000000000..fd24920f0 --- /dev/null +++ b/apps/web-tdesign/src/components/description/use-description.ts @@ -0,0 +1,31 @@ +import type { Component } from 'vue'; + +import type { DescInstance, DescriptionProps } from './typing'; + +import { h, reactive } from 'vue'; + +import Description from './description.vue'; + +export function useDescription(options?: Partial) { + const propsState = reactive>(options || {}); + + const api: DescInstance = { + setDescProps: (descProps: Partial): void => { + Object.assign(propsState, descProps); + }, + }; + + // 创建一个包装组件,将 propsState 合并到 props 中 + const DescriptionWrapper: Component = { + name: 'UseDescription', + inheritAttrs: false, + setup(_props, { attrs, slots }) { + return () => { + // @ts-ignore - 避免类型实例化过深 + return h(Description, { ...propsState, ...attrs }, slots); + }; + }, + }; + + return [DescriptionWrapper, api] as const; +} diff --git a/apps/web-tdesign/src/components/dict-tag/dict-tag.vue b/apps/web-tdesign/src/components/dict-tag/dict-tag.vue new file mode 100644 index 000000000..c40ed80e4 --- /dev/null +++ b/apps/web-tdesign/src/components/dict-tag/dict-tag.vue @@ -0,0 +1,82 @@ + + + + + {{ dictTag.label }} + + diff --git a/apps/web-tdesign/src/components/dict-tag/index.ts b/apps/web-tdesign/src/components/dict-tag/index.ts new file mode 100644 index 000000000..881265a39 --- /dev/null +++ b/apps/web-tdesign/src/components/dict-tag/index.ts @@ -0,0 +1 @@ +export { default as DictTag } from './dict-tag.vue'; diff --git a/apps/web-tdesign/src/components/table-action/icons.ts b/apps/web-tdesign/src/components/table-action/icons.ts new file mode 100644 index 000000000..df5bf1a77 --- /dev/null +++ b/apps/web-tdesign/src/components/table-action/icons.ts @@ -0,0 +1,14 @@ +export const ACTION_ICON = { + DOWNLOAD: 'lucide:download', + UPLOAD: 'lucide:upload', + ADD: 'lucide:plus', + EDIT: 'lucide:edit', + DELETE: 'lucide:trash-2', + REFRESH: 'lucide:refresh-cw', + SEARCH: 'lucide:search', + FILTER: 'lucide:filter', + MORE: 'lucide:ellipsis-vertical', + VIEW: 'lucide:eye', + COPY: 'lucide:copy', + CLOSE: 'lucide:x', +}; diff --git a/apps/web-tdesign/src/components/table-action/index.ts b/apps/web-tdesign/src/components/table-action/index.ts new file mode 100644 index 000000000..672c0a533 --- /dev/null +++ b/apps/web-tdesign/src/components/table-action/index.ts @@ -0,0 +1,4 @@ +export * from './icons'; + +export { default as TableAction } from './table-action.vue'; +export * from './typing'; diff --git a/apps/web-tdesign/src/components/table-action/table-action.vue b/apps/web-tdesign/src/components/table-action/table-action.vue new file mode 100644 index 000000000..8c4fda41a --- /dev/null +++ b/apps/web-tdesign/src/components/table-action/table-action.vue @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + {{ action.label }} + + + + + + + + + {{ action.label }} + + + + + + + + + + {{ $t('page.action.more') }} + + + + + + + + + + + + + + + + {{ action.label }} + + + + + + + + {{ action.label }} + + + + + + + + + + diff --git a/apps/web-tdesign/src/components/table-action/typing.ts b/apps/web-tdesign/src/components/table-action/typing.ts new file mode 100644 index 000000000..a54e58468 --- /dev/null +++ b/apps/web-tdesign/src/components/table-action/typing.ts @@ -0,0 +1,32 @@ +import type { TdButtonProps, TooltipProps } from 'tdesign-vue-next'; + +export interface PopConfirm { + title: string; + okText?: string; + cancelText?: string; + confirm: () => void; + cancel?: () => void; + icon?: string; + disabled?: boolean; +} + +export interface ActionItem { + onClick?: () => void; + type?: TdButtonProps['theme']; + label?: string; + icon?: string; + color?: 'error' | 'success' | 'warning'; + popConfirm?: PopConfirm; + disabled?: boolean; + divider?: boolean; + // 权限编码控制是否显示 + auth?: string[]; + // 业务控制是否显示 + ifShow?: ((action: ActionItem) => boolean) | boolean; + tooltip?: string | TooltipProps; + loading?: boolean; + size?: TdButtonProps['size']; + shape?: TdButtonProps['shape']; + variant?: TdButtonProps['variant']; + danger?: boolean; +} diff --git a/apps/web-tdesign/src/components/tinymce/editor.vue b/apps/web-tdesign/src/components/tinymce/editor.vue new file mode 100644 index 000000000..722073cf7 --- /dev/null +++ b/apps/web-tdesign/src/components/tinymce/editor.vue @@ -0,0 +1,344 @@ + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/components/tinymce/helper.ts b/apps/web-tdesign/src/components/tinymce/helper.ts new file mode 100644 index 000000000..1f98dda46 --- /dev/null +++ b/apps/web-tdesign/src/components/tinymce/helper.ts @@ -0,0 +1,85 @@ +const validEvents = new Set([ + 'onActivate', + 'onAddUndo', + 'onBeforeAddUndo', + 'onBeforeExecCommand', + 'onBeforeGetContent', + 'onBeforePaste', + 'onBeforeRenderUI', + 'onBeforeSetContent', + 'onBlur', + 'onChange', + 'onClearUndos', + 'onClick', + 'onContextMenu', + 'onCopy', + 'onCut', + 'onDblclick', + 'onDeactivate', + 'onDirty', + 'onDrag', + 'onDragDrop', + 'onDragEnd', + 'onDragGesture', + 'onDragOver', + 'onDrop', + 'onExecCommand', + 'onFocus', + 'onFocusIn', + 'onFocusOut', + 'onGetContent', + 'onHide', + 'onInit', + 'onKeyDown', + 'onKeyPress', + 'onKeyUp', + 'onLoadContent', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onNodeChange', + 'onObjectResized', + 'onObjectResizeStart', + 'onObjectSelected', + 'onPaste', + 'onPostProcess', + 'onPostRender', + 'onPreProcess', + 'onProgressState', + 'onRedo', + 'onRemove', + 'onReset', + 'onSaveContent', + 'onSelectionChange', + 'onSetAttrib', + 'onSetContent', + 'onShow', + 'onSubmit', + 'onUndo', + 'onVisualAid', +]); + +const isValidKey = (key: string) => validEvents.has(key); + +export const bindHandlers = ( + initEvent: Event, + listeners: any, + editor: any, +): void => { + Object.keys(listeners) + .filter((element) => isValidKey(element)) + .forEach((key: string) => { + const handler = listeners[key]; + if (typeof handler === 'function') { + if (key === 'onInit') { + handler(initEvent, editor); + } else { + editor.on(key.slice(2), (e: any) => handler(e, editor)); + } + } + }); +}; diff --git a/apps/web-tdesign/src/components/tinymce/img-upload.vue b/apps/web-tdesign/src/components/tinymce/img-upload.vue new file mode 100644 index 000000000..ee300d1ae --- /dev/null +++ b/apps/web-tdesign/src/components/tinymce/img-upload.vue @@ -0,0 +1,111 @@ + + + + + + {{ $t('ui.upload.imgUpload') }} + + + + + + diff --git a/apps/web-tdesign/src/components/tinymce/index.ts b/apps/web-tdesign/src/components/tinymce/index.ts new file mode 100644 index 000000000..c277d781d --- /dev/null +++ b/apps/web-tdesign/src/components/tinymce/index.ts @@ -0,0 +1 @@ +export { default as Tinymce } from './editor.vue'; diff --git a/apps/web-tdesign/src/components/tinymce/tinymce.ts b/apps/web-tdesign/src/components/tinymce/tinymce.ts new file mode 100644 index 000000000..45a867b61 --- /dev/null +++ b/apps/web-tdesign/src/components/tinymce/tinymce.ts @@ -0,0 +1,17 @@ +// Any plugins you want to setting has to be imported +// Detail plugins list see https://www.tiny.cloud/docs/plugins/ +// Custom builds see https://www.tiny.cloud/download/custom-builds/ +// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration + +export const plugins = + 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help emoticons accordion'; + +// 和 vben2.0 不同,从 https://www.tiny.cloud/ 拷贝 Vue 部分,然后去掉 importword exportword exportpdf | math 部分,并额外增加最后一行(来自 vben2.0 差异的部分) +export const toolbar = + 'undo redo | accordion accordionremove | \\\n' + + ' blocks fontfamily fontsize | bold italic underline strikethrough | \\\n' + + ' align numlist bullist | link image | table media | \\\n' + + ' lineheight outdent indent | forecolor backcolor removeformat | \\\n' + + ' charmap emoticons | code fullscreen preview | save print | \\\n' + + ' pagebreak anchor codesample | ltr rtl | \\\n' + + ' hr searchreplace alignleft aligncenter alignright blockquote subscript superscript'; diff --git a/apps/web-tdesign/src/components/upload/file-upload.vue b/apps/web-tdesign/src/components/upload/file-upload.vue new file mode 100644 index 000000000..668197711 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/file-upload.vue @@ -0,0 +1,381 @@ + + + + + + + + + + 点击或拖拽文件到此区域上传 + + 支持{{ accept.join('/') }}格式文件,不超过{{ maxSize }}MB + + + + + + {{ $t('ui.upload.upload') }} + + + + 请上传不超过 + {{ maxSize }}MB + 的 + {{ accept.join('/') }} + 格式文件 + + + + + + diff --git a/apps/web-tdesign/src/components/upload/helper.ts b/apps/web-tdesign/src/components/upload/helper.ts new file mode 100644 index 000000000..27313cea6 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/helper.ts @@ -0,0 +1,20 @@ +/** + * 默认图片类型 + */ +export const defaultImageAccepts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + +export function checkFileType(file: File, accepts: string[]) { + if (!accepts || accepts.length === 0) { + return true; + } + const newTypes = accepts.join('|'); + const reg = new RegExp(`${String.raw`\.(` + newTypes})$`, 'i'); + return reg.test(file.name); +} + +export function checkImgType( + file: File, + accepts: string[] = defaultImageAccepts, +) { + return checkFileType(file, accepts); +} diff --git a/apps/web-tdesign/src/components/upload/image-upload.vue b/apps/web-tdesign/src/components/upload/image-upload.vue new file mode 100644 index 000000000..14e3776c4 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/image-upload.vue @@ -0,0 +1,362 @@ + + + + + + + + {{ $t('ui.upload.imgUpload') }} + + + + 请上传不超过 + {{ maxSize }}MB + 的 + {{ accept.join('/') }} + 格式文件 + + + + + + + + diff --git a/apps/web-tdesign/src/components/upload/index.ts b/apps/web-tdesign/src/components/upload/index.ts new file mode 100644 index 000000000..14e57fede --- /dev/null +++ b/apps/web-tdesign/src/components/upload/index.ts @@ -0,0 +1,3 @@ +export { default as FileUpload } from './file-upload.vue'; +export { default as ImageUpload } from './image-upload.vue'; +export { default as InputUpload } from './input-upload.vue'; diff --git a/apps/web-tdesign/src/components/upload/input-upload.vue b/apps/web-tdesign/src/components/upload/input-upload.vue new file mode 100644 index 000000000..353e3c5d4 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/input-upload.vue @@ -0,0 +1,74 @@ + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/components/upload/typing.ts b/apps/web-tdesign/src/components/upload/typing.ts new file mode 100644 index 000000000..2488c4080 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/typing.ts @@ -0,0 +1,39 @@ +import type { AxiosResponse } from '@vben/request'; + +import type { AxiosProgressEvent } from '#/api/infra/file'; + +export enum UploadResultStatus { + ERROR = 'error', + PROGRESS = 'progress', + SUCCESS = 'success', + WAITING = 'waiting', +} + +export type UploadListType = 'picture' | 'picture-card' | 'text'; + +export interface FileUploadProps { + // 根据后缀,或者其他 + accept?: string[]; + api?: ( + file: File, + onUploadProgress?: AxiosProgressEvent, + ) => Promise>; + // 上传的目录 + directory?: string; + disabled?: boolean; + drag?: boolean; // 是否支持拖拽上传 + helpText?: string; + listType?: UploadListType; + // 最大数量的文件,Infinity不限制 + maxNumber?: number; + modelValue?: string | string[]; // v-model 支持 + // 文件最大多少MB + maxSize?: number; + // 是否支持多选 + multiple?: boolean; + // support xxx.xxx.xx + resultField?: string; + // 是否显示下面的描述 + showDescription?: boolean; + value?: string | string[]; +} diff --git a/apps/web-tdesign/src/components/upload/use-upload.ts b/apps/web-tdesign/src/components/upload/use-upload.ts new file mode 100644 index 000000000..2f46f9e98 --- /dev/null +++ b/apps/web-tdesign/src/components/upload/use-upload.ts @@ -0,0 +1,168 @@ +import type { Ref } from 'vue'; + +import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file'; + +import { computed, unref } from 'vue'; + +import { useAppConfig } from '@vben/hooks'; +import { $t } from '@vben/locales'; + +import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file'; +import { baseRequestClient } from '#/api/request'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +/** + * 上传类型 + */ +enum UPLOAD_TYPE { + // 客户端直接上传(只支持S3服务) + CLIENT = 'client', + // 客户端发送到后端上传 + SERVER = 'server', +} + +export function useUploadType({ + acceptRef, + helpTextRef, + maxNumberRef, + maxSizeRef, +}: { + acceptRef: Ref; + helpTextRef: Ref; + maxNumberRef: Ref; + maxSizeRef: Ref; +}) { + // 文件类型限制 + const getAccept = computed(() => { + const accept = unref(acceptRef); + if (accept && accept.length > 0) { + return accept; + } + return []; + }); + const getStringAccept = computed(() => { + return unref(getAccept) + .map((item) => { + return item.indexOf('/') > 0 || item.startsWith('.') + ? item + : `.${item}`; + }) + .join(','); + }); + + // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 + const getHelpText = computed(() => { + const helpText = unref(helpTextRef); + if (helpText) { + return helpText; + } + const helpTexts: string[] = []; + + const accept = unref(acceptRef); + if (accept.length > 0) { + helpTexts.push($t('ui.upload.accept', [accept.join(',')])); + } + + const maxSize = unref(maxSizeRef); + if (maxSize) { + helpTexts.push($t('ui.upload.maxSize', [maxSize])); + } + + const maxNumber = unref(maxNumberRef); + if (maxNumber && maxNumber !== Infinity) { + helpTexts.push($t('ui.upload.maxNumber', [maxNumber])); + } + return helpTexts.join(','); + }); + return { getAccept, getStringAccept, getHelpText }; +} + +// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构 +export function useUpload(directory?: string) { + // 后端上传地址 + const uploadUrl = getUploadUrl(); + // 是否使用前端直连上传 + const isClientUpload = + UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE; + // 重写ElUpload上传方法 + async function httpRequest( + file: File, + onUploadProgress?: AxiosProgressEvent, + ) { + // 模式一:前端上传 + if (isClientUpload) { + // 1.1 生成文件名称 + const fileName = await generateFileName(file); + // 1.2 获取文件预签名地址 + const presignedInfo = await getFilePresignedUrl(fileName, directory); + // 1.3 上传文件 + return baseRequestClient + .put(presignedInfo.uploadUrl, file, { + headers: { + 'Content-Type': file.type, + }, + }) + .then(() => { + // 1.4. 记录文件信息到后端(异步) + createFile0(presignedInfo, file); + // 通知成功,数据格式保持与后端上传的返回结果一致 + return { url: presignedInfo.url }; + }); + } else { + // 模式二:后端上传 + return uploadFile({ file, directory }, onUploadProgress); + } + } + + return { + uploadUrl, + httpRequest, + }; +} + +/** + * 获得上传 URL + */ +export function getUploadUrl(): string { + return `${apiURL}/infra/file/upload`; +} + +/** + * 创建文件信息 + * + * @param vo 文件预签名信息 + * @param file 文件 + */ +function createFile0( + vo: InfraFileApi.FilePresignedUrlRespVO, + file: File, +): InfraFileApi.File { + const fileVO = { + configId: vo.configId, + url: vo.url, + path: vo.path, + name: file.name, + type: file.type, + size: file.size, + }; + createFile(fileVO); + return fileVO; +} + +/** + * 生成文件名称(使用算法SHA256) + * + * @param file 要上传的文件 + */ +async function generateFileName(file: File) { + // // 读取文件内容 + // const data = await file.arrayBuffer(); + // const wordArray = CryptoJS.lib.WordArray.create(data); + // // 计算SHA256 + // const sha256 = CryptoJS.SHA256(wordArray).toString(); + // // 拼接后缀 + // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.'))); + // return `${sha256}${ext}`; + return file.name; +} diff --git a/apps/web-tdesign/src/locales/langs/en-US/utils.json b/apps/web-tdesign/src/locales/langs/en-US/utils.json new file mode 100644 index 000000000..b9206eff9 --- /dev/null +++ b/apps/web-tdesign/src/locales/langs/en-US/utils.json @@ -0,0 +1,14 @@ +{ + "rangePicker": { + "today": "Today", + "last7Days": "Last 7 Days", + "last30Days": "Last 30 Days", + "yesterday": "Yesterday", + "thisWeek": "This Week", + "thisMonth": "This Month", + "lastWeek": "Last Week", + "lastMonth": "Last Month", + "beginTime": "Begin Time", + "endTime": "End Time" + } +} diff --git a/apps/web-tdesign/src/locales/langs/zh-CN/utils.json b/apps/web-tdesign/src/locales/langs/zh-CN/utils.json new file mode 100644 index 000000000..d26f1f2cf --- /dev/null +++ b/apps/web-tdesign/src/locales/langs/zh-CN/utils.json @@ -0,0 +1,14 @@ +{ + "rangePicker": { + "today": "今天", + "last7Days": "最近 7 天", + "last30Days": "最近 30 天", + "yesterday": "昨天", + "thisWeek": "本周", + "thisMonth": "本月", + "lastWeek": "上周", + "lastMonth": "上月", + "beginTime": "开始时间", + "endTime": "结束时间" + } +} diff --git a/apps/web-tdesign/src/views/infra/apiAccessLog/data.ts b/apps/web-tdesign/src/views/infra/apiAccessLog/data.ts new file mode 100644 index 000000000..83b2977c0 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiAccessLog/data.ts @@ -0,0 +1,273 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + allowClear: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'beginTime', + label: '请求时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'duration', + label: '执行时长', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入执行时长', + }, + }, + { + fieldName: 'resultCode', + label: '结果码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入结果码', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 300, + }, + { + field: 'beginTime', + title: '请求时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'duration', + title: '执行时长', + minWidth: 120, + formatter: ({ cellValue }) => `${cellValue} ms`, + }, + { + field: 'resultCode', + title: '操作结果', + minWidth: 150, + formatter: ({ row }) => { + return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`; + }, + }, + { + field: 'operateModule', + title: '操作模块', + minWidth: 150, + }, + { + field: 'operateName', + title: '操作名', + minWidth: 220, + }, + { + field: 'operateType', + title: '操作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_OPERATE_TYPE }, + }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'responseBody', + label: '请求结果', + }, + { + label: '请求时间', + field: 'beginTime', + render: (val, data) => { + if (val && data?.endTime) { + return `${formatDateTime(val)} ~ ${formatDateTime(data.endTime)}`; + } + return ''; + }, + }, + { + label: '请求耗时', + field: 'duration', + render: (val) => { + return val ? `${val} ms` : ''; + }, + }, + { + label: '操作结果', + field: 'resultCode', + render: (val, data) => { + if (val === 0) { + return '正常'; + } else if (val > 0 && data?.resultMsg) { + return `失败 | ${val} | ${data.resultMsg}`; + } + return ''; + }, + }, + { + field: 'operateModule', + label: '操作模块', + }, + { + field: 'operateName', + label: '操作名', + }, + { + field: 'operateType', + label: '操作类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_OPERATE_TYPE, + value: val, + }); + }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/apiAccessLog/index.vue b/apps/web-tdesign/src/views/infra/apiAccessLog/index.vue new file mode 100644 index 000000000..251825e1e --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiAccessLog/index.vue @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/apiAccessLog/modules/detail.vue b/apps/web-tdesign/src/views/infra/apiAccessLog/modules/detail.vue new file mode 100644 index 000000000..badc9376c --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiAccessLog/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/apiErrorLog/data.ts b/apps/web-tdesign/src/views/infra/apiErrorLog/data.ts new file mode 100644 index 000000000..1a8c545fe --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiErrorLog/data.ts @@ -0,0 +1,249 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE, InfraApiErrorLogProcessStatusEnum } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + allowClear: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'exceptionTime', + label: '异常时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'processStatus', + label: '处理状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + 'number', + ), + allowClear: true, + placeholder: '请选择处理状态', + }, + defaultValue: InfraApiErrorLogProcessStatusEnum.INIT, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 200, + }, + { + field: 'exceptionTime', + title: '异常发生时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'exceptionName', + title: '异常名', + minWidth: 180, + }, + { + field: 'processStatus', + title: '处理状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS }, + }, + }, + { + title: '操作', + minWidth: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'exceptionTime', + label: '异常时间', + render: (val) => { + return formatDateTime(val) as string; + }, + }, + { + field: 'exceptionName', + label: '异常名', + }, + { + field: 'exceptionStackTrace', + label: '异常堆栈', + show: (val) => !val, + render: (val) => { + if (val) { + return h('textarea', { + value: val, + style: + 'width: 100%; min-height: 200px; max-height: 400px; resize: vertical;', + readonly: true, + }); + } + return ''; + }, + }, + { + field: 'processStatus', + label: '处理状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + value: val, + }); + }, + }, + { + field: 'processUserId', + label: '处理人', + show: (val) => !val, + }, + { + field: 'processTime', + label: '处理时间', + show: (val) => !val, + render: (val) => { + return formatDateTime(val) as string; + }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/apiErrorLog/index.vue b/apps/web-tdesign/src/views/infra/apiErrorLog/index.vue new file mode 100644 index 000000000..df3c279d5 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiErrorLog/index.vue @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/apiErrorLog/modules/detail.vue b/apps/web-tdesign/src/views/infra/apiErrorLog/modules/detail.vue new file mode 100644 index 000000000..da52c17fa --- /dev/null +++ b/apps/web-tdesign/src/views/infra/apiErrorLog/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/build/index.vue b/apps/web-tdesign/src/views/infra/build/index.vue new file mode 100644 index 000000000..0a12d2721 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/build/index.vue @@ -0,0 +1,7 @@ + + + + 待完成 + diff --git a/apps/web-tdesign/src/views/infra/codegen/data.ts b/apps/web-tdesign/src/views/infra/codegen/data.ts new file mode 100644 index 000000000..b07ea2a32 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/data.ts @@ -0,0 +1,552 @@ +import type { Recordable } from '@vben/types'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { InfraCodegenApi } from '#/api/infra/codegen'; +import type { SystemMenuApi } from '#/api/system/menu'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { handleTree } from '@vben/utils'; + +import { getDataSourceConfigList } from '#/api/infra/data-source-config'; +import { getMenuList } from '#/api/system/menu'; +import { $t } from '#/locales'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 导入数据库表的表单 */ +export function useImportTableFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'dataSourceConfigId', + label: '数据源', + component: 'ApiSelect', + componentProps: { + api: getDataSourceConfigList, + labelField: 'name', + valueField: 'id', + autoSelect: 'first', + placeholder: '请选择数据源', + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '表名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'comment', + label: '表描述', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入表描述', + }, + }, + ]; +} + +/** 导入数据库表表格列定义 */ +export function useImportTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { field: 'name', title: '表名称', minWidth: 200 }, + { field: 'comment', title: '表描述', minWidth: 200 }, + ]; +} + +/** 基本信息表单的 schema */ +export function useBasicInfoFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + placeholder: '请输入表描述', + }, + rules: 'required', + }, + { + fieldName: 'className', + label: '实体类名称', + component: 'Input', + componentProps: { + placeholder: '请输入实体类名称', + }, + rules: 'required', + help: '默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。', + }, + { + fieldName: 'author', + label: '作者', + component: 'Input', + componentProps: { + placeholder: '请输入作者', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + rows: 3, + placeholder: '请输入备注', + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 生成信息表单基础 schema */ +export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Select', + fieldName: 'templateType', + label: '生成模板', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, + 'number', + ), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'frontType', + label: '前端类型', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'scene', + label: '生成场景', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + fieldName: 'parentMenuId', + label: '上级菜单', + help: '分配到指定菜单下,例如 系统管理', + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: async () => { + const data = await getMenuList(); + data.unshift({ + id: 0, + name: '顶级菜单', + } as SystemMenuApi.Menu); + return handleTree(data); + }, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级菜单', + filterTreeNode(input: string, node: Recordable) { + if (!input || input.length === 0) { + return true; + } + const name: string = node.label ?? ''; + if (!name) return false; + return name.includes(input) || $t(name).includes(input); + }, + showSearch: true, + treeDefaultExpandedKeys: [0], + }, + rules: 'selectRequired', + renderComponentContent() { + return { + title({ label, icon }: { icon: string; label: string }) { + const components = []; + if (!label) return ''; + if (icon) { + components.push(h(IconifyIcon, { class: 'size-4', icon })); + } + components.push(h('span', { class: '' }, $t(label || ''))); + return h('div', { class: 'flex items-center gap-1' }, components); + }, + }; + }, + }, + { + component: 'Input', + fieldName: 'moduleName', + label: '模块名', + help: '模块名,即一级目录,例如 system、infra、tool 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'businessName', + label: '业务名', + help: '业务名,即二级目录,例如 user、permission、dict 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'className', + label: '类名称', + help: '类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'classComment', + label: '类描述', + help: '用作类描述,例如 用户', + rules: 'required', + }, + ]; +} + +/** 树表信息 schema */ +export function useGenerationInfoTreeFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'treeDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['树表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'treeParentColumnId', + label: '父编号字段', + help: '树显示的父编码字段名,例如 parent_Id', + componentProps: { + class: 'w-full', + allowClear: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'treeNameColumnId', + label: '名称字段', + help: '树节点显示的名称字段,一般是 name', + componentProps: { + class: 'w-full', + allowClear: true, + placeholder: '请选择名称字段', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + ]; +} + +/** 主子表信息 schema */ +export function useGenerationInfoSubTableFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], + tables: InfraCodegenApi.CodegenTable[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'subDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['主子表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'masterTableId', + label: '关联的主表', + help: '关联主表(父表)的表名, 如:system_user', + componentProps: { + class: 'w-full', + allowClear: true, + placeholder: '请选择', + options: tables.map((table) => ({ + label: `${table.tableName}:${table.tableComment}`, + value: table.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'subJoinColumnId', + label: '子表关联的字段', + help: '子表关联的字段, 如:user_id', + componentProps: { + class: 'w-full', + allowClear: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: `${column.columnName}:${column.columnComment}`, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'RadioGroup', + fieldName: 'subJoinMany', + label: '关联关系', + help: '主表与子表的关联关系', + componentProps: { + class: 'w-full', + allowClear: true, + placeholder: '请选择', + options: [ + { + label: '一对多', + value: true, + }, + { + label: '一对一', + value: false, + }, + ], + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入表描述', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + getDataSourceConfigName?: (dataSourceConfigId: number) => string | undefined, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'dataSourceConfigId', + title: '数据源', + minWidth: 120, + formatter: ({ cellValue }) => getDataSourceConfigName?.(cellValue) || '-', + }, + { + field: 'tableName', + title: '表名称', + minWidth: 200, + }, + { + field: 'tableComment', + title: '表描述', + minWidth: 200, + }, + { + field: 'className', + title: '实体', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 代码生成表格列定义 */ +export function useCodegenColumnTableColumns(): VxeTableGridOptions['columns'] { + return [ + { field: 'columnName', title: '字段列名', minWidth: 130 }, + { + field: 'columnComment', + title: '字段描述', + minWidth: 100, + slots: { default: 'columnComment' }, + }, + { field: 'dataType', title: '物理类型', minWidth: 100 }, + { + field: 'javaType', + title: 'Java 类型', + minWidth: 130, + slots: { default: 'javaType' }, + params: { + options: [ + { label: 'Long', value: 'Long' }, + { label: 'String', value: 'String' }, + { label: 'Integer', value: 'Integer' }, + { label: 'Double', value: 'Double' }, + { label: 'BigDecimal', value: 'BigDecimal' }, + { label: 'LocalDateTime', value: 'LocalDateTime' }, + { label: 'Boolean', value: 'Boolean' }, + ], + }, + }, + { + field: 'javaField', + title: 'Java 属性', + minWidth: 100, + slots: { default: 'javaField' }, + }, + { + field: 'createOperation', + title: '插入', + width: 40, + slots: { default: 'createOperation' }, + }, + { + field: 'updateOperation', + title: '编辑', + width: 40, + slots: { default: 'updateOperation' }, + }, + { + field: 'listOperationResult', + title: '列表', + width: 40, + slots: { default: 'listOperationResult' }, + }, + { + field: 'listOperation', + title: '查询', + width: 40, + slots: { default: 'listOperation' }, + }, + { + field: 'listOperationCondition', + title: '查询方式', + minWidth: 100, + slots: { default: 'listOperationCondition' }, + params: { + options: [ + { label: '=', value: '=' }, + { label: '!=', value: '!=' }, + { label: '>', value: '>' }, + { label: '>=', value: '>=' }, + { label: '<', value: '<' }, + { label: '<=', value: '<=' }, + { label: 'LIKE', value: 'LIKE' }, + { label: 'BETWEEN', value: 'BETWEEN' }, + ], + }, + }, + { + field: 'nullable', + title: '允许空', + width: 60, + slots: { default: 'nullable' }, + }, + { + field: 'htmlType', + title: '显示类型', + width: 130, + slots: { default: 'htmlType' }, + params: { + options: [ + { label: '文本框', value: 'input' }, + { label: '文本域', value: 'textarea' }, + { label: '下拉框', value: 'select' }, + { label: '单选框', value: 'radio' }, + { label: '复选框', value: 'checkbox' }, + { label: '日期控件', value: 'datetime' }, + { label: '图片上传', value: 'imageUpload' }, + { label: '文件上传', value: 'fileUpload' }, + { label: '富文本控件', value: 'editor' }, + ], + }, + }, + { + field: 'dictType', + title: '字典类型', + width: 120, + slots: { default: 'dictType' }, + }, + { + field: 'example', + title: '示例', + minWidth: 100, + slots: { default: 'example' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/codegen/edit/index.vue b/apps/web-tdesign/src/views/infra/codegen/edit/index.vue new file mode 100644 index 000000000..dbec22cf5 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/edit/index.vue @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + 上一步 + + 下一步 + + + 保存 + + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/index.vue b/apps/web-tdesign/src/views/infra/codegen/index.vue new file mode 100644 index 000000000..409861421 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/index.vue @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/modules/basic-info.vue b/apps/web-tdesign/src/views/infra/codegen/modules/basic-info.vue new file mode 100644 index 000000000..00c49911b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/modules/basic-info.vue @@ -0,0 +1,45 @@ + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/modules/column-info.vue b/apps/web-tdesign/src/views/infra/codegen/modules/column-info.vue new file mode 100644 index 000000000..148e311d0 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/modules/column-info.vue @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + {{ option.label }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ option.label }} + + + + + + + + + + + + + + {{ option.label }} + + + + + + + + + {{ option.name }} + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/modules/generation-info.vue b/apps/web-tdesign/src/views/infra/codegen/modules/generation-info.vue new file mode 100644 index 000000000..56ffe616f --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/modules/generation-info.vue @@ -0,0 +1,176 @@ + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/modules/import-table.vue b/apps/web-tdesign/src/views/infra/codegen/modules/import-table.vue new file mode 100644 index 000000000..4e7bad180 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/modules/import-table.vue @@ -0,0 +1,119 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/codegen/modules/preview-code.vue b/apps/web-tdesign/src/views/infra/codegen/modules/preview-code.vue new file mode 100644 index 000000000..7f6d86627 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/codegen/modules/preview-code.vue @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + 复制代码 + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/config/data.ts b/apps/web-tdesign/src/views/infra/config/data.ts new file mode 100644 index 000000000..a365fbd78 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/config/data.ts @@ -0,0 +1,187 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'category', + label: '参数分类', + component: 'Input', + componentProps: { + placeholder: '请输入参数分类', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + }, + rules: 'required', + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '参数键值', + component: 'Input', + componentProps: { + placeholder: '请输入参数键值', + }, + rules: 'required', + }, + { + fieldName: 'visible', + label: '是否可见', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + buttonStyle: 'solid', + optionType: 'button', + }, + defaultValue: true, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + allowClear: true, + }, + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + allowClear: true, + }, + }, + { + fieldName: 'type', + label: '系统内置', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CONFIG_TYPE, 'number'), + placeholder: '请选择系统内置', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '参数主键', + minWidth: 100, + }, + { + field: 'category', + title: '参数分类', + minWidth: 120, + }, + { + field: 'name', + title: '参数名称', + minWidth: 200, + }, + { + field: 'key', + title: '参数键名', + minWidth: 200, + }, + { + field: 'value', + title: '参数键值', + minWidth: 150, + }, + { + field: 'visible', + title: '是否可见', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'type', + title: '系统内置', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_CONFIG_TYPE }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/config/index.vue b/apps/web-tdesign/src/views/infra/config/index.vue new file mode 100644 index 000000000..098a65af4 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/config/index.vue @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/config/modules/form.vue b/apps/web-tdesign/src/views/infra/config/modules/form.vue new file mode 100644 index 000000000..caacf3074 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/config/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/dataSourceConfig/data.ts b/apps/web-tdesign/src/views/infra/dataSourceConfig/data.ts new file mode 100644 index 000000000..6f4c6027f --- /dev/null +++ b/apps/web-tdesign/src/views/infra/dataSourceConfig/data.ts @@ -0,0 +1,92 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '数据源名称', + component: 'Input', + componentProps: { + placeholder: '请输入数据源名称', + }, + rules: 'required', + }, + { + fieldName: 'url', + label: '数据源连接', + component: 'Input', + componentProps: { + placeholder: '请输入数据源连接', + }, + rules: 'required', + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + }, + { + fieldName: 'password', + label: '密码', + component: 'Input', + componentProps: { + placeholder: '请输入密码', + type: 'password', + }, + rules: 'required', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '主键编号', + minWidth: 100, + }, + { + field: 'name', + title: '数据源名称', + minWidth: 150, + }, + { + field: 'url', + title: '数据源连接', + minWidth: 300, + }, + { + field: 'username', + title: '用户名', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/dataSourceConfig/index.vue b/apps/web-tdesign/src/views/infra/dataSourceConfig/index.vue new file mode 100644 index 000000000..18be0d1e4 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/dataSourceConfig/modules/form.vue b/apps/web-tdesign/src/views/infra/dataSourceConfig/modules/form.vue new file mode 100644 index 000000000..e84bdad3e --- /dev/null +++ b/apps/web-tdesign/src/views/infra/dataSourceConfig/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo01/data.ts b/apps/web-tdesign/src/views/infra/demo/demo01/data.ts new file mode 100644 index 000000000..ec8ca5d02 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo01/data.ts @@ -0,0 +1,152 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo01ContactApi } from '#/api/infra/demo/demo01'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + }, + { + fieldName: 'birthday', + label: '出生年', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + { + fieldName: 'avatar', + label: '头像', + component: 'ImageUpload', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生年', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'avatar', + title: '头像', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/demo/demo01/index.vue b/apps/web-tdesign/src/views/infra/demo/demo01/index.vue new file mode 100644 index 000000000..3bf387dd6 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo01/index.vue @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo01/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/demo01/modules/form.vue new file mode 100644 index 000000000..4d6d70bdf --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo01/modules/form.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo02/data.ts b/apps/web-tdesign/src/views/infra/demo/demo02/data.ts new file mode 100644 index 000000000..74430fb28 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo02/data.ts @@ -0,0 +1,120 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo02CategoryApi } from '#/api/infra/demo/demo02'; + +import { handleTree } from '@vben/utils'; + +import { getDemo02CategoryList } from '#/api/infra/demo/demo02'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级示例分类', + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: async () => { + const data = await getDemo02CategoryList({}); + data.unshift({ + id: 0, + name: '顶级示例分类', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级示例分类', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'parentId', + label: '父级编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入父级编号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + treeNode: true, + }, + { + field: 'parentId', + title: '父级编号', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/demo/demo02/index.vue b/apps/web-tdesign/src/views/infra/demo/demo02/index.vue new file mode 100644 index 000000000..4c9fe1e95 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo02/index.vue @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo02/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/demo02/modules/form.vue new file mode 100644 index 000000000..a7440947a --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo02/modules/form.vue @@ -0,0 +1,90 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/data.ts b/apps/web-tdesign/src/views/infra/demo/demo03/erp/data.ts new file mode 100644 index 000000000..f8acac72a --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/data.ts @@ -0,0 +1,381 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改的表单 */ +export function useDemo03CourseFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入分数', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03CourseGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入分数', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03GradeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入班主任', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/index.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/index.vue new file mode 100644 index 000000000..78db8cc64 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/index.vue @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue new file mode 100644 index 000000000..4c747e7cd --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue @@ -0,0 +1,91 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue new file mode 100644 index 000000000..89168cbde --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue @@ -0,0 +1,197 @@ + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue new file mode 100644 index 000000000..f5167b3e8 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue @@ -0,0 +1,91 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue new file mode 100644 index 000000000..2a4df3b5a --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue @@ -0,0 +1,197 @@ + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/form.vue new file mode 100644 index 000000000..cc417c6bd --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/erp/modules/form.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/data.ts b/apps/web-tdesign/src/views/infra/demo/demo03/inner/data.ts new file mode 100644 index 000000000..0166ba1c0 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/data.ts @@ -0,0 +1,276 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { type: 'expand', width: 80, slots: { content: 'expand_content' } }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/index.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/index.vue new file mode 100644 index 000000000..5e2b18a8b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/index.vue @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue new file mode 100644 index 000000000..2533a2f3c --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生课程']) }} + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue new file mode 100644 index 000000000..50297db48 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue new file mode 100644 index 000000000..3e2854608 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue new file mode 100644 index 000000000..1ad2db740 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/form.vue new file mode 100644 index 000000000..7d0ced1a5 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/inner/modules/form.vue @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/normal/data.ts b/apps/web-tdesign/src/views/infra/demo/demo03/normal/data.ts new file mode 100644 index 000000000..583437d99 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/normal/data.ts @@ -0,0 +1,211 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/normal/index.vue b/apps/web-tdesign/src/views/infra/demo/demo03/normal/index.vue new file mode 100644 index 000000000..bd1f5c030 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/normal/index.vue @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue new file mode 100644 index 000000000..3923249c8 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生课程']) }} + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue new file mode 100644 index 000000000..5d5f396bf --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/form.vue new file mode 100644 index 000000000..cd3d8854b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/demo03/normal/modules/form.vue @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo01/index.vue b/apps/web-tdesign/src/views/infra/demo/general/demo01/index.vue new file mode 100644 index 000000000..a9b00a346 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo01/index.vue @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['示例联系人']) }} + + + + {{ $t('ui.actionTitle.export') }} + + + + 批量删除 + + + + + + + + + + + + + + + {{ formatDateTime(row.birthday) }} + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo01/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo01/modules/form.vue new file mode 100644 index 000000000..362ca24b1 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo01/modules/form.vue @@ -0,0 +1,139 @@ + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo02/index.vue b/apps/web-tdesign/src/views/infra/demo/general/demo02/index.vue new file mode 100644 index 000000000..fdc92f64e --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo02/index.vue @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + {{ isExpanded ? '收缩' : '展开' }} + + + + {{ $t('ui.actionTitle.create', ['示例分类']) }} + + + + {{ $t('ui.actionTitle.export') }} + + + + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + 新增下级 + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo02/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo02/modules/form.vue new file mode 100644 index 000000000..7f444e02b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo02/modules/form.vue @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/index.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/index.vue new file mode 100644 index 000000000..e246a62c6 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/index.vue @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生']) }} + + + + {{ $t('ui.actionTitle.export') }} + + + + 批量删除 + + + + + + + + + + + + + + + {{ formatDateTime(row.birthday) }} + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-form.vue new file mode 100644 index 000000000..91634369f --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-form.vue @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue new file mode 100644 index 000000000..1730c5094 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生']) }} + + + + 批量删除 + + + + + + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-form.vue new file mode 100644 index 000000000..961aa94b6 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-form.vue @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue new file mode 100644 index 000000000..9b7f4e138 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生']) }} + + + + 批量删除 + + + + + + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/form.vue new file mode 100644 index 000000000..395ef3535 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/erp/modules/form.vue @@ -0,0 +1,135 @@ + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/index.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/index.vue new file mode 100644 index 000000000..9e8ea1b8d --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/index.vue @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生']) }} + + + + {{ $t('ui.actionTitle.export') }} + + + + 批量删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ formatDateTime(row.birthday) }} + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-form.vue new file mode 100644 index 000000000..31f77d290 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-form.vue @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + {{ $t('ui.actionTitle.create', ['学生课程']) }} + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue new file mode 100644 index 000000000..bb49eeb74 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue @@ -0,0 +1,59 @@ + + + + + + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-form.vue new file mode 100644 index 000000000..7c59921bd --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-form.vue @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue new file mode 100644 index 000000000..a08d67af3 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue @@ -0,0 +1,59 @@ + + + + + + + + + + + + {{ formatDateTime(row.createTime) }} + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/form.vue new file mode 100644 index 000000000..646869558 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/inner/modules/form.vue @@ -0,0 +1,173 @@ + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/index.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/index.vue new file mode 100644 index 000000000..5f9d6a39e --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/index.vue @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + 重置 + + 搜索 + + + + + + + + + + + + {{ $t('ui.actionTitle.create', ['学生']) }} + + + + {{ $t('ui.actionTitle.export') }} + + + + 批量删除 + + + + + + + + + + + + + + + {{ formatDateTime(row.birthday) }} + + + + + + {{ formatDateTime(row.createTime) }} + + + + + + {{ $t('ui.actionTitle.edit') }} + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue new file mode 100644 index 000000000..31f77d290 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + {{ $t('ui.actionTitle.delete') }} + + + + + + + + {{ $t('ui.actionTitle.create', ['学生课程']) }} + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-grade-form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-grade-form.vue new file mode 100644 index 000000000..7c59921bd --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/demo03-grade-form.vue @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/form.vue b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/form.vue new file mode 100644 index 000000000..96cdcb788 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/demo/general/demo03/normal/modules/form.vue @@ -0,0 +1,172 @@ + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/druid/index.vue b/apps/web-tdesign/src/views/infra/druid/index.vue new file mode 100644 index 000000000..01ec58d5f --- /dev/null +++ b/apps/web-tdesign/src/views/infra/druid/index.vue @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/file/data.ts b/apps/web-tdesign/src/views/infra/file/data.ts new file mode 100644 index 000000000..9ee70319b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/file/data.ts @@ -0,0 +1,107 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的字段 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'file', + label: '文件上传', + component: 'Upload', + componentProps: { + placeholder: '请选择要上传的文件', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'path', + label: '文件路径', + component: 'Input', + componentProps: { + placeholder: '请输入文件路径', + clearable: true, + }, + }, + { + fieldName: 'type', + label: '文件类型', + component: 'Input', + componentProps: { + placeholder: '请输入文件类型', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'name', + title: '文件名', + minWidth: 150, + }, + { + field: 'path', + title: '文件路径', + minWidth: 200, + showOverflow: true, + }, + { + field: 'url', + title: 'URL', + minWidth: 200, + showOverflow: true, + }, + { + field: 'size', + title: '文件大小', + minWidth: 80, + formatter: 'formatFileSize', + }, + { + field: 'type', + title: '文件类型', + minWidth: 120, + }, + { + field: 'file-content', + title: '文件内容', + minWidth: 120, + slots: { + default: 'file-content', + }, + }, + { + field: 'createTime', + title: '上传时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/file/index.vue b/apps/web-tdesign/src/views/infra/file/index.vue new file mode 100644 index 000000000..2f24de3a8 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/file/index.vue @@ -0,0 +1,188 @@ + + + + + + + + + + + + openWindow(row.url!)"> + {{ row.type && row.type.includes('pdf') ? '预览' : '下载' }} + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/file/modules/form.vue b/apps/web-tdesign/src/views/infra/file/modules/form.vue new file mode 100644 index 000000000..e0fa6affc --- /dev/null +++ b/apps/web-tdesign/src/views/infra/file/modules/form.vue @@ -0,0 +1,82 @@ + + + + + + + + + + + + + 点击或拖拽文件到此区域上传 + + 支持 .jpg、.png、.gif、.webp 格式图片文件 + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/fileConfig/data.ts b/apps/web-tdesign/src/views/infra/fileConfig/data.ts new file mode 100644 index 000000000..efe5823f8 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/fileConfig/data.ts @@ -0,0 +1,333 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '配置名', + component: 'Input', + componentProps: { + placeholder: '请输入配置名', + }, + rules: 'required', + }, + { + fieldName: 'storage', + label: '存储器', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE, 'number'), + placeholder: '请选择存储器', + }, + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (formValues) => formValues.id, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + // DB / Local / FTP / SFTP + { + fieldName: 'config.basePath', + label: '基础路径', + component: 'Input', + componentProps: { + placeholder: '请输入基础路径', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => + formValues.storage >= 10 && formValues.storage <= 12, + }, + }, + { + fieldName: 'config.host', + label: '主机地址', + component: 'Input', + componentProps: { + placeholder: '请输入主机地址', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => + formValues.storage >= 11 && formValues.storage <= 12, + }, + }, + { + fieldName: 'config.port', + label: '主机端口', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入主机端口', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => + formValues.storage >= 11 && formValues.storage <= 12, + }, + }, + { + fieldName: 'config.username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => + formValues.storage >= 11 && formValues.storage <= 12, + }, + }, + { + fieldName: 'config.password', + label: '密码', + component: 'Input', + componentProps: { + placeholder: '请输入密码', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => + formValues.storage >= 11 && formValues.storage <= 12, + }, + }, + { + fieldName: 'config.mode', + label: '连接模式', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '主动模式', value: 'Active' }, + { label: '被动模式', value: 'Passive' }, + ], + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 11, + }, + }, + // S3 + { + fieldName: 'config.endpoint', + label: '节点地址', + component: 'Input', + componentProps: { + placeholder: '请输入节点地址', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + }, + { + fieldName: 'config.bucket', + label: '存储 bucket', + component: 'Input', + componentProps: { + placeholder: '请输入 bucket', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + }, + { + fieldName: 'config.accessKey', + label: 'accessKey', + component: 'Input', + componentProps: { + placeholder: '请输入 accessKey', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + }, + { + fieldName: 'config.accessSecret', + label: 'accessSecret', + component: 'Input', + componentProps: { + placeholder: '请输入 accessSecret', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + }, + { + fieldName: 'config.enablePathStyleAccess', + label: '是否 Path Style', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '启用', value: true }, + { label: '禁用', value: false }, + ], + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + defaultValue: false, + }, + { + fieldName: 'config.enablePublicAccess', + label: '公开访问', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '公开', value: true }, + { label: '私有', value: false }, + ], + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => formValues.storage === 20, + }, + defaultValue: false, + }, + // 通用 + { + fieldName: 'config.domain', + label: '自定义域名', + component: 'Input', + componentProps: { + placeholder: '请输入自定义域名', + }, + rules: 'required', + dependencies: { + triggerFields: ['storage'], + show: (formValues) => !!formValues.storage, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '配置名', + component: 'Input', + componentProps: { + placeholder: '请输入配置名', + allowClear: true, + }, + }, + { + fieldName: 'storage', + label: '存储器', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE, 'number'), + placeholder: '请选择存储器', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '配置名', + minWidth: 120, + }, + { + field: 'storage', + title: '存储器', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_FILE_STORAGE }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + }, + { + field: 'master', + title: '主配置', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/fileConfig/index.vue b/apps/web-tdesign/src/views/infra/fileConfig/index.vue new file mode 100644 index 000000000..1f2f2c065 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/fileConfig/index.vue @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/fileConfig/modules/form.vue b/apps/web-tdesign/src/views/infra/fileConfig/modules/form.vue new file mode 100644 index 000000000..19d8a5755 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/fileConfig/modules/form.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/job/data.ts b/apps/web-tdesign/src/views/infra/job/data.ts new file mode 100644 index 000000000..5e09bfb26 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/data.ts @@ -0,0 +1,245 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h, markRaw } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { Timeline } from 'tdesign-vue-next'; + +import { CronTab } from '#/components/cron-tab'; +import { DictTag } from '#/components/dict-tag'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + placeholder: '请输入任务名称', + }, + rules: 'required', + }, + { + fieldName: 'handlerName', + label: '处理器的名字', + component: 'Input', + componentProps: { + placeholder: '请输入处理器的名字', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => !!values.id, + }, + rules: 'required', + }, + { + fieldName: 'handlerParam', + label: '处理器的参数', + component: 'Input', + componentProps: { + placeholder: '请输入处理器的参数', + }, + }, + { + fieldName: 'cronExpression', + label: 'CRON 表达式', + component: markRaw(CronTab), + componentProps: { + placeholder: '请输入 CRON 表达式', + }, + rules: 'required', + }, + { + fieldName: 'retryCount', + label: '重试次数', + component: 'InputNumber', + componentProps: { + placeholder: '请输入重试次数。设置为 0 时,不进行重试', + min: 0, + }, + rules: 'required', + }, + { + fieldName: 'retryInterval', + label: '重试间隔', + component: 'InputNumber', + componentProps: { + placeholder: '请输入重试间隔,单位:毫秒。设置为 0 时,无需间隔', + min: 0, + }, + rules: 'required', + }, + { + fieldName: 'monitorTimeout', + label: '监控超时时间', + component: 'InputNumber', + componentProps: { + placeholder: '请输入监控超时时间,单位:毫秒', + min: 0, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入任务名称', + }, + }, + { + fieldName: 'status', + label: '任务状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_JOB_STATUS, 'number'), + allowClear: true, + placeholder: '请选择任务状态', + }, + }, + { + fieldName: 'handlerName', + label: '处理器的名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入处理器的名字', + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '任务编号', + minWidth: 80, + }, + { + field: 'name', + title: '任务名称', + minWidth: 120, + }, + { + field: 'status', + title: '任务状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_JOB_STATUS }, + }, + }, + { + field: 'handlerName', + title: '处理器的名字', + minWidth: 180, + }, + { + field: 'handlerParam', + title: '处理器的参数', + minWidth: 140, + }, + { + field: 'cronExpression', + title: 'CRON 表达式', + minWidth: 120, + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '任务编号', + }, + { + field: 'name', + label: '任务名称', + }, + { + field: 'status', + label: '任务状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_JOB_STATUS, + value: val, + }); + }, + }, + { + field: 'handlerName', + label: '处理器的名字', + }, + { + field: 'handlerParam', + label: '处理器的参数', + }, + { + field: 'cronExpression', + label: 'Cron 表达式', + }, + { + field: 'retryCount', + label: '重试次数', + }, + { + label: '重试间隔', + field: 'retryInterval', + render: (val) => { + return val ? `${val} 毫秒` : '无间隔'; + }, + }, + { + label: '监控超时时间', + field: 'monitorTimeout', + render: (val) => { + return val && val > 0 ? `${val} 毫秒` : '未开启'; + }, + }, + { + field: 'nextTimes', + label: '后续执行时间', + render: (val) => { + if (!val || val.length === 0) { + return '无后续执行时间'; + } + return h(Timeline, {}, () => + val?.map((time: Date) => + h(Timeline.Item, {}, () => formatDateTime(time)), + ), + ); + }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/job/index.vue b/apps/web-tdesign/src/views/infra/job/index.vue new file mode 100644 index 000000000..561f69f3b --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/index.vue @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/job/logger/data.ts b/apps/web-tdesign/src/views/infra/job/logger/data.ts new file mode 100644 index 000000000..f17622cb9 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/logger/data.ts @@ -0,0 +1,185 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import dayjs from 'dayjs'; + +import { DictTag } from '#/components/dict-tag'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'handlerName', + label: '处理器的名字', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入处理器的名字', + }, + }, + { + fieldName: 'beginTime', + label: '开始执行时间', + component: 'DatePicker', + componentProps: { + allowClear: true, + placeholder: '选择开始执行时间', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + showTime: { + format: 'HH:mm:ss', + defaultValue: dayjs('00:00:00', 'HH:mm:ss'), + }, + }, + }, + { + fieldName: 'endTime', + label: '结束执行时间', + component: 'DatePicker', + componentProps: { + allowClear: true, + placeholder: '选择结束执行时间', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + showTime: { + format: 'HH:mm:ss', + defaultValue: dayjs('23:59:59', 'HH:mm:ss'), + }, + }, + }, + { + fieldName: 'status', + label: '任务状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_JOB_LOG_STATUS, 'number'), + allowClear: true, + placeholder: '请选择任务状态', + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 80, + }, + { + field: 'jobId', + title: '任务编号', + minWidth: 80, + }, + { + field: 'handlerName', + title: '处理器的名字', + minWidth: 180, + }, + { + field: 'handlerParam', + title: '处理器的参数', + minWidth: 140, + }, + { + field: 'executeIndex', + title: '第几次执行', + minWidth: 100, + }, + { + field: 'beginTime', + title: '执行时间', + minWidth: 280, + formatter: ({ row }) => { + return `${formatDateTime(row.beginTime)} ~ ${formatDateTime(row.endTime)}`; + }, + }, + { + field: 'duration', + title: '执行时长', + minWidth: 120, + formatter: ({ row }) => { + return `${row.duration} 毫秒`; + }, + }, + { + field: 'status', + title: '任务状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_JOB_LOG_STATUS }, + }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'jobId', + label: '任务编号', + }, + { + field: 'handlerName', + label: '处理器的名字', + }, + { + field: 'handlerParam', + label: '处理器的参数', + }, + { + field: 'executeIndex', + label: '第几次执行', + }, + { + field: 'beginTime', + label: '执行时间', + render: (val, data) => { + if (val && data?.endTime) { + return `${formatDateTime(val)} ~ ${formatDateTime(data.endTime)}`; + } + return ''; + }, + }, + { + field: 'duration', + label: '执行时长', + render: (val) => { + return val ? `${val} 毫秒` : ''; + }, + }, + { + field: 'status', + label: '任务状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_JOB_LOG_STATUS, + value: val, + }); + }, + }, + { + field: 'result', + label: '执行结果', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/infra/job/logger/index.vue b/apps/web-tdesign/src/views/infra/job/logger/index.vue new file mode 100644 index 000000000..c0c4a7461 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/logger/index.vue @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/job/logger/modules/detail.vue b/apps/web-tdesign/src/views/infra/job/logger/modules/detail.vue new file mode 100644 index 000000000..ceb1b5bf1 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/logger/modules/detail.vue @@ -0,0 +1,51 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/job/modules/detail.vue b/apps/web-tdesign/src/views/infra/job/modules/detail.vue new file mode 100644 index 000000000..f23149e2d --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/modules/detail.vue @@ -0,0 +1,56 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/job/modules/form.vue b/apps/web-tdesign/src/views/infra/job/modules/form.vue new file mode 100644 index 000000000..71b145da5 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/job/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/redis/index.vue b/apps/web-tdesign/src/views/infra/redis/index.vue new file mode 100644 index 000000000..0dfd62c1d --- /dev/null +++ b/apps/web-tdesign/src/views/infra/redis/index.vue @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/redis/modules/commands.vue b/apps/web-tdesign/src/views/infra/redis/modules/commands.vue new file mode 100644 index 000000000..25c263e68 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/redis/modules/commands.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/redis/modules/info.vue b/apps/web-tdesign/src/views/infra/redis/modules/info.vue new file mode 100644 index 000000000..bee9922bb --- /dev/null +++ b/apps/web-tdesign/src/views/infra/redis/modules/info.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/redis/modules/memory.vue b/apps/web-tdesign/src/views/infra/redis/modules/memory.vue new file mode 100644 index 000000000..5220c4631 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/redis/modules/memory.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/apps/web-tdesign/src/views/infra/server/index.vue b/apps/web-tdesign/src/views/infra/server/index.vue new file mode 100644 index 000000000..d8893bdbd --- /dev/null +++ b/apps/web-tdesign/src/views/infra/server/index.vue @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/skywalking/index.vue b/apps/web-tdesign/src/views/infra/skywalking/index.vue new file mode 100644 index 000000000..2ff01c3bf --- /dev/null +++ b/apps/web-tdesign/src/views/infra/skywalking/index.vue @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/swagger/index.vue b/apps/web-tdesign/src/views/infra/swagger/index.vue new file mode 100644 index 000000000..c04365cdf --- /dev/null +++ b/apps/web-tdesign/src/views/infra/swagger/index.vue @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/infra/webSocket/index.vue b/apps/web-tdesign/src/views/infra/webSocket/index.vue new file mode 100644 index 000000000..c4fd5fa54 --- /dev/null +++ b/apps/web-tdesign/src/views/infra/webSocket/index.vue @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + 连接管理 + + + + 连接状态: + {{ getStatusText }} + + + + + 服务地址 + + + + {{ getIsOpen ? '关闭连接' : '开启连接' }} + + + + + 消息发送 + + + + + + 全 + 所有人 + + + + + + {{ user.nickname.slice(0, 1) }} + + {{ user.nickname }} + + + + + + + + + + + 发送消息 + + + + + + + + + 消息记录 + + {{ messageList.length }} 条 + + + + + + + + + + + + {{ getMessageTypeText(msg.type) }} + + + 用户 ID: {{ msg.userId }} + + + + {{ formatDate(msg.time) }} + + + + {{ msg.text }} + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/area/data.ts b/apps/web-tdesign/src/views/system/area/data.ts new file mode 100644 index 000000000..e6cc06c99 --- /dev/null +++ b/apps/web-tdesign/src/views/system/area/data.ts @@ -0,0 +1,48 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemAreaApi } from '#/api/system/area'; + +import { z } from '#/adapter/form'; + +/** 查询 IP 的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'ip', + label: 'IP 地址', + component: 'Input', + componentProps: { + placeholder: '请输入 IP 地址', + }, + rules: z.string().ip({ message: '请输入正确的 IP 地址' }), + }, + { + fieldName: 'result', + label: '地址', + component: 'Input', + componentProps: { + placeholder: '展示查询 IP 结果', + readonly: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '地区编码', + minWidth: 120, + align: 'left', + fixed: 'left', + treeNode: true, + }, + { + field: 'name', + title: '地区名称', + minWidth: 200, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/area/index.vue b/apps/web-tdesign/src/views/system/area/index.vue new file mode 100644 index 000000000..a07141905 --- /dev/null +++ b/apps/web-tdesign/src/views/system/area/index.vue @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/area/modules/form.vue b/apps/web-tdesign/src/views/system/area/modules/form.vue new file mode 100644 index 000000000..67e1db080 --- /dev/null +++ b/apps/web-tdesign/src/views/system/area/modules/form.vue @@ -0,0 +1,49 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dept/data.ts b/apps/web-tdesign/src/views/system/dept/data.ts new file mode 100644 index 000000000..f5421c429 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dept/data.ts @@ -0,0 +1,162 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemDeptApi } from '#/api/system/dept'; +import type { SystemUserApi } from '#/api/system/user'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级部门', + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: async () => { + const data = await getDeptList(); + data.unshift({ + id: 0, + name: '顶级部门', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级部门', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '部门名称', + component: 'Input', + componentProps: { + placeholder: '请输入部门名称', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + }, + rules: 'required', + }, + { + fieldName: 'leaderUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + }, + rules: z.number().optional(), + }, + { + fieldName: 'phone', + label: '联系电话', + component: 'Input', + componentProps: { + maxLength: 11, + placeholder: '请输入联系电话', + }, + rules: 'mobileRequired', + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + rules: z.string().email('邮箱格式不正确').or(z.literal('')).optional(), + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'name', + title: '部门名称', + minWidth: 150, + align: 'left', + fixed: 'left', + treeNode: true, + }, + { + field: 'leaderUserId', + title: '负责人', + minWidth: 150, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'status', + title: '部门状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/dept/index.vue b/apps/web-tdesign/src/views/system/dept/index.vue new file mode 100644 index 000000000..1fd74db74 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dept/index.vue @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dept/modules/form.vue b/apps/web-tdesign/src/views/system/dept/modules/form.vue new file mode 100644 index 000000000..7539f49b4 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dept/modules/form.vue @@ -0,0 +1,83 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dict/data.ts b/apps/web-tdesign/src/views/system/dict/data.ts new file mode 100644 index 000000000..a2c6ede66 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/data.ts @@ -0,0 +1,353 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleDictTypeList } from '#/api/system/dict/type'; + +// ============================== 字典类型 ============================== + +/** 类型新增/修改的表单 */ +export function useTypeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '字典名称', + component: 'Input', + componentProps: { + placeholder: '请输入字典名称', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '字典类型', + component: 'Input', + componentProps: (values) => { + return { + placeholder: '请输入字典类型', + disabled: !!values.id, + }; + }, + rules: 'required', + dependencies: { + triggerFields: [''], + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 类型列表的搜索表单 */ +export function useTypeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '字典名称', + component: 'Input', + componentProps: { + placeholder: '请输入字典名称', + allowClear: true, + }, + }, + { + fieldName: 'type', + label: '字典类型', + component: 'Input', + componentProps: { + placeholder: '请输入字典类型', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + ]; +} + +/** 类型列表的字段 */ +export function useTypeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '字典编号', + minWidth: 100, + }, + { + field: 'name', + title: '字典名称', + minWidth: 200, + }, + { + field: 'type', + title: '字典类型', + minWidth: 220, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + minWidth: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ============================== 字典数据 ============================== + +// TODO @芋艿:后续针对 antd,增加 +/** + * 颜色选项 + */ +const colorOptions = [ + { value: '', label: '无' }, + { value: 'processing', label: '主要' }, + { value: 'success', label: '成功' }, + { value: 'default', label: '默认' }, + { value: 'warning', label: '警告' }, + { value: 'error', label: '危险' }, + { value: 'pink', label: 'pink' }, + { value: 'red', label: 'red' }, + { value: 'orange', label: 'orange' }, + { value: 'green', label: 'green' }, + { value: 'cyan', label: 'cyan' }, + { value: 'blue', label: 'blue' }, + { value: 'purple', label: 'purple' }, +]; + +/** 数据新增/修改的表单 */ +export function useDataFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'dictType', + label: '字典类型', + component: 'ApiSelect', + componentProps: (values) => { + return { + api: getSimpleDictTypeList, + placeholder: '请输入字典类型', + labelField: 'name', + valueField: 'type', + disabled: !!values.id, + }; + }, + rules: 'required', + dependencies: { + triggerFields: [''], + }, + }, + { + fieldName: 'label', + label: '数据标签', + component: 'Input', + componentProps: { + placeholder: '请输入数据标签', + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '数据键值', + component: 'Input', + componentProps: { + placeholder: '请输入数据键值', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入显示排序', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'colorType', + label: '颜色类型', + component: 'Select', + componentProps: { + options: colorOptions, + placeholder: '请选择颜色类型', + }, + }, + { + fieldName: 'cssClass', + label: 'CSS Class', + component: 'Input', + componentProps: { + placeholder: '请输入 CSS Class', + }, + help: '输入 hex 模式的颜色, 例如 #108ee9', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 字典数据列表搜索表单 */ +export function useDataGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'label', + label: '字典标签', + component: 'Input', + componentProps: { + placeholder: '请输入字典标签', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + ]; +} + +/** 字典数据表格列 */ +export function useDataGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '字典编码', + minWidth: 100, + }, + { + field: 'label', + title: '字典标签', + minWidth: 180, + }, + { + field: 'value', + title: '字典键值', + minWidth: 100, + }, + { + field: 'sort', + title: '字典排序', + minWidth: 100, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'colorType', + title: '颜色类型', + minWidth: 120, + slots: { default: 'colorType' }, + }, + { + field: 'cssClass', + title: 'CSS Class', + minWidth: 120, + slots: { default: 'cssClass' }, + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + minWidth: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/dict/index.vue b/apps/web-tdesign/src/views/system/dict/index.vue new file mode 100644 index 000000000..8d824256d --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/index.vue @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dict/modules/data-form.vue b/apps/web-tdesign/src/views/system/dict/modules/data-form.vue new file mode 100644 index 000000000..b1e65a3b7 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/modules/data-form.vue @@ -0,0 +1,89 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dict/modules/data-grid.vue b/apps/web-tdesign/src/views/system/dict/modules/data-grid.vue new file mode 100644 index 000000000..7e2d620fc --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/modules/data-grid.vue @@ -0,0 +1,211 @@ + + + + + + + + + + + + {{ row.colorType }} + + + {{ row.cssClass }} + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dict/modules/type-form.vue b/apps/web-tdesign/src/views/system/dict/modules/type-form.vue new file mode 100644 index 000000000..670096030 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/modules/type-form.vue @@ -0,0 +1,85 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/dict/modules/type-grid.vue b/apps/web-tdesign/src/views/system/dict/modules/type-grid.vue new file mode 100644 index 000000000..691f57c27 --- /dev/null +++ b/apps/web-tdesign/src/views/system/dict/modules/type-grid.vue @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/loginlog/data.ts b/apps/web-tdesign/src/views/system/loginlog/data.ts new file mode 100644 index 000000000..24433e9ac --- /dev/null +++ b/apps/web-tdesign/src/views/system/loginlog/data.ts @@ -0,0 +1,147 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入用户名称', + }, + }, + { + fieldName: 'userIp', + label: '登录地址', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入登录地址', + }, + }, + { + fieldName: 'createTime', + label: '登录时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'logType', + title: '操作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_LOGIN_TYPE }, + }, + }, + { + field: 'username', + title: '用户名称', + minWidth: 180, + }, + { + field: 'userIp', + title: '登录地址', + minWidth: 180, + }, + { + field: 'userAgent', + title: '浏览器', + minWidth: 200, + }, + { + field: 'result', + title: '登录结果', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_LOGIN_RESULT }, + }, + }, + { + field: 'createTime', + title: '登录日期', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'logType', + label: '操作类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_LOGIN_TYPE, + value: val, + }); + }, + }, + { + field: 'username', + label: '用户名称', + }, + { + field: 'userIp', + label: '登录地址', + }, + { + field: 'userAgent', + label: '浏览器', + }, + { + field: 'result', + label: '登录结果', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_LOGIN_RESULT, + value: val, + }); + }, + }, + { + field: 'createTime', + label: '登录日期', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/loginlog/index.vue b/apps/web-tdesign/src/views/system/loginlog/index.vue new file mode 100644 index 000000000..9c9de61bc --- /dev/null +++ b/apps/web-tdesign/src/views/system/loginlog/index.vue @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/loginlog/modules/detail.vue b/apps/web-tdesign/src/views/system/loginlog/modules/detail.vue new file mode 100644 index 000000000..4982a32b3 --- /dev/null +++ b/apps/web-tdesign/src/views/system/loginlog/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/account/data.ts b/apps/web-tdesign/src/views/system/mail/account/data.ts new file mode 100644 index 000000000..2a00f39e4 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/account/data.ts @@ -0,0 +1,184 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'mail', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + rules: 'required', + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + }, + { + fieldName: 'password', + label: '密码', + component: 'InputPassword', + componentProps: { + placeholder: '请输入密码', + }, + rules: 'required', + }, + { + fieldName: 'host', + label: 'SMTP 服务器域名', + component: 'Input', + componentProps: { + placeholder: '请输入 SMTP 服务器域名', + }, + rules: 'required', + }, + { + fieldName: 'port', + label: 'SMTP 服务器端口', + component: 'InputNumber', + componentProps: { + placeholder: '请输入 SMTP 服务器端口', + min: 0, + max: 65_535, + }, + rules: 'required', + }, + { + fieldName: 'sslEnable', + label: '是否开启 SSL', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'starttlsEnable', + label: '是否开启 STARTTLS', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'mail', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + allowClear: true, + }, + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'mail', + title: '邮箱', + minWidth: 160, + }, + { + field: 'username', + title: '用户名', + minWidth: 160, + }, + { + field: 'host', + title: 'SMTP 服务器域名', + minWidth: 150, + }, + { + field: 'port', + title: 'SMTP 服务器端口', + minWidth: 130, + }, + { + field: 'sslEnable', + title: '是否开启 SSL', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'starttlsEnable', + title: '是否开启 STARTTLS', + minWidth: 145, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/mail/account/index.vue b/apps/web-tdesign/src/views/system/mail/account/index.vue new file mode 100644 index 000000000..066237b17 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/account/index.vue @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/account/modules/form.vue b/apps/web-tdesign/src/views/system/mail/account/modules/form.vue new file mode 100644 index 000000000..ff5de38d3 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/account/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/log/data.ts b/apps/web-tdesign/src/views/system/mail/log/data.ts new file mode 100644 index 000000000..3a12081d5 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/log/data.ts @@ -0,0 +1,259 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getSimpleMailAccountList } from '#/api/system/mail/account'; +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'sendTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + allowClear: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'sendStatus', + label: '发送状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, 'number'), + allowClear: true, + placeholder: '请选择发送状态', + }, + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + allowClear: true, + placeholder: '请选择邮箱账号', + }, + }, + { + fieldName: 'templateId', + label: '模板编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编号', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'sendTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'userType', + title: '接收用户', + minWidth: 150, + slots: { default: 'userInfo' }, + }, + { + field: 'toMails', + title: '接收信息', + minWidth: 300, + formatter: ({ row }) => { + const lines: string[] = []; + if (row.toMails && row.toMails.length > 0) { + lines.push(`收件:${row.toMails.join('、')}`); + } + if (row.ccMails && row.ccMails.length > 0) { + lines.push(`抄送:${row.ccMails.join('、')}`); + } + if (row.bccMails && row.bccMails.length > 0) { + lines.push(`密送:${row.bccMails.join('、')}`); + } + return lines.join('\n'); + }, + }, + { + field: 'templateTitle', + title: '邮件标题', + minWidth: 120, + }, + { + field: 'templateContent', + title: '邮件内容', + minWidth: 300, + }, + { + field: 'fromMail', + title: '发送邮箱', + minWidth: 120, + }, + { + field: 'sendStatus', + title: '发送状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS }, + }, + }, + { + field: 'templateCode', + title: '模板编码', + minWidth: 120, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '编号', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => { + return formatDateTime(val) as string; + }, + }, + { + field: 'fromMail', + label: '发送邮箱', + }, + { + field: 'userId', + label: '接收用户', + render: (val, data) => { + if (data?.userType && val) { + return h('div', [ + h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: data.userType, + }), + ` (${val})`, + ]); + } + return '无'; + }, + }, + { + field: 'toMails', + label: '接收信息', + render: (val, data) => { + const lines: string[] = []; + if (val && val.length > 0) { + lines.push(`收件:${val.join('、')}`); + } + if (data?.ccMails && data.ccMails.length > 0) { + lines.push(`抄送:${data.ccMails.join('、')}`); + } + if (data?.bccMails && data.bccMails.length > 0) { + lines.push(`密送:${data.bccMails.join('、')}`); + } + return h( + 'div', + { + style: { whiteSpace: 'pre-line' }, + }, + lines.join('\n'), + ); + }, + }, + { + field: 'templateId', + label: '模板编号', + }, + { + field: 'templateCode', + label: '模板编码', + }, + { + field: 'templateTitle', + label: '邮件标题', + }, + { + field: 'templateContent', + label: '邮件内容', + span: 2, + render: (val) => { + return h('div', { + innerHTML: val || '', + }); + }, + }, + { + field: 'sendStatus', + label: '发送状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, + value: val, + }); + }, + }, + { + field: 'sendTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'sendMessageId', + label: '发送消息编号', + }, + { + field: 'sendException', + label: '发送异常', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/mail/log/index.vue b/apps/web-tdesign/src/views/system/mail/log/index.vue new file mode 100644 index 000000000..43dfd1dbb --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/log/index.vue @@ -0,0 +1,92 @@ + + + + + + + + + + + + + ({{ row.userId }}) + + - + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/log/modules/detail.vue b/apps/web-tdesign/src/views/system/mail/log/modules/detail.vue new file mode 100644 index 000000000..3b04a9d04 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/log/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/template/data.ts b/apps/web-tdesign/src/views/system/mail/template/data.ts new file mode 100644 index 000000000..f7345abb5 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/template/data.ts @@ -0,0 +1,266 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleMailAccountList } from '#/api/system/mail/account'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + placeholder: '请选择邮箱账号', + }, + rules: 'required', + }, + { + fieldName: 'nickname', + label: '发送人名称', + component: 'Input', + componentProps: { + placeholder: '请输入发送人名称', + }, + }, + { + fieldName: 'title', + label: '模板标题', + component: 'Input', + componentProps: { + placeholder: '请输入模板标题', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '模板内容', + component: 'RichTextarea', + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 发送邮件表单 */ +export function useSendMailFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'content', + label: '模板内容', + component: 'RichTextarea', + componentProps: { + options: { + readonly: true, + }, + }, + }, + { + fieldName: 'toMails', + label: '收件邮箱', + component: 'Select', + componentProps: { + mode: 'tags', + allowClear: true, + placeholder: '请输入收件邮箱,按 Enter 添加', + }, + }, + { + fieldName: 'ccMails', + label: '抄送邮箱', + component: 'Select', + componentProps: { + mode: 'tags', + allowClear: true, + placeholder: '请输入抄送邮箱,按 Enter 添加', + }, + }, + { + fieldName: 'bccMails', + label: '密送邮箱', + component: 'Select', + componentProps: { + mode: 'tags', + allowClear: true, + placeholder: '请输入密送邮箱,按 Enter 添加', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'status', + label: '开启状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + placeholder: '请选择开启状态', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + allowClear: true, + placeholder: '请选择邮箱账号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + getAccountMail?: (accountId: number) => string | undefined, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'title', + title: '模板标题', + minWidth: 120, + }, + { + field: 'accountId', + title: '邮箱账号', + minWidth: 120, + formatter: ({ cellValue }) => getAccountMail?.(cellValue) || '-', + }, + { + field: 'nickname', + title: '发送人名称', + minWidth: 120, + }, + { + field: 'status', + title: '开启状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/mail/template/index.vue b/apps/web-tdesign/src/views/system/mail/template/index.vue new file mode 100644 index 000000000..fe593974f --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/template/index.vue @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/template/modules/form.vue b/apps/web-tdesign/src/views/system/mail/template/modules/form.vue new file mode 100644 index 000000000..87863641f --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/template/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/mail/template/modules/send-form.vue b/apps/web-tdesign/src/views/system/mail/template/modules/send-form.vue new file mode 100644 index 000000000..489a225e5 --- /dev/null +++ b/apps/web-tdesign/src/views/system/mail/template/modules/send-form.vue @@ -0,0 +1,108 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/menu/data.ts b/apps/web-tdesign/src/views/system/menu/data.ts new file mode 100644 index 000000000..3226f0c41 --- /dev/null +++ b/apps/web-tdesign/src/views/system/menu/data.ts @@ -0,0 +1,324 @@ +import type { Recordable } from '@vben/types'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemMenuApi } from '#/api/system/menu'; + +import { h } from 'vue'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemMenuTypeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { handleTree, isHttpUrl } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getMenuList } from '#/api/system/menu'; +import { $t } from '#/locales'; +import { componentKeys } from '#/router/routes'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级菜单', + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: async () => { + const data = await getMenuList(); + data.unshift({ + id: 0, + name: '顶级部门', + } as SystemMenuApi.Menu); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级菜单', + filterTreeNode(input: string, node: Recordable) { + if (!input || input.length === 0) { + return true; + } + const name: string = node.label ?? ''; + if (!name) return false; + return name.includes(input) || $t(name).includes(input); + }, + showSearch: true, + treeDefaultExpandedKeys: [0], + }, + rules: 'selectRequired', + renderComponentContent() { + return { + title({ label, icon }: { icon: string; label: string }) { + const components = []; + if (!label) return ''; + if (icon) { + components.push(h(IconifyIcon, { class: 'size-4', icon })); + } + components.push(h('span', { class: '' }, $t(label || ''))); + return h('div', { class: 'flex items-center gap-1' }, components); + }, + }; + }, + }, + { + fieldName: 'name', + label: '菜单名称', + component: 'Input', + componentProps: { + placeholder: '请输入菜单名称', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '菜单类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(SystemMenuTypeEnum.DIR), + }, + { + fieldName: 'icon', + label: '菜单图标', + component: 'IconPicker', + componentProps: { + placeholder: '请选择菜单图标', + prefix: 'carbon', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.DIR, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + }, + }, + { + fieldName: 'path', + label: '路由地址', + component: 'Input', + componentProps: { + placeholder: '请输入路由地址', + }, + rules: z.string(), + help: '访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头', + dependencies: { + triggerFields: ['type', 'parentId'], + show: (values) => { + return [SystemMenuTypeEnum.DIR, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + rules: (values) => { + const schema = z.string().min(1, '路由地址不能为空'); + if (isHttpUrl(values.path)) { + return schema; + } + if (values.parentId === 0) { + return schema.refine( + (path) => path.charAt(0) === '/', + '路径必须以 / 开头', + ); + } + return schema.refine( + (path) => path.charAt(0) !== '/', + '路径不能以 / 开头', + ); + }, + }, + }, + { + fieldName: 'component', + label: '组件地址', + component: 'Input', + componentProps: { + placeholder: '请输入组件地址', + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'componentName', + label: '组件名称', + component: 'AutoComplete', + componentProps: { + allowClear: true, + filterOption(input: string, option: { value: string }) { + return option.value.toLowerCase().includes(input.toLowerCase()); + }, + placeholder: '请选择组件名称', + options: componentKeys.map((v) => ({ value: v })), + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'permission', + label: '权限标识', + component: 'Input', + componentProps: { + placeholder: '请输入菜单描述', + }, + dependencies: { + show: (values) => { + return [SystemMenuTypeEnum.BUTTON, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + triggerFields: ['type'], + }, + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '菜单状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'alwaysShow', + label: '总是显示', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '总是', value: true }, + { label: '不是', value: false }, + ], + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + defaultValue: true, + help: '选择不是时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'keepAlive', + label: '缓存状态', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '缓存', value: true }, + { label: '不缓存', value: false }, + ], + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + defaultValue: true, + help: '选择缓存时,则会被 `keep-alive` 缓存,必须填写「组件名称」字段', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '菜单名称', + minWidth: 250, + align: 'left', + fixed: 'left', + slots: { default: 'name' }, + treeNode: true, + }, + { + field: 'type', + title: '菜单类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_MENU_TYPE }, + }, + }, + { + field: 'sort', + title: '显示排序', + minWidth: 100, + }, + { + field: 'permission', + title: '权限标识', + minWidth: 200, + }, + { + field: 'path', + title: '组件路径', + minWidth: 200, + }, + { + field: 'componentName', + title: '组件名称', + minWidth: 200, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/menu/index.vue b/apps/web-tdesign/src/views/system/menu/index.vue new file mode 100644 index 000000000..47dfc6c3c --- /dev/null +++ b/apps/web-tdesign/src/views/system/menu/index.vue @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + {{ $t(row.name) }} + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/menu/modules/form.vue b/apps/web-tdesign/src/views/system/menu/modules/form.vue new file mode 100644 index 000000000..1a355bf5d --- /dev/null +++ b/apps/web-tdesign/src/views/system/menu/modules/form.vue @@ -0,0 +1,83 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notice/data.ts b/apps/web-tdesign/src/views/system/notice/data.ts new file mode 100644 index 000000000..79c156c86 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notice/data.ts @@ -0,0 +1,135 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'title', + label: '公告标题', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'type', + label: '公告类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_NOTICE_TYPE, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '公告内容', + component: 'RichTextarea', + rules: 'required', + }, + { + fieldName: 'status', + label: '公告状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'title', + label: '公告标题', + component: 'Input', + componentProps: { + placeholder: '请输入公告标题', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '公告状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择公告状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '公告编号', + minWidth: 100, + }, + { + field: 'title', + title: '公告标题', + minWidth: 200, + }, + { + field: 'type', + title: '公告类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTICE_TYPE }, + }, + }, + { + field: 'status', + title: '公告状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/notice/index.vue b/apps/web-tdesign/src/views/system/notice/index.vue new file mode 100644 index 000000000..8d2258631 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notice/index.vue @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notice/modules/form.vue b/apps/web-tdesign/src/views/system/notice/modules/form.vue new file mode 100644 index 000000000..fa95fc49f --- /dev/null +++ b/apps/web-tdesign/src/views/system/notice/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/message/data.ts b/apps/web-tdesign/src/views/system/notify/message/data.ts new file mode 100644 index 000000000..c6a315229 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/message/data.ts @@ -0,0 +1,237 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'templateCode', + label: '模板编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'templateType', + label: '模版类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + allowClear: true, + placeholder: '请选择模版类型', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'templateCode', + title: '模板编码', + minWidth: 120, + }, + { + field: 'templateNickname', + title: '发送人名称', + minWidth: 180, + }, + { + field: 'templateContent', + title: '模版内容', + minWidth: 200, + }, + { + field: 'templateParams', + title: '模版参数', + minWidth: 180, + formatter: ({ cellValue }) => { + try { + return JSON.stringify(cellValue); + } catch { + return ''; + } + }, + }, + { + field: 'templateType', + title: '模版类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'readStatus', + title: '是否已读', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'readTime', + title: '阅读时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '编号', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userId', + label: '用户编号', + }, + { + field: 'templateId', + label: '模版编号', + }, + { + field: 'templateCode', + label: '模板编码', + }, + { + field: 'templateNickname', + label: '发送人名称', + }, + { + field: 'templateContent', + label: '模版内容', + }, + { + field: 'templateParams', + label: '模版参数', + render: (val) => { + try { + return JSON.stringify(val); + } catch { + return ''; + } + }, + }, + { + field: 'templateType', + label: '模版类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'readStatus', + label: '是否已读', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_BOOLEAN_STRING, + value: val, + }); + }, + }, + { + field: 'readTime', + label: '阅读时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/notify/message/index.vue b/apps/web-tdesign/src/views/system/notify/message/index.vue new file mode 100644 index 000000000..aea7b8623 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/message/index.vue @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/message/modules/detail.vue b/apps/web-tdesign/src/views/system/notify/message/modules/detail.vue new file mode 100644 index 000000000..a0e100dd4 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/message/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/my/data.ts b/apps/web-tdesign/src/views/system/notify/my/data.ts new file mode 100644 index 000000000..b31d437c2 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/my/data.ts @@ -0,0 +1,137 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'readStatus', + label: '是否已读', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + allowClear: true, + placeholder: '请选择是否已读', + }, + }, + { + fieldName: 'createTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '', + width: 40, + type: 'checkbox', + }, + { + field: 'templateNickname', + title: '发送人', + minWidth: 180, + }, + { + field: 'createTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'templateType', + title: '类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'templateContent', + title: '消息内容', + minWidth: 300, + }, + { + field: 'readStatus', + title: '是否已读', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'readTime', + title: '阅读时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'templateNickname', + label: '发送人', + }, + { + field: 'createTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'templateType', + label: '消息类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'readStatus', + label: '是否已读', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_BOOLEAN_STRING, + value: val, + }); + }, + }, + { + field: 'readTime', + label: '阅读时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'templateContent', + label: '消息内容', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/notify/my/index.vue b/apps/web-tdesign/src/views/system/notify/my/index.vue new file mode 100644 index 000000000..f9122ee3d --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/my/index.vue @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/my/modules/detail.vue b/apps/web-tdesign/src/views/system/notify/my/modules/detail.vue new file mode 100644 index 000000000..a0e100dd4 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/my/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/template/data.ts b/apps/web-tdesign/src/views/system/notify/template/data.ts new file mode 100644 index 000000000..d22b16ce9 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/template/data.ts @@ -0,0 +1,288 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE, UserTypeEnum } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'nickname', + label: '发送人名称', + component: 'Input', + componentProps: { + placeholder: '请输入发送人名称', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + placeholder: '请输入模板内容', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '模板类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + placeholder: '请选择模板类型', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + placeholder: '请选择状态', + }, + }, + { + fieldName: 'type', + label: '模板类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + allowClear: true, + placeholder: '请选择模板类型', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 发送站内信表单 */ +export function useSendNotifyFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'templateCode', + label: '模板编码', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + }, + rules: z.number().default(UserTypeEnum.MEMBER), + }, + { + fieldName: 'userId', + label: '接收人 ID', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + }, + dependencies: { + show(values) { + return values.userType === UserTypeEnum.MEMBER; + }, + triggerFields: ['userType'], + }, + rules: 'required', + }, + { + fieldName: 'userId', + label: '接收人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择接收人', + }, + dependencies: { + show(values) { + return values.userType === UserTypeEnum.ADMIN; + }, + triggerFields: ['userType'], + }, + rules: 'required', + }, + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'nickname', + title: '发送人名称', + minWidth: 120, + }, + { + field: 'content', + title: '模板内容', + minWidth: 200, + }, + { + field: 'type', + title: '模板类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/notify/template/index.vue b/apps/web-tdesign/src/views/system/notify/template/index.vue new file mode 100644 index 000000000..c34f07a2b --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/template/index.vue @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/template/modules/form.vue b/apps/web-tdesign/src/views/system/notify/template/modules/form.vue new file mode 100644 index 000000000..bbf986a2a --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/template/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/notify/template/modules/send-form.vue b/apps/web-tdesign/src/views/system/notify/template/modules/send-form.vue new file mode 100644 index 000000000..6710baff9 --- /dev/null +++ b/apps/web-tdesign/src/views/system/notify/template/modules/send-form.vue @@ -0,0 +1,111 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/oauth2/client/data.ts b/apps/web-tdesign/src/views/system/oauth2/client/data.ts new file mode 100644 index 000000000..b5bc683c0 --- /dev/null +++ b/apps/web-tdesign/src/views/system/oauth2/client/data.ts @@ -0,0 +1,263 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + }, + rules: 'required', + }, + { + fieldName: 'secret', + label: '客户端密钥', + component: 'Input', + componentProps: { + placeholder: '请输入客户端密钥', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + }, + rules: 'required', + }, + { + fieldName: 'logo', + label: '应用图标', + component: 'ImageUpload', + rules: 'required', + }, + { + fieldName: 'description', + label: '应用描述', + component: 'Textarea', + componentProps: { + placeholder: '请输入应用描述', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'accessTokenValiditySeconds', + label: '访问令牌的有效期', + component: 'InputNumber', + componentProps: { + placeholder: '请输入访问令牌的有效期,单位:秒', + min: 0, + }, + rules: 'required', + }, + { + fieldName: 'refreshTokenValiditySeconds', + label: '刷新令牌的有效期', + component: 'InputNumber', + componentProps: { + placeholder: '请输入刷新令牌的有效期,单位:秒', + min: 0, + }, + rules: 'required', + }, + { + fieldName: 'authorizedGrantTypes', + label: '授权类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE), + mode: 'multiple', + placeholder: '请输入授权类型', + }, + rules: 'required', + }, + { + fieldName: 'scopes', + label: '授权范围', + component: 'Select', + componentProps: { + placeholder: '请输入授权范围', + mode: 'tags', + allowClear: true, + }, + }, + { + fieldName: 'autoApproveScopes', + label: '自动授权范围', + component: 'Select', + componentProps: { + placeholder: '请输入自动授权范围', + mode: 'multiple', + }, + dependencies: { + triggerFields: ['scopes'], + componentProps: (values) => ({ + options: values.scopes + ? values.scopes.map((scope: string) => ({ + label: scope, + value: scope, + })) + : [], + }), + }, + }, + { + fieldName: 'redirectUris', + label: '可重定向的 URI 地址', + component: 'Select', + componentProps: { + placeholder: '请输入可重定向的 URI 地址', + mode: 'tags', + }, + rules: 'required', + }, + { + fieldName: 'authorities', + label: '权限', + component: 'Select', + componentProps: { + placeholder: '请输入权限', + mode: 'tags', + }, + }, + { + fieldName: 'resourceIds', + label: '资源', + component: 'Select', + componentProps: { + mode: 'tags', + placeholder: '请输入资源', + }, + }, + { + fieldName: 'additionalInformation', + label: '附加信息', + component: 'Textarea', + componentProps: { + placeholder: '请输入附加信息,JSON 格式数据', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + placeholder: '请输入状态', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 120, + }, + { + field: 'secret', + title: '客户端密钥', + minWidth: 120, + }, + { + field: 'name', + title: '应用名', + minWidth: 120, + }, + { + field: 'logo', + title: '应用图标', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'status', + title: '状态', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'accessTokenValiditySeconds', + title: '访问令牌的有效期', + minWidth: 150, + formatter: ({ cellValue }) => `${cellValue} 秒`, + }, + { + field: 'refreshTokenValiditySeconds', + title: '刷新令牌的有效期', + minWidth: 150, + formatter: ({ cellValue }) => `${cellValue} 秒`, + }, + { + field: 'authorizedGrantTypes', + title: '授权类型', + minWidth: 100, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/oauth2/client/index.vue b/apps/web-tdesign/src/views/system/oauth2/client/index.vue new file mode 100644 index 000000000..7c039447e --- /dev/null +++ b/apps/web-tdesign/src/views/system/oauth2/client/index.vue @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/oauth2/client/modules/form.vue b/apps/web-tdesign/src/views/system/oauth2/client/modules/form.vue new file mode 100644 index 000000000..c1174da76 --- /dev/null +++ b/apps/web-tdesign/src/views/system/oauth2/client/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/oauth2/token/data.ts b/apps/web-tdesign/src/views/system/oauth2/token/data.ts new file mode 100644 index 000000000..ab5cf8a66 --- /dev/null +++ b/apps/web-tdesign/src/views/system/oauth2/token/data.ts @@ -0,0 +1,93 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + allowClear: true, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + allowClear: true, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'accessToken', + title: '访问令牌', + minWidth: 300, + }, + { + field: 'refreshToken', + title: '刷新令牌', + minWidth: 300, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 120, + }, + { + field: 'expiresTime', + title: '过期时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/oauth2/token/index.vue b/apps/web-tdesign/src/views/system/oauth2/token/index.vue new file mode 100644 index 000000000..f08b595b8 --- /dev/null +++ b/apps/web-tdesign/src/views/system/oauth2/token/index.vue @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/operatelog/data.ts b/apps/web-tdesign/src/views/system/operatelog/data.ts new file mode 100644 index 000000000..3144daa90 --- /dev/null +++ b/apps/web-tdesign/src/views/system/operatelog/data.ts @@ -0,0 +1,191 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { formatDateTime } from '@vben/utils'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '操作人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + allowClear: true, + placeholder: '请选择操作人员', + }, + }, + { + fieldName: 'type', + label: '操作模块', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入操作模块', + }, + }, + { + fieldName: 'subType', + label: '操作名', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入操作名', + }, + }, + { + fieldName: 'action', + label: '操作内容', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入操作内容', + }, + }, + { + fieldName: 'createTime', + label: '操作时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'bizId', + label: '业务编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入业务编号', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userName', + title: '操作人', + minWidth: 120, + }, + { + field: 'type', + title: '操作模块', + minWidth: 120, + }, + { + field: 'subType', + title: '操作名', + minWidth: 160, + }, + { + field: 'action', + title: '操作内容', + minWidth: 200, + }, + { + field: 'createTime', + title: '操作时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'bizId', + title: '业务编号', + minWidth: 120, + }, + { + field: 'userIp', + title: '操作 IP', + minWidth: 120, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + show: (data) => !data?.traceId, + }, + { + field: 'userId', + label: '操作人编号', + }, + { + field: 'userName', + label: '操作人名字', + }, + { + field: 'userIp', + label: '操作人 IP', + }, + { + field: 'userAgent', + label: '操作人 UA', + }, + { + field: 'type', + label: '操作模块', + }, + { + field: 'subType', + label: '操作名', + }, + { + field: 'action', + label: '操作内容', + }, + { + field: 'extra', + label: '操作拓展参数', + show: (val) => !val, + }, + { + field: 'requestUrl', + label: '请求 URL', + render: (val, data) => { + if (data?.requestMethod && val) { + return `${data.requestMethod} ${val}`; + } + return ''; + }, + }, + { + field: 'createTime', + label: '操作时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'bizId', + label: '业务编号', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/operatelog/index.vue b/apps/web-tdesign/src/views/system/operatelog/index.vue new file mode 100644 index 000000000..eaf7b3a88 --- /dev/null +++ b/apps/web-tdesign/src/views/system/operatelog/index.vue @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/operatelog/modules/detail.vue b/apps/web-tdesign/src/views/system/operatelog/modules/detail.vue new file mode 100644 index 000000000..5ada4818a --- /dev/null +++ b/apps/web-tdesign/src/views/system/operatelog/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/post/data.ts b/apps/web-tdesign/src/views/system/post/data.ts new file mode 100644 index 000000000..43e746522 --- /dev/null +++ b/apps/web-tdesign/src/views/system/post/data.ts @@ -0,0 +1,145 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '岗位名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'code', + label: '岗位编码', + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '岗位状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '岗位备注', + component: 'Textarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '岗位名称', + component: 'Input', + componentProps: { + placeholder: '请输入岗位名称', + allowClear: true, + }, + }, + { + fieldName: 'code', + label: '岗位编码', + component: 'Input', + componentProps: { + placeholder: '请输入岗位编码', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '岗位状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择岗位状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '岗位编号', + minWidth: 200, + }, + { + field: 'name', + title: '岗位名称', + minWidth: 200, + }, + { + field: 'code', + title: '岗位编码', + minWidth: 200, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'remark', + title: '岗位备注', + minWidth: 200, + }, + { + field: 'status', + title: '岗位状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/post/index.vue b/apps/web-tdesign/src/views/system/post/index.vue new file mode 100644 index 000000000..50abc48ee --- /dev/null +++ b/apps/web-tdesign/src/views/system/post/index.vue @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/post/modules/form.vue b/apps/web-tdesign/src/views/system/post/modules/form.vue new file mode 100644 index 000000000..04ee9198c --- /dev/null +++ b/apps/web-tdesign/src/views/system/post/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/role/data.ts b/apps/web-tdesign/src/views/system/role/data.ts new file mode 100644 index 000000000..d9da8fa90 --- /dev/null +++ b/apps/web-tdesign/src/views/system/role/data.ts @@ -0,0 +1,255 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemDataScopeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '角色状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '角色备注', + component: 'Textarea', + }, + ]; +} + +/** 分配数据权限的表单 */ +export function useAssignDataPermissionFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + component: 'Input', + fieldName: 'code', + label: '角色标识', + componentProps: { + disabled: true, + }, + }, + { + component: 'Select', + fieldName: 'dataScope', + label: '权限范围', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE, 'number'), + }, + }, + { + fieldName: 'dataScopeDeptIds', + label: '部门范围', + component: 'Input', + formItemClass: 'items-start', + dependencies: { + triggerFields: ['dataScope'], + show: (values) => { + return values.dataScope === SystemDataScopeEnum.DEPT_CUSTOM; + }, + }, + }, + ]; +} + +/** 分配菜单的表单 */ +export function useAssignMenuFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'menuIds', + label: '菜单权限', + component: 'Input', + formItemClass: 'items-start', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + placeholder: '请输入角色名称', + allowClear: true, + }, + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + componentProps: { + placeholder: '请输入角色标识', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '角色状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择角色状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '角色编号', + minWidth: 100, + }, + { + field: 'name', + title: '角色名称', + minWidth: 200, + }, + { + field: 'type', + title: '角色类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_ROLE_TYPE }, + }, + }, + { + field: 'code', + title: '角色标识', + minWidth: 200, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'remark', + title: '角色备注', + minWidth: 100, + }, + { + field: 'status', + title: '角色状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/role/index.vue b/apps/web-tdesign/src/views/system/role/index.vue new file mode 100644 index 000000000..078e52317 --- /dev/null +++ b/apps/web-tdesign/src/views/system/role/index.vue @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/role/modules/assign-data-permission-form.vue b/apps/web-tdesign/src/views/system/role/modules/assign-data-permission-form.vue new file mode 100644 index 000000000..931a458f9 --- /dev/null +++ b/apps/web-tdesign/src/views/system/role/modules/assign-data-permission-form.vue @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + 全选 + + + 全部展开 + + + 父子联动 + + + + + diff --git a/apps/web-tdesign/src/views/system/role/modules/assign-menu-form.vue b/apps/web-tdesign/src/views/system/role/modules/assign-menu-form.vue new file mode 100644 index 000000000..3f0fb0a92 --- /dev/null +++ b/apps/web-tdesign/src/views/system/role/modules/assign-menu-form.vue @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + 全选 + + + 全部展开 + + + + + diff --git a/apps/web-tdesign/src/views/system/role/modules/form.vue b/apps/web-tdesign/src/views/system/role/modules/form.vue new file mode 100644 index 000000000..553517bc5 --- /dev/null +++ b/apps/web-tdesign/src/views/system/role/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/channel/data.ts b/apps/web-tdesign/src/views/system/sms/channel/data.ts new file mode 100644 index 000000000..3320291c2 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/channel/data.ts @@ -0,0 +1,195 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'signature', + label: '短信签名', + component: 'Input', + componentProps: { + placeholder: '请输入短信签名', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '渠道编码', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'), + placeholder: '请选择短信渠道', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '启用状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + { + fieldName: 'apiKey', + label: '短信 API 的账号', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的账号', + }, + rules: 'required', + }, + { + fieldName: 'apiSecret', + label: '短信 API 的密钥', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的密钥', + }, + }, + { + fieldName: 'callbackUrl', + label: '短信发送回调 URL', + component: 'Input', + componentProps: { + placeholder: '请输入短信发送回调 URL', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'signature', + label: '短信签名', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入短信签名', + }, + }, + { + fieldName: 'code', + label: '渠道编码', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'), + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'signature', + title: '短信签名', + minWidth: 120, + }, + { + field: 'code', + title: '渠道编码', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'status', + title: '启用状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'apiKey', + title: '短信 API 的账号', + minWidth: 180, + }, + { + field: 'apiSecret', + title: '短信 API 的密钥', + minWidth: 180, + }, + { + field: 'callbackUrl', + title: '短信发送回调 URL', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/sms/channel/index.vue b/apps/web-tdesign/src/views/system/sms/channel/index.vue new file mode 100644 index 000000000..97df50025 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/channel/index.vue @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/channel/modules/form.vue b/apps/web-tdesign/src/views/system/sms/channel/modules/form.vue new file mode 100644 index 000000000..bf639e2d7 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/channel/modules/form.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/log/data.ts b/apps/web-tdesign/src/views/system/sms/log/data.ts new file mode 100644 index 000000000..3babce416 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/log/data.ts @@ -0,0 +1,265 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getSimpleSmsChannelList } from '#/api/system/sms/channel'; +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入手机号', + }, + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + allowClear: true, + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'templateId', + label: '模板编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编号', + }, + }, + { + fieldName: 'sendStatus', + label: '发送状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_SEND_STATUS, 'number'), + allowClear: true, + placeholder: '请选择发送状态', + }, + }, + { + fieldName: 'sendTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'receiveStatus', + label: '接收状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS, 'number'), + allowClear: true, + placeholder: '请选择接收状态', + }, + }, + { + fieldName: 'receiveTime', + label: '接收时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'mobile', + title: '手机号', + minWidth: 120, + }, + { + field: 'templateContent', + title: '短信内容', + minWidth: 300, + }, + { + field: 'sendStatus', + title: '发送状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_SEND_STATUS }, + }, + }, + { + field: 'sendTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'receiveStatus', + title: '接收状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS }, + }, + }, + { + field: 'receiveTime', + title: '接收时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'channelCode', + title: '短信渠道', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'templateId', + title: '模板编号', + minWidth: 100, + }, + { + field: 'templateType', + title: '短信类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'mobile', + label: '手机号', + }, + { + field: 'channelCode', + label: '短信渠道', + }, + { + field: 'templateId', + label: '模板编号', + }, + { + field: 'templateType', + label: '模板类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'templateContent', + label: '短信内容', + }, + { + field: 'sendStatus', + label: '发送状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_SEND_STATUS, + value: val, + }); + }, + }, + { + field: 'sendTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'apiSendCode', + label: 'API 发送编码', + }, + { + field: 'apiSendMsg', + label: 'API 发送消息', + }, + { + field: 'receiveStatus', + label: '接收状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS, + value: val, + }); + }, + }, + { + field: 'receiveTime', + label: '接收时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'apiReceiveCode', + label: 'API 接收编码', + }, + { + field: 'apiReceiveMsg', + label: 'API 接收消息', + span: 2, + }, + { + field: 'apiRequestId', + label: 'API 请求 ID', + }, + { + field: 'apiSerialNo', + label: 'API 序列号', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/sms/log/index.vue b/apps/web-tdesign/src/views/system/sms/log/index.vue new file mode 100644 index 000000000..6735fde57 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/log/index.vue @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/log/modules/detail.vue b/apps/web-tdesign/src/views/system/sms/log/modules/detail.vue new file mode 100644 index 000000000..2831f6ff5 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/log/modules/detail.vue @@ -0,0 +1,50 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/template/data.ts b/apps/web-tdesign/src/views/system/sms/template/data.ts new file mode 100644 index 000000000..13786c56a --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/template/data.ts @@ -0,0 +1,274 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleSmsChannelList } from '#/api/system/sms/channel'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'type', + label: '短信类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, 'number'), + placeholder: '请选择短信类型', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + placeholder: '请选择短信渠道', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + placeholder: '请输入模板内容', + rows: 4, + }, + rules: 'required', + }, + { + fieldName: 'apiTemplateId', + label: '短信 API 的模板编号', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的模板编号', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'type', + label: '短信类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, 'number'), + allowClear: true, + placeholder: '请选择短信类型', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + placeholder: '请选择开启状态', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + allowClear: true, + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 发送短信表单 */ +export function useSendSmsFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + rules: 'required', + }, + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'type', + title: '短信类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE }, + }, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'content', + title: '模板内容', + minWidth: 200, + }, + { + field: 'status', + title: '开启状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'apiTemplateId', + title: '短信 API 的模板编号', + minWidth: 180, + }, + { + field: 'channelCode', + title: '短信渠道', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/sms/template/index.vue b/apps/web-tdesign/src/views/system/sms/template/index.vue new file mode 100644 index 000000000..e2bb00ad8 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/template/index.vue @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/template/modules/form.vue b/apps/web-tdesign/src/views/system/sms/template/modules/form.vue new file mode 100644 index 000000000..74d894f23 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/template/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/sms/template/modules/send-form.vue b/apps/web-tdesign/src/views/system/sms/template/modules/send-form.vue new file mode 100644 index 000000000..839b44477 --- /dev/null +++ b/apps/web-tdesign/src/views/system/sms/template/modules/send-form.vue @@ -0,0 +1,108 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/social/client/data.ts b/apps/web-tdesign/src/views/system/social/client/data.ts new file mode 100644 index 000000000..bbcd3c18b --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/client/data.ts @@ -0,0 +1,211 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemUserSocialTypeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + }, + rules: 'required', + }, + { + fieldName: 'socialType', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: 'required', + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号,对应各平台的 appKey', + }, + rules: 'required', + }, + { + fieldName: 'clientSecret', + label: '客户端密钥', + component: 'Input', + componentProps: { + placeholder: '请输入客户端密钥,对应各平台的 appSecret', + }, + rules: 'required', + }, + { + fieldName: 'agentId', + label: 'agentId', + component: 'Input', + componentProps: { + placeholder: '授权方的网页应用 ID,有则填', + }, + dependencies: { + triggerFields: ['socialType'], + show: (values) => + values.socialType === SystemUserSocialTypeEnum.WECHAT_ENTERPRISE.type, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + allowClear: true, + }, + }, + { + fieldName: 'socialType', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + placeholder: '请选择社交平台', + allowClear: true, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + allowClear: true, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '应用名', + minWidth: 120, + }, + { + field: 'socialType', + title: '社交平台', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SOCIAL_TYPE }, + }, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/social/client/index.vue b/apps/web-tdesign/src/views/system/social/client/index.vue new file mode 100644 index 000000000..9cf69805f --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/client/index.vue @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/social/client/modules/form.vue b/apps/web-tdesign/src/views/system/social/client/modules/form.vue new file mode 100644 index 000000000..647e5806d --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/client/modules/form.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/social/user/data.ts b/apps/web-tdesign/src/views/system/social/user/data.ts new file mode 100644 index 000000000..afc0b537b --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/user/data.ts @@ -0,0 +1,152 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { Image } from 'tdesign-vue-next'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'type', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + placeholder: '请选择社交平台', + allowClear: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + allowClear: true, + }, + }, + { + fieldName: 'openid', + label: '社交 openid', + component: 'Input', + componentProps: { + placeholder: '请输入社交 openid', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'type', + title: '社交平台', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SOCIAL_TYPE }, + }, + }, + { + field: 'openid', + title: '社交 openid', + minWidth: 180, + }, + { + field: 'nickname', + title: '用户昵称', + minWidth: 120, + }, + { + field: 'avatar', + title: '用户头像', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'type', + label: '社交平台', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SOCIAL_TYPE, + value: val, + }); + }, + }, + { + field: 'nickname', + label: '用户昵称', + }, + { + field: 'avatar', + label: '用户头像', + render: (val) => (val ? h(Image, { src: val }) : '无'), + }, + { + field: 'token', + label: '社交 token', + }, + { + field: 'rawTokenInfo', + label: '原始 Token 数据', + }, + { + field: 'rawUserInfo', + label: '原始 User 数据', + }, + { + field: 'code', + label: '最后一次的认证 code', + }, + { + field: 'state', + label: '最后一次的认证 state', + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/social/user/index.vue b/apps/web-tdesign/src/views/system/social/user/index.vue new file mode 100644 index 000000000..1f5a3aec3 --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/user/index.vue @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/social/user/modules/detail.vue b/apps/web-tdesign/src/views/system/social/user/modules/detail.vue new file mode 100644 index 000000000..de2b35cbf --- /dev/null +++ b/apps/web-tdesign/src/views/system/social/user/modules/detail.vue @@ -0,0 +1,53 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/tenant/data.ts b/apps/web-tdesign/src/views/system/tenant/data.ts new file mode 100644 index 000000000..d2a7dbf70 --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenant/data.ts @@ -0,0 +1,242 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemTenantPackageApi } from '#/api/system/tenant-package'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getTenantPackageList } from '#/api/system/tenant-package'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let tenantPackageList: SystemTenantPackageApi.TenantPackage[] = []; +getTenantPackageList().then((data) => (tenantPackageList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '租户名称', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'packageId', + label: '租户套餐', + component: 'ApiSelect', + componentProps: { + api: getTenantPackageList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择租户套餐', + }, + rules: 'required', + }, + { + fieldName: 'contactName', + label: '联系人', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'contactMobile', + label: '联系手机', + component: 'Input', + rules: 'mobile', + }, + { + label: '用户名称', + fieldName: 'username', + component: 'Input', + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + label: '用户密码', + fieldName: 'password', + component: 'InputPassword', + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + label: '账号额度', + fieldName: 'accountCount', + component: 'InputNumber', + rules: 'required', + }, + { + label: '过期时间', + fieldName: 'expireTime', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择过期时间', + }, + rules: 'required', + }, + { + label: '绑定域名', + fieldName: 'websites', + component: 'Select', + componentProps: { + placeholder: '请输入绑定域名', + mode: 'tags', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '租户状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '租户名', + component: 'Input', + componentProps: { + placeholder: '请输入租户名', + allowClear: true, + }, + }, + { + fieldName: 'contactName', + label: '联系人', + component: 'Input', + componentProps: { + placeholder: '请输入联系人', + allowClear: true, + }, + }, + { + fieldName: 'contactMobile', + label: '联系手机', + component: 'Input', + componentProps: { + placeholder: '请输入联系手机', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '租户编号', + minWidth: 100, + }, + { + field: 'name', + title: '租户名', + minWidth: 180, + }, + { + field: 'packageId', + title: '租户套餐', + minWidth: 180, + formatter: ({ cellValue }) => { + return cellValue === 0 + ? '系统租户' + : tenantPackageList.find((pkg) => pkg.id === cellValue)?.name || '-'; + }, + }, + { + field: 'contactName', + title: '联系人', + minWidth: 100, + }, + { + field: 'contactMobile', + title: '联系手机', + minWidth: 180, + }, + { + field: 'accountCount', + title: '账号额度', + minWidth: 100, + }, + { + field: 'expireTime', + title: '过期时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'websites', + title: '绑定域名', + minWidth: 180, + }, + { + field: 'status', + title: '租户状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/tenant/index.vue b/apps/web-tdesign/src/views/system/tenant/index.vue new file mode 100644 index 000000000..6816ff422 --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenant/index.vue @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/tenant/modules/form.vue b/apps/web-tdesign/src/views/system/tenant/modules/form.vue new file mode 100644 index 000000000..a9e152151 --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenant/modules/form.vue @@ -0,0 +1,79 @@ + + + + + + diff --git a/apps/web-tdesign/src/views/system/tenantPackage/data.ts b/apps/web-tdesign/src/views/system/tenantPackage/data.ts new file mode 100644 index 000000000..7ac4e2899 --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenantPackage/data.ts @@ -0,0 +1,127 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'menuIds', + label: '菜单权限', + component: 'Input', + formItemClass: 'items-start', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入套餐名称', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + placeholder: '请选择状态', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '套餐编号', + minWidth: 100, + }, + { + field: 'name', + title: '套餐名称', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/tenantPackage/index.vue b/apps/web-tdesign/src/views/system/tenantPackage/index.vue new file mode 100644 index 000000000..17f349100 --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenantPackage/index.vue @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/tenantPackage/modules/form.vue b/apps/web-tdesign/src/views/system/tenantPackage/modules/form.vue new file mode 100644 index 000000000..f1fb2afec --- /dev/null +++ b/apps/web-tdesign/src/views/system/tenantPackage/modules/form.vue @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + 全选 + + + 全部展开 + + + + + diff --git a/apps/web-tdesign/src/views/system/user/data.ts b/apps/web-tdesign/src/views/system/user/data.ts new file mode 100644 index 000000000..fca5ddefd --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/data.ts @@ -0,0 +1,344 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { $t } from '@vben/locales'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDeptList } from '#/api/system/dept'; +import { getSimplePostList } from '#/api/system/post'; +import { getSimpleRoleList } from '#/api/system/role'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + rules: 'required', + }, + { + label: '用户密码', + fieldName: 'password', + component: 'InputPassword', + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + rules: 'required', + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择归属部门', + treeDefaultExpandAll: true, + }, + }, + { + fieldName: 'postIds', + label: '岗位', + component: 'ApiSelect', + componentProps: { + api: getSimplePostList, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + placeholder: '请选择岗位', + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + rules: z.string().email('邮箱格式不正确').or(z.literal('')).optional(), + componentProps: { + placeholder: '请输入邮箱', + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + }, + { + fieldName: 'sex', + label: '用户性别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(1), + }, + { + fieldName: 'status', + label: '用户状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + }, + ]; +} + +/** 重置密码的表单 */ +export function useResetPasswordFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'VbenInputPassword', + componentProps: { + passwordStrength: true, + placeholder: '请输入新密码', + }, + dependencies: { + rules(values) { + return z + .string({ message: '请输入新密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value !== values.oldPassword, + '新旧密码不能相同', + ); + }, + triggerFields: ['newPassword', 'oldPassword'], + }, + fieldName: 'newPassword', + label: '新密码', + rules: 'required', + }, + { + component: 'VbenInputPassword', + componentProps: { + passwordStrength: true, + placeholder: $t('authentication.confirmPassword'), + }, + dependencies: { + rules(values) { + return z + .string({ message: '请输入确认密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value === values.newPassword, + '新密码和确认密码不一致', + ); + }, + triggerFields: ['newPassword', 'confirmPassword'], + }, + fieldName: 'confirmPassword', + label: '确认密码', + rules: 'required', + }, + ]; +} + +/** 分配角色的表单 */ +export function useAssignRoleFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'roleIds', + label: '角色', + component: 'ApiSelect', + componentProps: { + api: getSimpleRoleList, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + placeholder: '请选择角色', + }, + }, + ]; +} + +/** 用户导入的表单 */ +export function useImportFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'file', + label: '用户数据', + component: 'Upload', + rules: 'required', + help: '仅允许导入 xls、xlsx 格式文件', + }, + { + fieldName: 'updateSupport', + label: '是否覆盖', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(false), + help: '是否更新已经存在的用户数据', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + placeholder: '请输入用户名称', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: SystemUserApi.User, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '用户编号', + minWidth: 100, + }, + { + field: 'username', + title: '用户名称', + minWidth: 120, + }, + { + field: 'nickname', + title: '用户昵称', + minWidth: 120, + }, + { + field: 'deptName', + title: '部门', + minWidth: 120, + }, + { + field: 'mobile', + title: '手机号码', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + align: 'center', + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + checkedValue: CommonStatusEnum.ENABLE, + unCheckedValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-tdesign/src/views/system/user/index.vue b/apps/web-tdesign/src/views/system/user/index.vue new file mode 100644 index 000000000..c69b9235d --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/index.vue @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web-tdesign/src/views/system/user/modules/assign-role-form.vue b/apps/web-tdesign/src/views/system/user/modules/assign-role-form.vue new file mode 100644 index 000000000..80dbd8487 --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/modules/assign-role-form.vue @@ -0,0 +1,77 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/user/modules/dept-tree.vue b/apps/web-tdesign/src/views/system/user/modules/dept-tree.vue new file mode 100644 index 000000000..d5ae8da78 --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/modules/dept-tree.vue @@ -0,0 +1,81 @@ + + + + + + + + + + + + + 暂无数据 + + + + diff --git a/apps/web-tdesign/src/views/system/user/modules/form.vue b/apps/web-tdesign/src/views/system/user/modules/form.vue new file mode 100644 index 000000000..2576cbed2 --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/modules/form.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/apps/web-tdesign/src/views/system/user/modules/import-form.vue b/apps/web-tdesign/src/views/system/user/modules/import-form.vue new file mode 100644 index 000000000..ebc69cd93 --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/modules/import-form.vue @@ -0,0 +1,82 @@ + + + + + + + + + 选择 Excel 文件 + + + + + + + 下载导入模板 + + + + diff --git a/apps/web-tdesign/src/views/system/user/modules/reset-password-form.vue b/apps/web-tdesign/src/views/system/user/modules/reset-password-form.vue new file mode 100644 index 000000000..22b2e4243 --- /dev/null +++ b/apps/web-tdesign/src/views/system/user/modules/reset-password-form.vue @@ -0,0 +1,65 @@ + + + + + + +
+ +
点击或拖拽文件到此区域上传
+ 支持{{ accept.join('/') }}格式文件,不超过{{ maxSize }}MB +
+ 支持 .jpg、.png、.gif、.webp 格式图片文件 +