feat: support vscode i18n-ally plugin
| | |
| | | "stylelint.vscode-stylelint", |
| | | "DavidAnson.vscode-markdownlint", |
| | | "esbenp.prettier-vscode", |
| | | "mrmlnc.vscode-scss", |
| | | "mrmlnc.vscode-less", |
| | | "antfu.i18n-ally", |
| | | "cpylua.language-postcss", |
| | | "Orta.vscode-jest", |
| | | "antfu.iconify", |
| | |
| | | "editor.smoothScrolling": true, |
| | | "editor.cursorBlinking": "phase", |
| | | "editor.cursorSmoothCaretAnimation": true, |
| | | "editor.detectIndentation": false, // vscode默认启用了根据文件类型自动设置tabsize的选项 |
| | | "editor.detectIndentation": false, |
| | | "diffEditor.ignoreTrimWhitespace": false, |
| | | "editor.formatOnPaste": true, //自动格式化粘贴的内容 |
| | | "editor.formatOnSave": true, //保存自动格式化 |
| | | "editor.formatOnPaste": true, |
| | | "editor.formatOnSave": true, |
| | | "editor.suggestSelection": "first", |
| | | "editor.trimAutoWhitespace": true, |
| | | "editor.quickSuggestions": { |
| | | // 快速提示 |
| | | "other": true, |
| | | "comments": true, |
| | | "strings": true |
| | |
| | | //=========================================== |
| | | //============= Other ======================= |
| | | //=========================================== |
| | | "breadcrumbs.enabled": true, // 启用/禁用导航路径 |
| | | "open-in-browser.default": "chrome", // 默认浏览器 |
| | | "breadcrumbs.enabled": true, |
| | | "open-in-browser.default": "chrome", |
| | | //=========================================== |
| | | //============= emmet ======================= |
| | | //=========================================== |
| | | "emmet.triggerExpansionOnTab": true, // 配置emmet是否启用tab展开缩写 |
| | | "emmet.triggerExpansionOnTab": true, |
| | | "emmet.showAbbreviationSuggestions": true, |
| | | "emmet.showExpandedAbbreviation": "always", |
| | | "emmet.syntaxProfiles": { |
| | | // 配置emmet对文件类型的支持,比如vue后缀文件按照html文件来进行emmet扩写 |
| | | "vue-html": "html", |
| | | "vue": "html", |
| | | "javascript": "javascriptreact", |
| | | // xml类型文件默认都是单引号,开启对非单引号的emmet识别 |
| | | "xml": { |
| | | "attr_quotes": "single" |
| | | } |
| | | }, |
| | | "emmet.includeLanguages": { |
| | | // 在react的jsx中添加对emmet的支持 |
| | | "jsx-sublime-babel-tags": "javascriptreact" |
| | | }, |
| | | //=========================================== |
| | | //============= files ======================= |
| | | //=========================================== |
| | | // "files.autoSave": "onWindowChange", // 窗口失去焦点自动保存 |
| | | // "files.autoSaveDelay": 1000, // 自动保存时间 |
| | | "files.trimTrailingWhitespace": true, // 启用后,将在保存文件时剪裁尾随空格。 |
| | | // 文件末尾插入新行 |
| | | "files.trimTrailingWhitespace": true, |
| | | "files.insertFinalNewline": true, |
| | | // 删除文件末尾多余的新行 |
| | | "files.trimFinalNewlines": true, |
| | | "files.eol": "\n", |
| | | "search.exclude": { |
| | | // 搜索排除这些区域 |
| | | "**/node_modules": true, |
| | | "**/*.log": true, |
| | | "**/*.log*": true, |
| | |
| | | "**/tmp": true |
| | | }, |
| | | "files.exclude": { |
| | | // 排除文件搜索区域,比如node_modules(默认设置已经屏蔽了) |
| | | // "**/node_modules": true, |
| | | "**/bower_components": true, |
| | | "**/.idea": true, |
| | | "**/yarn.lock": true, |
| | |
| | | "**/yarn.lock": true |
| | | }, |
| | | "files.associations": { |
| | | // 配置文件关联,以便启用对应的智能提示,比如wxss使用css |
| | | "*.vue": "vue", |
| | | "*.wxss": "css" |
| | | }, |
| | |
| | | "css.validate": true, |
| | | "less.validate": true, |
| | | "scss.validate": true, |
| | | // ↓↓↓↓↓↓↓↓↓↓↓↓↓ 以下为插件设置 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ |
| | | // ↓↓↓↓↓↓↓↓↓↓↓↓↓ 需要安装对应的插件 ↓↓↓↓↓↓↓↓↓↓↓↓ |
| | | // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ |
| | | // =========================================== |
| | | // ================ Eslint =================== |
| | | // =========================================== |
| | | "eslint.enable": true, // 是否开启eslint |
| | | "eslint.enable": true, |
| | | "eslint.options": { |
| | | // 配置 |
| | | "plugins": [ |
| | |
| | | "typescript" |
| | | ] |
| | | }, |
| | | "eslint.autoFixOnSave": true, // 保存自动格式化 |
| | | "eslint.autoFixOnSave": true, |
| | | // =========================================== |
| | | // ================ Vetur ==================== |
| | | // =========================================== |
| | | "vetur.experimental.templateInterpolationService": true, |
| | | "vetur.format.options.tabSize": 2, |
| | | "vetur.format.defaultFormatter.html": "js-beautify-html", // 使用js-beautify-html格式化 |
| | | "vetur.format.defaultFormatter.scss": "prettier", // 使用js-beautify-html格式化 |
| | | "vetur.format.defaultFormatter.css": "prettier", // 使用js-beautify-html格式化 |
| | | // "vetur.format.defaultFormatter.html": "prettyhtml", |
| | | "vetur.format.defaultFormatter.html": "js-beautify-html", |
| | | "vetur.format.defaultFormatter.scss": "prettier", |
| | | "vetur.format.defaultFormatter.css": "prettier", |
| | | "vetur.format.defaultFormatter.ts": "prettier-tslint", |
| | | "vetur.format.defaultFormatter.js": "prettier", |
| | | "vetur.languageFeatures.codeActions": false, |
| | | // "vetur.useWorkspaceDependencies": true, |
| | | "vetur.languageFeatures.codeActions": false, |
| | | "vetur.format.defaultFormatterOptions": { |
| | | "js-beautify-html": { |
| | | // "wrap_attributes": "force-aligned", // 单行 |
| | | "wrap_attributes": "force-expand-multiline" // 属性强制折行对齐 多行 |
| | | "wrap_attributes": "force-expand-multiline" |
| | | }, |
| | | "prettier": { |
| | | "eslintIntegration": true, // 让perttier使用eslint的格式进行检查 |
| | | "arrowParens": "always", // 箭头函数参数括号 默认avoid 可选 avoid | always |
| | | "semi": false, // 使用分号, 默认true |
| | | "singleQuote": true // 使用单引号, 默认false(在jsx中配置无效, 默认都是双引号) |
| | | "eslintIntegration": true, |
| | | "arrowParens": "always", |
| | | "semi": false, |
| | | "singleQuote": true |
| | | } |
| | | }, |
| | | // 函数注释 |
| | | //=========================================== |
| | | //============= Code Runner ================= |
| | | //=========================================== |
| | | "javascript.updateImportsOnFileMove.enabled": "never", |
| | | "liveServer.settings.donotShowInfoMsg": true, |
| | | "[javascript]": { |
| | | "editor.defaultFormatter": "esbenp.prettier-vscode" |
| | | }, |
| | | "terminal.integrated.rendererType": "dom", //关闭liveserver提示 |
| | | "terminal.integrated.rendererType": "dom", |
| | | "telemetry.enableCrashReporter": false, |
| | | "telemetry.enableTelemetry": false, |
| | | "workbench.settings.enableNaturalLanguageSearch": false, |
| | | // 引用路径设置 |
| | | "path-intellisense.mappings": { |
| | | "/@/": "${workspaceRoot}/src" |
| | | }, |
| | | "prettier.requireConfig": true, |
| | | "typescript.updateImportsOnFileMove.enabled": "always", |
| | | "workbench.sideBar.location": "left", |
| | | |
| | | "[javascriptreact]": { |
| | | "editor.defaultFormatter": "esbenp.prettier-vscode" |
| | | }, |
| | |
| | | "[markdown]": { |
| | | "editor.defaultFormatter": "esbenp.prettier-vscode" |
| | | }, |
| | | |
| | | "editor.codeActionsOnSave": { |
| | | "source.fixAll.eslint": true |
| | | } |
| | | } |
| | | }, |
| | | "i18n-ally.localesPaths": [ |
| | | "src/locales/lang", |
| | | ], |
| | | "i18n-ally.keystyle": "nested", |
| | | "i18n-ally.sortKeys": true, |
| | | "i18n-ally.namespace": true, |
| | | "i18n-ally.pathMatcher":"{locale}/{namespaces}.{ext}", |
| | | "i18n-ally.enabledParsers": [ |
| | | "ts" |
| | | ], |
| | | "i18n-ally.sourceLanguage": "zh" |
| | | } |
| | |
| | | ### ✨ Features |
| | | |
| | | - 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例 |
| | | - i18n 支持 vscode `i18n-ally`插件 |
| | | |
| | | ### 🎫 Chores |
| | | |
| | |
| | | const visibleRef = ref(false); |
| | | const propsRef = ref<Partial<Nullable<DrawerProps>>>(null); |
| | | |
| | | const { t } = useI18n('component.drawer'); |
| | | const { t } = useI18n(); |
| | | |
| | | const getMergeProps = computed( |
| | | (): DrawerProps => { |
| | |
| | | default: () => ( |
| | | <> |
| | | <div ref={scrollRef} style={unref(getScrollContentStyle)}> |
| | | <Loading absolute tip={t('loadingText')} loading={unref(getLoading)} /> |
| | | <Loading |
| | | absolute |
| | | tip={t('component.drawer.loadingText')} |
| | | loading={unref(getLoading)} |
| | | /> |
| | | {getSlot(slots)} |
| | | </div> |
| | | {renderFooter()} |
| | |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | const { t } = useI18n('component.drawer'); |
| | | const { t } = useI18n(); |
| | | |
| | | export const footerProps = { |
| | | confirmLoading: propTypes.bool, |
| | |
| | | */ |
| | | showCancelBtn: propTypes.bool.def(true), |
| | | cancelButtonProps: Object as PropType<any>, |
| | | cancelText: propTypes.string.def(t('cancelText')), |
| | | cancelText: propTypes.string.def(t('component.drawer.cancelText')), |
| | | /** |
| | | * @description: Show confirmation button |
| | | */ |
| | | showOkBtn: propTypes.bool.def(true), |
| | | okButtonProps: propTypes.any, |
| | | okText: propTypes.string.def(t('okText')), |
| | | okText: propTypes.string.def(t('component.drawer.okText')), |
| | | okType: propTypes.string.def('primary'), |
| | | showFooter: propTypes.bool, |
| | | footerHeight: { |
| | |
| | | <template> |
| | | <BasicModal |
| | | v-bind="$attrs" |
| | | :title="t('exportModalTitle')" |
| | | :title="t('component.excel.exportModalTitle')" |
| | | @ok="handleOk" |
| | | @register="registerModal" |
| | | > |
| | |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('component.excel'); |
| | | const { t } = useI18n(); |
| | | |
| | | const schemas: FormSchema[] = [ |
| | | { |
| | | field: 'filename', |
| | | component: 'Input', |
| | | label: t('fileName'), |
| | | label: t('component.excel.fileName'), |
| | | rules: [{ required: true }], |
| | | }, |
| | | { |
| | | field: 'bookType', |
| | | component: 'Select', |
| | | label: t('fileType'), |
| | | label: t('component.excel.fileType'), |
| | | defaultValue: 'xlsx', |
| | | rules: [{ required: true }], |
| | | componentProps: { |
| | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | |
| | | const { t } = useI18n('component.form'); |
| | | const { t } = useI18n(); |
| | | |
| | | export default defineComponent({ |
| | | name: 'BasicFormAction', |
| | |
| | | setup(props, { slots, emit }) { |
| | | const getResetBtnOptionsRef = computed(() => { |
| | | return { |
| | | text: t('resetButton'), |
| | | text: t('component.form.resetButton'), |
| | | ...props.resetButtonOptions, |
| | | }; |
| | | }); |
| | | |
| | | const getSubmitBtnOptionsRef = computed(() => { |
| | | return { |
| | | text: t('submitButton'), |
| | | text: t('component.form.submitButton'), |
| | | // htmlType: 'submit', |
| | | ...props.submitButtonOptions, |
| | | }; |
| | |
| | | <Button type="default" class="mr-2" onClick={toggleAdvanced}> |
| | | {() => ( |
| | | <> |
| | | {isAdvanced ? t('putAway') : t('unfold')} |
| | | {isAdvanced ? t('component.form.putAway') : t('component.form.unfold')} |
| | | <BasicArrow expand={!isAdvanced} top /> |
| | | </> |
| | | )} |
| | |
| | | }, |
| | | }, |
| | | setup(props, { slots }) { |
| | | const { t } = useI18n('component.form'); |
| | | const { t } = useI18n(); |
| | | // @ts-ignore |
| | | const itemLabelWidthRef = useItemLabelWidth(toRef(props, 'schema'), toRef(props, 'formProps')); |
| | | |
| | |
| | | const characterInx = rules.findIndex((val) => val.max); |
| | | if (characterInx !== -1 && !rules[characterInx].validator) { |
| | | rules[characterInx].message = |
| | | rules[characterInx].message || t('maxTip', [rules[characterInx].max]); |
| | | rules[characterInx].message || t('component.form.maxTip', [rules[characterInx].max]); |
| | | } |
| | | return rules; |
| | | } |
| | |
| | | import type { ComponentType } from './types/index'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('component.form'); |
| | | const { t } = useI18n(); |
| | | |
| | | /** |
| | | * @description: 生成placeholder |
| | | */ |
| | | export function createPlaceholderMessage(component: ComponentType) { |
| | | if (component.includes('Input') || component.includes('Complete')) { |
| | | return t('input'); |
| | | return t('component.form.input'); |
| | | } |
| | | if (component.includes('Picker')) { |
| | | return t('choose'); |
| | | return t('component.form.choose'); |
| | | } |
| | | if ( |
| | | component.includes('Select') || |
| | |
| | | component.includes('Switch') |
| | | ) { |
| | | // return `请选择${label}`; |
| | | return t('choose'); |
| | | return t('component.form.choose'); |
| | | } |
| | | return ''; |
| | | } |
| | |
| | | <template> |
| | | <section class="menu-search-input" @Click="handleClick" :class="searchClass"> |
| | | <a-input-search |
| | | :placeholder="t('search')" |
| | | :placeholder="t('component.menu.search')" |
| | | class="menu-search-input__search" |
| | | allowClear |
| | | @change="handleChange" |
| | |
| | | }, |
| | | }, |
| | | setup(props, { emit }) { |
| | | const { t } = useI18n('component.menu'); |
| | | const { t } = useI18n(); |
| | | |
| | | const [debounceEmitChange] = useDebounce(emitChange, 200); |
| | | |
| | |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | const { t } = useI18n('component.modal'); |
| | | const { t } = useI18n(); |
| | | |
| | | export const modalProps = { |
| | | visible: propTypes.bool, |
| | | // open drag |
| | | draggable: propTypes.bool.def(true), |
| | | centered: propTypes.bool, |
| | | cancelText: propTypes.string.def(t('cancelText')), |
| | | okText: propTypes.string.def(t('okText')), |
| | | cancelText: propTypes.string.def(t('component.modal.cancelText')), |
| | | okText: propTypes.string.def(t('component.modal.okText')), |
| | | |
| | | closeFunc: Function as PropType<() => Promise<boolean>>, |
| | | }; |
| | |
| | | |
| | | <Tooltip placement="top" v-if="getSetting.redo"> |
| | | <template #title> |
| | | <span>{{ t('settingRedo') }}</span> |
| | | <span>{{ t('component.table.settingRedo') }}</span> |
| | | </template> |
| | | <RedoOutlined @click="redo" /> |
| | | </Tooltip> |
| | | |
| | | <Tooltip placement="top" v-if="getSetting.size"> |
| | | <template #title> |
| | | <span>{{ t('settingDens') }}</span> |
| | | <span>{{ t('component.table.settingDens') }}</span> |
| | | </template> |
| | | <Dropdown placement="bottomCenter" :trigger="['click']"> |
| | | <ColumnHeightOutlined /> |
| | | <template #overlay> |
| | | <Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef"> |
| | | <MenuItem key="default"> |
| | | <span>{{ t('settingDensDefault') }}</span> |
| | | <span>{{ t('component.table.settingDensDefault') }}</span> |
| | | </MenuItem> |
| | | <MenuItem key="middle"> |
| | | <span>{{ t('settingDensMiddle') }}</span> |
| | | <span>{{ t('component.table.settingDensMiddle') }}</span> |
| | | </MenuItem> |
| | | <MenuItem key="small"> |
| | | <span>{{ t('settingDensSmall') }}</span> |
| | | <span>{{ t('component.table.settingDensSmall') }}</span> |
| | | </MenuItem> |
| | | </Menu> |
| | | </template> |
| | |
| | | defaultCheckList: [], |
| | | }); |
| | | |
| | | const { t } = useI18n('component.table'); |
| | | const { t } = useI18n(); |
| | | |
| | | watchEffect(() => { |
| | | const columns = table.getColumns(); |
| | |
| | | import { useProps } from './useProps'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('component.table'); |
| | | const { t } = useI18n(); |
| | | export function useColumns( |
| | | refProps: ComputedRef<BasicTableProps>, |
| | | getPaginationRef: ComputedRef<false | PaginationProps> |
| | |
| | | columns.unshift({ |
| | | flag: 'INDEX', |
| | | width: 50, |
| | | title: t('index'), |
| | | title: t('component.table.index'), |
| | | align: 'center', |
| | | customRender: ({ index }) => { |
| | | const getPagination = unref(getPaginationRef); |
| | |
| | | import { useProps } from './useProps'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('component.table'); |
| | | const { t } = useI18n(); |
| | | export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
| | | const configRef = ref<PaginationProps>({}); |
| | | const { propsRef } = useProps(refProps); |
| | |
| | | pageSize: PAGE_SIZE, |
| | | size: 'small', |
| | | defaultPageSize: PAGE_SIZE, |
| | | showTotal: (total) => t('total', { total }), |
| | | showTotal: (total) => t('component.table.total', { total }), |
| | | showSizeChanger: true, |
| | | pageSizeOptions: PAGE_SIZE_OPTIONS, |
| | | itemRender: ({ page, type, originalElement }) => { |
| | |
| | | <div> |
| | | <a-button-group> |
| | | <a-button type="primary" @click="openUploadModal" preIcon="ant-design:cloud-upload-outlined"> |
| | | {{ t('upload') }} |
| | | {{ t('component.upload.upload') }} |
| | | </a-button> |
| | | <Tooltip placement="bottom" v-if="showPreview"> |
| | | <template #title> |
| | | {{ t('uploaded') }} |
| | | {{ t('component.upload.uploaded') }} |
| | | <template v-if="fileListRef.length">{{ fileListRef.length }}</template> |
| | | </template> |
| | | <a-button @click="openPreviewModal"> |
| | |
| | | components: { UploadModal, UploadPreviewModal, Icon, Tooltip }, |
| | | props: uploadContainerProps, |
| | | setup(props, { emit, attrs }) { |
| | | const { t } = useI18n('component.upload'); |
| | | const { t } = useI18n(); |
| | | // 上传modal |
| | | const [registerUploadModal, { openModal: openUploadModal }] = useModal(); |
| | | |
| | |
| | | <template> |
| | | <BasicModal |
| | | width="800px" |
| | | :title="t('upload')" |
| | | :okText="t('save')" |
| | | :title="t('component.upload.upload')" |
| | | :okText="t('component.upload.save')" |
| | | v-bind="$attrs" |
| | | @register="register" |
| | | @ok="handleOk" |
| | |
| | | :before-upload="beforeUpload" |
| | | class="upload-modal-toolbar__btn" |
| | | > |
| | | <a-button type="primary"> {{ t('choose') }} </a-button> |
| | | <a-button type="primary"> {{ t('component.upload.choose') }} </a-button> |
| | | </Upload> |
| | | </div> |
| | | <FileList :dataSource="fileListRef" :columns="columns" :actionColumn="actionColumn" /> |
| | |
| | | props: basicProps, |
| | | setup(props, { emit }) { |
| | | // 是否正在上传 |
| | | const { t } = useI18n('component.upload'); |
| | | const { t } = useI18n(); |
| | | |
| | | const isUploadingRef = ref(false); |
| | | const fileListRef = ref<FileItem[]>([]); |
| | |
| | | (item) => item.status === UploadResultStatus.ERROR |
| | | ); |
| | | return isUploadingRef.value |
| | | ? t('uploading') |
| | | ? t('component.upload.uploading') |
| | | : someError |
| | | ? t('reUploadFailed') |
| | | : t('startUpload'); |
| | | ? t('component.upload.reUploadFailed') |
| | | : t('component.upload.startUpload'); |
| | | }); |
| | | |
| | | // 上传前校验 |
| | |
| | | |
| | | // 设置最大值,则判断 |
| | | if (maxSize && file.size / 1024 / 1024 >= maxSize) { |
| | | createMessage.error(t('maxSizeMultiple', [maxSize])); |
| | | createMessage.error(t('component.upload.maxSizeMultiple', [maxSize])); |
| | | return false; |
| | | } |
| | | |
| | | // 设置类型,则判断 |
| | | if (accept.length > 0 && !checkFileType(file, accept)) { |
| | | createMessage.error!(t('acceptUpload', [accept.join(',')])); |
| | | createMessage.error!(t('acomponent.upload.cceptUpload', [accept.join(',')])); |
| | | return false; |
| | | } |
| | | const commonItem = { |
| | |
| | | async function handleStartUpload() { |
| | | const { maxNumber } = props; |
| | | if (fileListRef.value.length > maxNumber) { |
| | | return createMessage.warning(t('maxNumber', [maxNumber])); |
| | | return createMessage.warning(t('component.upload.maxNumber', [maxNumber])); |
| | | } |
| | | try { |
| | | isUploadingRef.value = true; |
| | |
| | | const { maxNumber } = props; |
| | | |
| | | if (fileListRef.value.length > maxNumber) { |
| | | return createMessage.warning(t('maxNumber', [maxNumber])); |
| | | return createMessage.warning(t('component.upload.maxNumber', [maxNumber])); |
| | | } |
| | | if (isUploadingRef.value) { |
| | | return createMessage.warning(t('saveWarn')); |
| | | return createMessage.warning(t('component.upload.saveWarn')); |
| | | } |
| | | const fileList: string[] = []; |
| | | |
| | |
| | | } |
| | | // 存在一个上传成功的即可保存 |
| | | if (fileList.length <= 0) { |
| | | return createMessage.warning(t('saveError')); |
| | | return createMessage.warning(t('component.upload.saveError')); |
| | | } |
| | | fileListRef.value = []; |
| | | closeModal(); |
| | |
| | | fileListRef.value = []; |
| | | return true; |
| | | } else { |
| | | createMessage.warning(t('uploadWait')); |
| | | createMessage.warning(t('component.upload.uploadWait')); |
| | | return false; |
| | | } |
| | | } |
| | |
| | | <template> |
| | | <BasicModal |
| | | width="800px" |
| | | :title="t('preview')" |
| | | :title="t('component.upload.preview')" |
| | | wrapClassName="upload-preview-modal" |
| | | v-bind="$attrs" |
| | | @register="register" |
| | |
| | | props: previewProps, |
| | | setup(props, { emit }) { |
| | | const [register, { closeModal }] = useModalInner(); |
| | | const { t } = useI18n('component.upload'); |
| | | const { t } = useI18n(); |
| | | |
| | | const fileListRef = ref<PreviewFileItem[]>([]); |
| | | watch( |
| | |
| | | import TableAction from '/@/components/Table/src/components/TableAction'; |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | const { t } = useI18n('component.upload'); |
| | | const { t } = useI18n(); |
| | | |
| | | // 文件上传列表 |
| | | export function createTableColumns(): BasicColumn[] { |
| | | return [ |
| | | { |
| | | dataIndex: 'thumbUrl', |
| | | title: t('legend'), |
| | | title: t('component.upload.legend'), |
| | | width: 100, |
| | | customRender: ({ record }) => { |
| | | const { thumbUrl, type } = (record as FileItem) || {}; |
| | |
| | | }, |
| | | { |
| | | dataIndex: 'name', |
| | | title: t('fileName'), |
| | | title: t('component.upload.fileName'), |
| | | align: 'left', |
| | | customRender: ({ text, record }) => { |
| | | const { percent, status: uploadStatus } = (record as FileItem) || {}; |
| | |
| | | }, |
| | | { |
| | | dataIndex: 'size', |
| | | title: t('fileSize'), |
| | | title: t('component.upload.fileSize'), |
| | | width: 100, |
| | | customRender: ({ text = 0 }) => { |
| | | return text && (text / 1024).toFixed(2) + 'KB'; |
| | |
| | | // }, |
| | | { |
| | | dataIndex: 'status', |
| | | title: t('fileStatue'), |
| | | title: t('component.upload.fileStatue'), |
| | | width: 100, |
| | | customRender: ({ text }) => { |
| | | if (text === UploadResultStatus.SUCCESS) { |
| | | return <Tag color="green">{() => t('uploadSuccess')}</Tag>; |
| | | return <Tag color="green">{() => t('component.upload.uploadSuccess')}</Tag>; |
| | | } else if (text === UploadResultStatus.ERROR) { |
| | | return <Tag color="red">{() => t('uploadError')}</Tag>; |
| | | return <Tag color="red">{() => t('component.upload.uploadError')}</Tag>; |
| | | } else if (text === UploadResultStatus.UPLOADING) { |
| | | return <Tag color="blue">{() => t('uploading')}</Tag>; |
| | | return <Tag color="blue">{() => t('component.upload.uploading')}</Tag>; |
| | | } |
| | | |
| | | return text; |
| | |
| | | export function createActionColumn(handleRemove: Function, handlePreview: Function): BasicColumn { |
| | | return { |
| | | width: 120, |
| | | title: t('operating'), |
| | | title: t('component.upload.operating'), |
| | | dataIndex: 'action', |
| | | fixed: false, |
| | | customRender: ({ record }) => { |
| | | const actions: ActionItem[] = [ |
| | | { |
| | | label: t('del'), |
| | | label: t('component.upload.del'), |
| | | color: 'error', |
| | | onClick: handleRemove.bind(null, record), |
| | | }, |
| | | ]; |
| | | if (checkImgType(record)) { |
| | | actions.unshift({ |
| | | label: t('preview'), |
| | | label: t('component.upload.preview'), |
| | | onClick: handlePreview.bind(null, record), |
| | | }); |
| | | } |
| | |
| | | return [ |
| | | { |
| | | dataIndex: 'url', |
| | | title: t('legend'), |
| | | title: t('component.upload.legend'), |
| | | width: 100, |
| | | customRender: ({ record }) => { |
| | | const { url, type } = (record as PreviewFileItem) || {}; |
| | |
| | | }, |
| | | { |
| | | dataIndex: 'name', |
| | | title: t('fileName'), |
| | | title: t('component.upload.fileName'), |
| | | align: 'left', |
| | | }, |
| | | ]; |
| | |
| | | }): BasicColumn { |
| | | return { |
| | | width: 160, |
| | | title: t('operating'), |
| | | title: t('component.upload.operating'), |
| | | dataIndex: 'action', |
| | | fixed: false, |
| | | customRender: ({ record }) => { |
| | |
| | | |
| | | const actions: ActionItem[] = [ |
| | | { |
| | | label: t('del'), |
| | | label: t('component.upload.del'), |
| | | color: 'error', |
| | | onClick: handleRemove.bind(null, record), |
| | | }, |
| | | { |
| | | label: t('download'), |
| | | label: t('component.upload.download'), |
| | | onClick: handleDownload.bind(null, record), |
| | | }, |
| | | ]; |
| | | if (isImgTypeByName(url)) { |
| | | actions.unshift({ |
| | | label: t('preview'), |
| | | label: t('component.upload.preview'), |
| | | onClick: handlePreview.bind(null, record), |
| | | }); |
| | | } |
| | |
| | | import { Ref, unref, computed } from 'vue'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | const { t } = useI18n('component.upload'); |
| | | const { t } = useI18n(); |
| | | export function useUploadType({ |
| | | acceptRef, |
| | | // uploadTypeRef, |
| | |
| | | |
| | | const accept = unref(acceptRef); |
| | | if (accept.length > 0) { |
| | | helpTexts.push(t('accept', [accept.join(',')])); |
| | | helpTexts.push(t('component.upload.accept', [accept.join(',')])); |
| | | } |
| | | |
| | | const maxSize = unref(maxSizeRef); |
| | | if (maxSize) { |
| | | helpTexts.push(t('maxSize', [maxSize])); |
| | | helpTexts.push(t('component.upload.maxSize', [maxSize])); |
| | | } |
| | | |
| | | const maxNumber = unref(maxNumberRef); |
| | | if (maxNumber && maxNumber !== Infinity) { |
| | | helpTexts.push(t('maxNumber', [maxNumber])); |
| | | helpTexts.push(t('component.upload.maxNumber', [maxNumber])); |
| | | } |
| | | return helpTexts.join(','); |
| | | }); |
| | |
| | | endTime: 0, |
| | | draged: false, |
| | | }); |
| | | const { t } = useI18n('component.verify'); |
| | | const { t } = useI18n(); |
| | | |
| | | watch( |
| | | () => state.isPassing, |
| | |
| | | /> |
| | | {state.showTip && ( |
| | | <span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}> |
| | | {state.isPassing ? t('time', { time: time.toFixed(1) }) : t('error')} |
| | | {state.isPassing |
| | | ? t('component.verify.time', { time: time.toFixed(1) }) |
| | | : t('component.verify.error')} |
| | | </span> |
| | | )} |
| | | {!state.showTip && !state.draged && ( |
| | |
| | | import type { PropType } from 'vue'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('component.verify'); |
| | | const { t } = useI18n(); |
| | | export const basicProps = { |
| | | value: { |
| | | type: Boolean as PropType<boolean>, |
| | |
| | | |
| | | text: { |
| | | type: [String] as PropType<string>, |
| | | default: t('dragText'), |
| | | default: t('component.verify.dragText'), |
| | | }, |
| | | successText: { |
| | | type: [String] as PropType<string>, |
| | | default: t('successText'), |
| | | default: t('component.verify.successText'), |
| | | }, |
| | | height: { |
| | | type: [Number, String] as PropType<number | string>, |
| | |
| | | export default defineComponent({ |
| | | name: 'LayoutContent', |
| | | setup() { |
| | | const { t } = useI18n('layout.footer'); |
| | | const { t } = useI18n(); |
| | | return () => { |
| | | return ( |
| | | <Layout.Footer class="layout-footer"> |
| | | {() => ( |
| | | <> |
| | | <div class="layout-footer__links"> |
| | | <a onClick={() => openWindow(SITE_URL)}>{t('onlinePreview')}</a> |
| | | <a onClick={() => openWindow(SITE_URL)}>{t('layout.footer.onlinePreview')}</a> |
| | | <GithubFilled onClick={() => openWindow(GITHUB_URL)} class="github" /> |
| | | <a onClick={() => openWindow(DOC_URL)}>{t('onlineDocument')}</a> |
| | | <a onClick={() => openWindow(DOC_URL)}>{t('layout.footer.onlineDocument')}</a> |
| | | </div> |
| | | <div>Copyright ©2020 Vben Admin</div> |
| | | </> |
| | |
| | | const logoWidthRef = ref(200); |
| | | const logoRef = ref<ComponentRef>(null); |
| | | const { refreshPage } = useTabs(); |
| | | const { t } = useI18n('layout.header'); |
| | | const { t } = useI18n(); |
| | | |
| | | const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting(); |
| | | |
| | |
| | | export default defineComponent({ |
| | | name: 'UserDropdown', |
| | | setup() { |
| | | const { t } = useI18n('layout.header'); |
| | | const { t } = useI18n(); |
| | | const { getShowDoc } = useHeaderSetting(); |
| | | |
| | | const getUserInfo = computed(() => { |
| | |
| | | <Menu onClick={handleMenuClick}> |
| | | {() => ( |
| | | <> |
| | | {showDoc && <MenuItem key="doc" text={t('dropdownItemDoc')} icon="gg:loadbar-doc" />} |
| | | {showDoc && ( |
| | | <MenuItem |
| | | key="doc" |
| | | text={t('layout.header.dropdownItemDoc')} |
| | | icon="gg:loadbar-doc" |
| | | /> |
| | | )} |
| | | {/* @ts-ignore */} |
| | | {showDoc && <Menu.Divider />} |
| | | <MenuItem |
| | | key="loginOut" |
| | | text={t('dropdownItemLoginOut')} |
| | | text={t('layout.header.dropdownItemLoginOut')} |
| | | icon="ant-design:poweroff-outlined" |
| | | /> |
| | | </> |
| | |
| | | export default defineComponent({ |
| | | name: 'LockModal', |
| | | setup(_, { attrs }) { |
| | | const { t } = useI18n('layout.header'); |
| | | const { t } = useI18n(); |
| | | const [register, { closeModal }] = useModalInner(); |
| | | |
| | | const [registerForm, { validateFields, resetFields }] = useForm({ |
| | |
| | | schemas: [ |
| | | { |
| | | field: 'password', |
| | | label: t('lockScreenPassword'), |
| | | label: t('layout.header.lockScreenPassword'), |
| | | component: 'InputPassword', |
| | | required: true, |
| | | }, |
| | |
| | | return () => ( |
| | | <BasicModal |
| | | footer={null} |
| | | title={t('lockScreen')} |
| | | title={t('layout.header.lockScreen')} |
| | | {...attrs} |
| | | class={prefixCls} |
| | | onRegister={register} |
| | |
| | | |
| | | <div class={`${prefixCls}__footer`}> |
| | | <Button type="primary" block class="mt-2" onClick={lock}> |
| | | {() => t('lockScreenBtn')} |
| | | {() => t('layout.header.lockScreenBtn')} |
| | | </Button> |
| | | <Button block class="mt-2" onClick={lock.bind(null, false)}> |
| | | {() => t('notLockScreenPassword')} |
| | | {() => t('layout.header.notLockScreenPassword')} |
| | | </Button> |
| | | </div> |
| | | </div> |
| | |
| | | }, |
| | | }, |
| | | setup(props) { |
| | | const { t } = useI18n('layout.multipleTab'); |
| | | const { t } = useI18n(); |
| | | const { getShowMenu } = useMenuSetting(); |
| | | const { getShowHeader } = useHeaderSetting(); |
| | | const { getShowQuick } = useMultipleTabSetting(); |
| | |
| | | |
| | | return () => { |
| | | const scaleAction = getScaleAction( |
| | | unref(getIsScale) ? t('putAway') : t('unfold'), |
| | | unref(getIsScale) ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'), |
| | | unref(getIsScale) |
| | | ); |
| | | const dropMenuList = unref(getDropMenuList) || []; |
| | |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('layout.multipleTab'); |
| | | const { t } = useI18n(); |
| | | |
| | | export enum TabContentEnum { |
| | | TAB_TYPE, |
| | |
| | | const REFRESH_PAGE: DropMenu = { |
| | | icon: 'ant-design:reload-outlined', |
| | | event: MenuEventEnum.REFRESH_PAGE, |
| | | text: t('redo'), |
| | | text: t('layout.multipleTab.redo'), |
| | | disabled: false, |
| | | }; |
| | | const CLOSE_CURRENT: DropMenu = { |
| | | icon: 'ant-design:close-outlined', |
| | | event: MenuEventEnum.CLOSE_CURRENT, |
| | | text: t('close'), |
| | | text: t('layout.multipleTab.close'), |
| | | disabled: false, |
| | | divider: true, |
| | | }; |
| | | const CLOSE_LEFT: DropMenu = { |
| | | icon: 'ant-design:pic-left-outlined', |
| | | event: MenuEventEnum.CLOSE_LEFT, |
| | | text: t('closeLeft'), |
| | | text: t('layout.multipleTab.closeLeft'), |
| | | disabled: false, |
| | | divider: false, |
| | | }; |
| | | const CLOSE_RIGHT: DropMenu = { |
| | | icon: 'ant-design:pic-right-outlined', |
| | | event: MenuEventEnum.CLOSE_RIGHT, |
| | | text: t('closeRight'), |
| | | text: t('layout.multipleTab.closeRight'), |
| | | disabled: false, |
| | | divider: true, |
| | | }; |
| | | const CLOSE_OTHER: DropMenu = { |
| | | icon: 'ant-design:pic-center-outlined', |
| | | event: MenuEventEnum.CLOSE_OTHER, |
| | | text: t('closeOther'), |
| | | text: t('layout.multipleTab.closeOther'), |
| | | disabled: false, |
| | | }; |
| | | const CLOSE_ALL: DropMenu = { |
| | | icon: 'ant-design:line-outlined', |
| | | event: MenuEventEnum.CLOSE_ALL, |
| | | text: t('closeAll'), |
| | | text: t('layout.multipleTab.closeAll'), |
| | | disabled: false, |
| | | }; |
| | | return [REFRESH_PAGE, CLOSE_CURRENT, CLOSE_LEFT, CLOSE_RIGHT, CLOSE_OTHER, CLOSE_ALL]; |
| | |
| | | } |
| | | |
| | | const { createSuccessModal, createMessage } = useMessage(); |
| | | const { t } = useI18n('layout.setting'); |
| | | const { t } = useI18n(); |
| | | |
| | | /** |
| | | * Menu type Picker comp |
| | |
| | | const { isSuccessRef } = useCopyToClipboard(JSON.stringify(unref(getRootSetting), null, 2)); |
| | | unref(isSuccessRef) && |
| | | createSuccessModal({ |
| | | title: t('operatingTitle'), |
| | | content: t('operatingContent'), |
| | | title: t('layout.setting.operatingTitle'), |
| | | content: t('layout.setting.operatingContent'), |
| | | }); |
| | | } |
| | | function handleResetSetting() { |
| | |
| | | // updateTheme(themeColor); |
| | | updateColorWeak(colorWeak); |
| | | updateGrayMode(grayMode); |
| | | createMessage.success(t('resetSuccess')); |
| | | createMessage.success(t('layout.setting.resetSuccess')); |
| | | } catch (error) { |
| | | createMessage.error(error); |
| | | } |
| | |
| | | {() => ( |
| | | <> |
| | | <CopyOutlined class="mr-2" /> |
| | | {t('copyBtn')} |
| | | {t('layout.setting.copyBtn')} |
| | | </> |
| | | )} |
| | | </Button> |
| | |
| | | {() => ( |
| | | <> |
| | | <RedoOutlined class="mr-2" /> |
| | | {t('resetBtn')} |
| | | {t('layout.setting.resetBtn')} |
| | | </> |
| | | )} |
| | | </Button> |
| | |
| | | {() => ( |
| | | <> |
| | | <RedoOutlined class="mr-2" /> |
| | | {t('clearBtn')} |
| | | {t('layout.setting.clearBtn')} |
| | | </> |
| | | )} |
| | | </Button> |
| | |
| | | return ( |
| | | <> |
| | | <MenuTypePicker /> |
| | | {renderSwitchItem(t('splitMenu'), { |
| | | {renderSwitchItem(t('layout.setting.splitMenu'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_SPLIT, e); |
| | | }, |
| | |
| | | function renderTheme() { |
| | | return ( |
| | | <> |
| | | <Divider>{() => t('headerTheme')}</Divider> |
| | | <Divider>{() => t('layout.setting.headerTheme')}</Divider> |
| | | <ThemePicker |
| | | colorList={HEADER_PRESET_BG_COLOR_LIST} |
| | | def={unref(getHeaderBgColor)} |
| | |
| | | baseHandler(HandlerEnum.HEADER_THEME, e); |
| | | }} |
| | | /> |
| | | <Divider>{() => t('sidebarTheme')}</Divider> |
| | | <Divider>{() => t('layout.setting.sidebarTheme')}</Divider> |
| | | <ThemePicker |
| | | colorList={SIDE_BAR_BG_COLOR_LIST} |
| | | def={unref(getMenuBgColor)} |
| | |
| | | */ |
| | | function renderFeatures() { |
| | | return [ |
| | | renderSwitchItem(t('menuDrag'), { |
| | | renderSwitchItem(t('layout.setting.menuDrag'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_HAS_DRAG, e); |
| | | }, |
| | | def: unref(getCanDrag), |
| | | disabled: !unref(getShowMenuRef), |
| | | }), |
| | | renderSwitchItem(t('menuSearch'), { |
| | | renderSwitchItem(t('layout.setting.menuSearch'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e); |
| | | }, |
| | | def: unref(getShowSearch), |
| | | disabled: !unref(getShowMenuRef), |
| | | }), |
| | | renderSwitchItem(t('menuAccordion'), { |
| | | renderSwitchItem(t('layout.setting.menuAccordion'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_ACCORDION, e); |
| | | }, |
| | | def: unref(getAccordion), |
| | | disabled: !unref(getShowMenuRef), |
| | | }), |
| | | renderSwitchItem(t('menuCollapse'), { |
| | | renderSwitchItem(t('layout.setting.menuCollapse'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_COLLAPSED, e); |
| | | }, |
| | | def: unref(getCollapsed), |
| | | disabled: !unref(getShowMenuRef), |
| | | }), |
| | | renderSwitchItem(t('collapseMenuDisplayName'), { |
| | | renderSwitchItem(t('layout.setting.collapseMenuDisplayName'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e); |
| | | }, |
| | | def: unref(getCollapsedShowTitle), |
| | | disabled: !unref(getShowMenuRef) || !unref(getCollapsed), |
| | | }), |
| | | renderSwitchItem(t('fixedHeader'), { |
| | | renderSwitchItem(t('layout.setting.fixedHeader'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.HEADER_FIXED, e); |
| | | }, |
| | | def: unref(getHeaderFixed), |
| | | disabled: !unref(getShowHeader), |
| | | }), |
| | | renderSwitchItem(t('fixedSideBar'), { |
| | | renderSwitchItem(t('layout.setting.fixedSideBar'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_FIXED, e); |
| | | }, |
| | | def: unref(getMenuFixed), |
| | | disabled: !unref(getShowMenuRef), |
| | | }), |
| | | renderSelectItem(t('topMenuLayout'), { |
| | | renderSelectItem(t('layout.setting.topMenuLayout'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_TOP_ALIGN, e); |
| | | }, |
| | |
| | | options: topMenuAlignOptions, |
| | | disabled: !unref(getShowHeader) || (!unref(getIsTopMenu) && !unref(getSplit)), |
| | | }), |
| | | renderSelectItem(t('menuCollapseButton'), { |
| | | renderSelectItem(t('layout.setting.menuCollapseButton'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_TRIGGER, e); |
| | | }, |
| | |
| | | options: menuTriggerOptions, |
| | | }), |
| | | |
| | | renderSelectItem(t('contentMode'), { |
| | | renderSelectItem(t('layout.setting.contentMode'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.CONTENT_MODE, e); |
| | | }, |
| | |
| | | options: contentModeOptions, |
| | | }), |
| | | <div class={`setting-drawer__cell-item`}> |
| | | <span>{t('autoScreenLock')}</span> |
| | | <span>{t('layout.setting.autoScreenLock')}</span> |
| | | <InputNumber |
| | | style="width:126px" |
| | | size="small" |
| | |
| | | defaultValue={appStore.getProjectConfig.lockTime} |
| | | formatter={(value: string) => { |
| | | if (parseInt(value) === 0) { |
| | | return `0(${t('notAutoScreenLock')})`; |
| | | return `0(${t('layout.setting.notAutoScreenLock')})`; |
| | | } |
| | | return `${value}${t('minute')}`; |
| | | return `${value}${t('layout.setting.minute')}`; |
| | | }} |
| | | /> |
| | | </div>, |
| | | <div class={`setting-drawer__cell-item`}> |
| | | <span>{t('expandedMenuWidth')}</span> |
| | | <span>{t('layout.setting.expandedMenuWidth')}</span> |
| | | <InputNumber |
| | | style="width:126px" |
| | | size="small" |
| | |
| | | |
| | | function renderContent() { |
| | | return [ |
| | | renderSwitchItem(t('breadcrumb'), { |
| | | renderSwitchItem(t('layout.setting.breadcrumb'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.SHOW_BREADCRUMB, e); |
| | | }, |
| | | def: unref(getShowBreadCrumb), |
| | | disabled: !unref(getShowHeader), |
| | | }), |
| | | renderSwitchItem(t('breadcrumbIcon'), { |
| | | renderSwitchItem(t('layout.setting.breadcrumbIcon'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e); |
| | | }, |
| | | def: unref(getShowBreadCrumbIcon), |
| | | disabled: !unref(getShowHeader), |
| | | }), |
| | | renderSwitchItem(t('tabs'), { |
| | | renderSwitchItem(t('layout.setting.tabs'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.TABS_SHOW, e); |
| | | }, |
| | | def: unref(getShowMultipleTab), |
| | | }), |
| | | renderSwitchItem(t('tabsQuickBtn'), { |
| | | renderSwitchItem(t('layout.setting.tabsQuickBtn'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.TABS_SHOW_QUICK, e); |
| | | }, |
| | |
| | | disabled: !unref(getShowMultipleTab), |
| | | }), |
| | | |
| | | renderSwitchItem(t('sidebar'), { |
| | | renderSwitchItem(t('layout.setting.sidebar'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e); |
| | | }, |
| | | def: unref(getShowMenu), |
| | | disabled: unref(getIsHorizontal), |
| | | }), |
| | | renderSwitchItem(t('header'), { |
| | | renderSwitchItem(t('layout.setting.header'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.HEADER_SHOW, e); |
| | | }, |
| | |
| | | }, |
| | | def: unref(getShowLogo), |
| | | }), |
| | | renderSwitchItem(t('footer'), { |
| | | renderSwitchItem(t('layout.setting.footer'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.SHOW_FOOTER, e); |
| | | }, |
| | | def: unref(getShowFooter), |
| | | }), |
| | | renderSwitchItem(t('fullContent'), { |
| | | renderSwitchItem(t('layout.setting.fullContent'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.FULL_CONTENT, e); |
| | | }, |
| | | def: unref(getFullContent), |
| | | }), |
| | | renderSwitchItem(t('grayMode'), { |
| | | renderSwitchItem(t('layout.setting.grayMode'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.GRAY_MODE, e); |
| | | }, |
| | | def: unref(getGrayMode), |
| | | }), |
| | | renderSwitchItem(t('colorWeak'), { |
| | | renderSwitchItem(t('layout.setting.colorWeak'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.COLOR_WEAK, e); |
| | | }, |
| | |
| | | function renderTransition() { |
| | | return ( |
| | | <> |
| | | {renderSwitchItem(t('progress'), { |
| | | {renderSwitchItem(t('layout.setting.progress'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.OPEN_PROGRESS, e); |
| | | }, |
| | | def: unref(getOpenNProgress), |
| | | })} |
| | | {renderSwitchItem(t('switchLoading'), { |
| | | {renderSwitchItem(t('layout.setting.switchLoading'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e); |
| | | }, |
| | |
| | | disabled: !unref(getEnableTransition), |
| | | })} |
| | | |
| | | {renderSwitchItem(t('switchAnimation'), { |
| | | {renderSwitchItem(t('layout.setting.switchAnimation'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e); |
| | | }, |
| | | def: unref(getEnableTransition), |
| | | })} |
| | | |
| | | {renderSelectItem(t('animationType'), { |
| | | {renderSelectItem(t('layout.setting.animationType'), { |
| | | handler: (e) => { |
| | | baseHandler(HandlerEnum.ROUTER_TRANSITION, e); |
| | | }, |
| | |
| | | onChange={(e: any) => { |
| | | handler && handler(e); |
| | | }} |
| | | checkedChildren={t('on')} |
| | | unCheckedChildren={t('off')} |
| | | checkedChildren={t('layout.setting.on')} |
| | | unCheckedChildren={t('layout.setting.off')} |
| | | /> |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | return () => ( |
| | | <BasicDrawer {...attrs} title={t('drawerTitle')} width={330} wrapClassName="setting-drawer"> |
| | | <BasicDrawer |
| | | {...attrs} |
| | | title={t('layout.setting.drawerTitle')} |
| | | width={330} |
| | | wrapClassName="setting-drawer" |
| | | > |
| | | {{ |
| | | default: () => ( |
| | | <> |
| | | <Divider>{() => t('navMode')}</Divider> |
| | | <Divider>{() => t('layout.setting.navMode')}</Divider> |
| | | {renderSidebar()} |
| | | {renderTheme()} |
| | | <Divider>{() => t('interfaceFunction')}</Divider> |
| | | <Divider>{() => t('layout.setting.interfaceFunction')}</Divider> |
| | | {renderFeatures()} |
| | | <Divider>{() => t('interfaceDisplay')}</Divider> |
| | | <Divider>{() => t('layout.setting.interfaceDisplay')}</Divider> |
| | | {renderContent()} |
| | | <Divider>{() => t('animation')}</Divider> |
| | | <Divider>{() => t('layout.setting.animation')}</Divider> |
| | | {renderTransition()} |
| | | <Divider /> |
| | | <FooterButton /> |
| | |
| | | import menuTopImg from '/@/assets/images/layout/menu-top.svg'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('layout.setting'); |
| | | const { t } = useI18n(); |
| | | |
| | | export enum HandlerEnum { |
| | | CHANGE_LAYOUT, |
| | |
| | | export const contentModeOptions = [ |
| | | { |
| | | value: ContentEnum.FULL, |
| | | label: t('contentModeFull'), |
| | | label: t('layout.setting.contentModeFull'), |
| | | }, |
| | | { |
| | | value: ContentEnum.FIXED, |
| | | label: t('contentModeFixed'), |
| | | label: t('layout.setting.contentModeFixed'), |
| | | }, |
| | | ]; |
| | | |
| | | export const topMenuAlignOptions = [ |
| | | { |
| | | value: TopMenuAlignEnum.CENTER, |
| | | label: t('topMenuAlignRight'), |
| | | label: t('layout.setting.topMenuAlignRight'), |
| | | }, |
| | | { |
| | | value: TopMenuAlignEnum.START, |
| | | label: t('topMenuAlignLeft'), |
| | | label: t('layout.setting.topMenuAlignLeft'), |
| | | }, |
| | | { |
| | | value: TopMenuAlignEnum.END, |
| | | label: t('topMenuAlignCenter'), |
| | | label: t('layout.setting.topMenuAlignCenter'), |
| | | }, |
| | | ]; |
| | | |
| | | export const menuTriggerOptions = [ |
| | | { |
| | | value: TriggerEnum.NONE, |
| | | label: t('menuTriggerNone'), |
| | | label: t('layout.setting.menuTriggerNone'), |
| | | }, |
| | | { |
| | | value: TriggerEnum.FOOTER, |
| | | label: t('menuTriggerBottom'), |
| | | label: t('layout.setting.menuTriggerBottom'), |
| | | }, |
| | | { |
| | | value: TriggerEnum.HEADER, |
| | | label: t('menuTriggerTop'), |
| | | label: t('layout.setting.menuTriggerTop'), |
| | | }, |
| | | ]; |
| | | |
| | |
| | | |
| | | export const menuTypeList = [ |
| | | { |
| | | title: t('menuTypeSidebar'), |
| | | title: t('layout.setting.menuTypeSidebar'), |
| | | mode: MenuModeEnum.INLINE, |
| | | type: MenuTypeEnum.SIDEBAR, |
| | | src: sidebarImg, |
| | | }, |
| | | { |
| | | title: t('menuTypeMix'), |
| | | title: t('layout.setting.menuTypeMix'), |
| | | mode: MenuModeEnum.INLINE, |
| | | type: MenuTypeEnum.MIX, |
| | | src: mixImg, |
| | | }, |
| | | |
| | | { |
| | | title: t('menuTypeTopMenu'), |
| | | title: t('layout.setting.menuTypeTopMenu'), |
| | | mode: MenuModeEnum.HORIZONTAL, |
| | | type: MenuTypeEnum.TOP_MENU, |
| | | src: menuTopImg, |
| | |
| | | // import { warn } from '/@/utils/log'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('sys.app'); |
| | | const { t } = useI18n(); |
| | | |
| | | const { createMessage } = useMessage(); |
| | | const NAME = 'permission'; |
| | |
| | | } else if (permissionMode === PermissionModeEnum.BACK) { |
| | | const messageKey = 'loadMenu'; |
| | | createMessage.loading({ |
| | | content: t('menuLoading'), |
| | | content: t('sys.app.menuLoading'), |
| | | key: messageKey, |
| | | duration: 1, |
| | | }); |
| | |
| | | @Action |
| | | async confirmLoginOut() { |
| | | const { createConfirm } = useMessage(); |
| | | const { t } = useI18n('sys.app'); |
| | | const { t } = useI18n(); |
| | | createConfirm({ |
| | | iconType: 'warning', |
| | | title: t('loginOutTip'), |
| | | content: t('loginOutMessage'), |
| | | title: t('sys.app.loginOutTip'), |
| | | content: t('sys.app.loginOutMessage'), |
| | | onOk: async () => { |
| | | await this.loginOut(true); |
| | | }, |
| | |
| | | |
| | | const error = createMessage.error!; |
| | | export function checkStatus(status: number, msg: string): void { |
| | | const { t } = useI18n('sys.api'); |
| | | const { t } = useI18n(); |
| | | switch (status) { |
| | | case 400: |
| | | error(`${msg}`); |
| | |
| | | // 未登录则跳转登录页面,并携带当前页面的路径 |
| | | // 在登录成功后返回当前页面,这一步需要在登录页操作。 |
| | | case 401: |
| | | error(t('errMsg401')); |
| | | error(t('sys.api.errMsg401')); |
| | | userStore.loginOut(true); |
| | | break; |
| | | case 403: |
| | | error(t('errMsg403')); |
| | | error(t('sys.api.errMsg403')); |
| | | break; |
| | | // 404请求不存在 |
| | | case 404: |
| | | error(t('errMsg404')); |
| | | error(t('sys.api.errMsg404')); |
| | | break; |
| | | case 405: |
| | | error(t('errMsg405')); |
| | | error(t('sys.api.errMsg405')); |
| | | break; |
| | | case 408: |
| | | error(t('errMsg408')); |
| | | error(t('sys.api.errMsg408')); |
| | | break; |
| | | case 500: |
| | | error(t('errMsg500')); |
| | | error(t('sys.api.errMsg500')); |
| | | break; |
| | | case 501: |
| | | error(t('errMsg501')); |
| | | error(t('sys.api.errMsg501')); |
| | | break; |
| | | case 502: |
| | | error(t('errMsg502')); |
| | | error(t('sys.api.errMsg502')); |
| | | break; |
| | | case 503: |
| | | error(t('errMsg503')); |
| | | error(t('sys.api.errMsg503')); |
| | | break; |
| | | case 504: |
| | | error(t('errMsg504')); |
| | | error(t('sys.api.errMsg504')); |
| | | break; |
| | | case 505: |
| | | error(t('errMsg505')); |
| | | error(t('sys.api.errMsg505')); |
| | | break; |
| | | default: |
| | | } |
| | |
| | | * @description: 处理请求数据 |
| | | */ |
| | | transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => { |
| | | const { t } = useI18n('sys.api'); |
| | | const { t } = useI18n(); |
| | | const { isTransformRequestResult } = options; |
| | | // 不进行任何处理,直接返回 |
| | | // 用于页面代码可能需要直接获取code,data,message这些信息时开启 |
| | |
| | | if (message) { |
| | | // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误 |
| | | if (options.errorMessageMode === 'modal') { |
| | | createErrorModal({ title: t('errorTip'), content: message }); |
| | | createErrorModal({ title: t('sys.api.errorTip'), content: message }); |
| | | } else { |
| | | createMessage.error(message); |
| | | } |
| | |
| | | createMessage.error(data.message); |
| | | Promise.reject(new Error(message)); |
| | | } else { |
| | | const msg = t('errorMessage'); |
| | | const msg = t('sys.api.errorMessage'); |
| | | createMessage.error(msg); |
| | | Promise.reject(new Error(msg)); |
| | | } |
| | |
| | | } |
| | | // 登录超时 |
| | | if (code === ResultEnum.TIMEOUT) { |
| | | const timeoutMsg = t('timeoutMessage'); |
| | | const timeoutMsg = t('sys.api.timeoutMessage'); |
| | | createErrorModal({ |
| | | title: t('operationFailed'), |
| | | title: t('sys.api.operationFailed'), |
| | | content: timeoutMsg, |
| | | }); |
| | | Promise.reject(new Error(timeoutMsg)); |
| | |
| | | * @description: 响应错误处理 |
| | | */ |
| | | responseInterceptorsCatch: (error: any) => { |
| | | const { t } = useI18n('sys.api'); |
| | | const { t } = useI18n(); |
| | | errorStore.setupErrorHandle(error); |
| | | const { response, code, message } = error || {}; |
| | | const msg: string = |
| | |
| | | const err: string = error.toString(); |
| | | try { |
| | | if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) { |
| | | createMessage.error(t('apiTimeoutMessage')); |
| | | createMessage.error(t('sys.api.apiTimeoutMessage')); |
| | | } |
| | | if (err && err.includes('Network Error')) { |
| | | createErrorModal({ |
| | | title: t('networkException'), |
| | | content: t('networkExceptionMsg'), |
| | | title: t('sys.api.networkException'), |
| | | content: t('sys.api.networkExceptionMsg'), |
| | | }); |
| | | } |
| | | } catch (error) { |
| | |
| | | <template> |
| | | <BasicModal :width="800" :title="t('tableActionDesc')" v-bind="$attrs"> |
| | | <BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs"> |
| | | <Description :data="info" @register="register" /> |
| | | </BasicModal> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | setup() { |
| | | const { t } = useI18n('sys.errorLog'); |
| | | const { t } = useI18n(); |
| | | const [register] = useDescription({ |
| | | column: 2, |
| | | schema: getDescSchema(), |
| | |
| | | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | const { t } = useI18n('sys.errorLog'); |
| | | const { t } = useI18n(); |
| | | |
| | | export function getColumns(): BasicColumn[] { |
| | | return [ |
| | | { |
| | | dataIndex: 'type', |
| | | title: t('tableColumnType'), |
| | | title: t('sys.errorLog.tableColumnType'), |
| | | width: 80, |
| | | customRender: ({ text }) => { |
| | | const color = |
| | |
| | | }, |
| | | { |
| | | dataIndex: 'time', |
| | | title: t('tableColumnDate'), |
| | | title: t('sys.errorLog.tableColumnDate'), |
| | | width: 160, |
| | | }, |
| | | { |
| | | dataIndex: 'file', |
| | | title: t('tableColumnFile'), |
| | | title: t('sys.errorLog.tableColumnFile'), |
| | | width: 200, |
| | | }, |
| | | { |
| | |
| | | }, |
| | | { |
| | | dataIndex: 'message', |
| | | title: t('tableColumnMsg'), |
| | | title: t('sys.errorLog.tableColumnMsg'), |
| | | width: 300, |
| | | }, |
| | | { |
| | | dataIndex: 'stack', |
| | | title: t('tableColumnStackMsg'), |
| | | title: t('sys.errorLog.tableColumnStackMsg'), |
| | | width: 300, |
| | | }, |
| | | ]; |
| | |
| | | const rowInfoRef = ref<ErrorInfo>(); |
| | | const imgListRef = ref<string[]>([]); |
| | | |
| | | const { t } = useI18n('sys.errorLog'); |
| | | const { t } = useI18n(); |
| | | |
| | | const [register, { setTableData }] = useTable({ |
| | | title: t('sys.errorLog.tableTitle'), |
| | |
| | | ); |
| | | const { createMessage } = useMessage(); |
| | | if (isDevMode()) { |
| | | createMessage.info(t('enableMessage')); |
| | | createMessage.info(t('sys.errorLog.enableMessage')); |
| | | } |
| | | // 查看详情 |
| | | function handleDetail(row: ErrorInfo) { |
| | |
| | | const { query } = useRoute(); |
| | | const go = useGo(); |
| | | const redo = useRedo(); |
| | | const { t } = useI18n('sys.exception'); |
| | | const { t } = useI18n(); |
| | | |
| | | const getStatus = computed(() => { |
| | | const { status: routeStatus } = query; |
| | |
| | | } |
| | | ); |
| | | |
| | | const backLoginI18n = t('backLogin'); |
| | | const backHomeI18n = t('backHome'); |
| | | const backLoginI18n = t('sys.exception.backLogin'); |
| | | const backHomeI18n = t('sys.exception.backHome'); |
| | | |
| | | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, { |
| | | title: '403', |
| | | status: `${ExceptionEnum.PAGE_NOT_ACCESS}`, |
| | | subTitle: t('subTitle403'), |
| | | subTitle: t('sys.exception.subTitle403'), |
| | | btnText: props.full ? backLoginI18n : backHomeI18n, |
| | | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), |
| | | }); |
| | |
| | | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, { |
| | | title: '404', |
| | | status: `${ExceptionEnum.PAGE_NOT_FOUND}`, |
| | | subTitle: t('subTitle404'), |
| | | subTitle: t('sys.exception.subTitle404'), |
| | | btnText: props.full ? backLoginI18n : backHomeI18n, |
| | | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), |
| | | }); |
| | |
| | | unref(statusMapRef).set(ExceptionEnum.ERROR, { |
| | | title: '500', |
| | | status: `${ExceptionEnum.ERROR}`, |
| | | subTitle: t('subTitle500'), |
| | | subTitle: t('sys.exception.subTitle500'), |
| | | btnText: backHomeI18n, |
| | | handler: () => go(), |
| | | }); |
| | | |
| | | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, { |
| | | title: t('noDataTitle'), |
| | | title: t('sys.exception.noDataTitle'), |
| | | subTitle: '', |
| | | btnText: t('redo'), |
| | | btnText: t('sys.exception.redo'), |
| | | handler: () => redo(), |
| | | icon: notDataImg, |
| | | }); |
| | | |
| | | unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, { |
| | | title: t('networkErrorTitle'), |
| | | subTitle: t('networkErrorSubTitle'), |
| | | title: t('sys.exception.networkErrorTitle'), |
| | | subTitle: t('sys.exception.networkErrorSubTitle'), |
| | | btnText: 'Refresh', |
| | | handler: () => redo(), |
| | | icon: netWorkImg, |
| | |
| | | const loadingRef = ref(false); |
| | | const errMsgRef = ref(false); |
| | | |
| | | const { t } = useI18n('sys.lock'); |
| | | const { t } = useI18n(); |
| | | const [register, { validateFields }] = useForm({ |
| | | showActionButtonGroup: false, |
| | | schemas: [ |
| | |
| | | component: 'InputPassword', |
| | | componentProps: { |
| | | style: { width: '100%' }, |
| | | placeholder: t('placeholder'), |
| | | placeholder: t('sys.lock.placeholder'), |
| | | }, |
| | | rules: [{ required: true }], |
| | | }, |
| | |
| | | const globSetting = useGlobSetting(); |
| | | const { locale } = useProjectSetting(); |
| | | const { notification } = useMessage(); |
| | | const { t } = useI18n('sys.login'); |
| | | const { t } = useI18n(); |
| | | |
| | | // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify); |
| | | |
| | |
| | | }); |
| | | |
| | | const formRules = reactive({ |
| | | account: [{ required: true, message: t('accountPlaceholder'), trigger: 'blur' }], |
| | | password: [{ required: true, message: t('passwordPlaceholder'), trigger: 'blur' }], |
| | | account: [{ required: true, message: t('sys.login.accountPlaceholder'), trigger: 'blur' }], |
| | | password: [ |
| | | { required: true, message: t('sys.login.passwordPlaceholder'), trigger: 'blur' }, |
| | | ], |
| | | // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [], |
| | | }); |
| | | |
| | |
| | | ); |
| | | if (userInfo) { |
| | | notification.success({ |
| | | message: t('loginSuccessTitle'), |
| | | description: `${t('loginSuccessDesc')}: ${userInfo.realName}`, |
| | | message: t('sys.login.loginSuccessTitle'), |
| | | description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`, |
| | | duration: 3, |
| | | }); |
| | | } |