completepage: 🍻 多语言添加页面完成
This commit is contained in:
parent
7efa04759c
commit
d373c138fb
|
@ -1,82 +0,0 @@
|
|||
// 模拟后端动态生成路由
|
||||
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
|
||||
|
||||
/**
|
||||
* roles:页面级别权限,这里模拟二种 "admin"、"common"
|
||||
* admin:管理员角色
|
||||
* common:普通角色
|
||||
*/
|
||||
const permissionRouter = {
|
||||
path: '/permission',
|
||||
meta: {
|
||||
title: 'menus.purePermission',
|
||||
icon: 'ep:lollipop',
|
||||
rank: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/permission/page/index',
|
||||
name: 'PermissionPage',
|
||||
meta: {
|
||||
title: 'menus.purePermissionPage',
|
||||
roles: ['admin', 'common'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/permission/button/router',
|
||||
component: 'permission/button/index',
|
||||
name: 'PermissionButtonRouter',
|
||||
meta: {
|
||||
title: 'menus.purePermissionButtonRouter',
|
||||
auths: ['permission:btn:add', 'permission:btn:edit', 'permission:btn:delete'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/permission/button/login',
|
||||
component: 'permission/button/perms',
|
||||
name: 'PermissionButtonLogin',
|
||||
meta: {
|
||||
title: 'menus.purePermissionButtonLogin',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const user = {
|
||||
path: '/user',
|
||||
meta: {
|
||||
icon: 'ep:lollipop',
|
||||
title: '用户',
|
||||
rank: 1,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/user/index',
|
||||
meta: {
|
||||
icon: null,
|
||||
title: '用户index',
|
||||
rank: 2,
|
||||
roles: ['admin', 'common'],
|
||||
auths: ['*', '*::*', '*::*::*', '*', '*::*', '::*::*'],
|
||||
},
|
||||
id: '2',
|
||||
parentId: '1',
|
||||
name: '用户index',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 获取系统路由
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: '/mock/get-async-routes',
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: [permissionRouter, user],
|
||||
};
|
||||
},
|
||||
},
|
||||
]);
|
16
mock/i18n.ts
16
mock/i18n.ts
|
@ -1,16 +0,0 @@
|
|||
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
|
||||
import en from './i18n/en';
|
||||
import zh from './i18n/zh';
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: '/mock/getI18n',
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
code: 200,
|
||||
data: { zh, en, local: 'zh' },
|
||||
};
|
||||
},
|
||||
},
|
||||
]);
|
|
@ -1,20 +0,0 @@
|
|||
import { buttons } from "./en/buttons";
|
||||
import { search } from "./en/search";
|
||||
import { panel } from "./en/panel";
|
||||
import { menus } from "./en/menus";
|
||||
import { status } from "./en/status";
|
||||
import { login } from "./en/login";
|
||||
import { style } from "./en/style";
|
||||
import { system } from "./en/system";
|
||||
|
||||
export default {
|
||||
name: "en",
|
||||
buttons,
|
||||
search,
|
||||
panel,
|
||||
menus,
|
||||
status,
|
||||
login,
|
||||
style,
|
||||
system
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
export const buttons = {
|
||||
openSystemSet: "Open System Configs",
|
||||
pureOpenSystemSet: "pureOpenSystemSet",
|
||||
|
||||
pureAccountSettings: "Account",
|
||||
pureLoginOut: "LoginOut",
|
||||
pureLogin: "Login",
|
||||
pureReload: "Reload",
|
||||
pureCloseCurrentTab: "Close CurrentTab",
|
||||
pureCloseLeftTabs: "Close LeftTabs",
|
||||
pureCloseRightTabs: "Close RightTabs",
|
||||
pureCloseOtherTabs: "Close OtherTabs",
|
||||
pureCloseAllTabs: "Close AllTabs",
|
||||
pureContentFullScreen: "Content FullScreen",
|
||||
pureContentExitFullScreen: "Content ExitFullScreen",
|
||||
pureClickCollapse: "Collapse",
|
||||
pureClickExpand: "Expand",
|
||||
confirm: "Confirm",
|
||||
pureSwitch: "Switch",
|
||||
close: "Close",
|
||||
pureBackTop: "BackTop",
|
||||
pureOpenText: "Open",
|
||||
pureCloseText: "Close",
|
||||
rest: "Rest"
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
export const login = {
|
||||
loginSuccess: "Login Success",
|
||||
loginFail: "Login Fail",
|
||||
usernameRegex: "please input username",
|
||||
username: "input username",
|
||||
password: "input password",
|
||||
login: "Login",
|
||||
|
||||
email: "email",
|
||||
repeatPassword: "Sure Password",
|
||||
emailCode: "input email code",
|
||||
verifyCode: "verify code",
|
||||
emailRegex: "please input email",
|
||||
passwordRegex: "please input password",
|
||||
passwordSureRegex: "Please entr confirm password",
|
||||
passwordDifferentRegex: "The two passwords do not match!",
|
||||
emailCodeRegex: "please input email code",
|
||||
getEmailCode: "get email code",
|
||||
rememberMe: "days no need to login",
|
||||
rememberInfo:
|
||||
"After checking and logging in, will automatically log in to the system without entering your username and password within the specified number of days.",
|
||||
forgetPassword: "Forget Password?",
|
||||
getCodeInfo: "Seconds",
|
||||
getVerifyCode: "Get VerifyCode",
|
||||
definite: "definite",
|
||||
back: "back",
|
||||
passWordUpdateReg: "Password has been updated",
|
||||
pureTip: 'After scanning the code, click "Confirm" to complete the login',
|
||||
pureRegisterSuccess: "Regist Success",
|
||||
pureTickPrivacy: "Please tick Privacy Policy",
|
||||
pureReadAccept: "I have read it carefully and accept",
|
||||
purePrivacyPolicy: "Privacy Policy",
|
||||
pureVerifyCodeReg: "Please enter verify code",
|
||||
pureVerifyCodeCorrectReg: "Please enter correct verify code",
|
||||
pureVerifyCodeSixReg: "Please enter a 6-digit verify code",
|
||||
purePhoneReg: "Please enter the phone",
|
||||
purePhoneCorrectReg: "Please enter the correct phone number format",
|
||||
purePassWordRuleReg:
|
||||
"The password format should be any combination of 8-18 digits"
|
||||
};
|
|
@ -1,120 +0,0 @@
|
|||
export const menus = {
|
||||
home: "Home",
|
||||
purePermissionButtonRouter: "PermissionButtonRouter",
|
||||
purePermissionButtonLogin: "purePermissionButtonLogin",
|
||||
pureLogin: "Login",
|
||||
pureEmpty: "Empty Page",
|
||||
pureTable: "Table",
|
||||
pureSysManagement: "System Manage",
|
||||
pureUser: "User Manage",
|
||||
pureRole: "Role Manage",
|
||||
pureSystemMenu: "Menu Manage",
|
||||
pureDept: "Dept Manage",
|
||||
pureSysMonitor: "System Monitor",
|
||||
pureOnlineUser: "Online User",
|
||||
pureLoginLog: "Login Log",
|
||||
pureOperationLog: "Operation Log",
|
||||
pureSystemLog: "System Log",
|
||||
pureEditor: "Editor",
|
||||
pureAbnormal: "Abnormal Page",
|
||||
pureFourZeroFour: "404",
|
||||
pureFourZeroOne: "403",
|
||||
pureFive: "500",
|
||||
pureComponents: "Components",
|
||||
pureDialog: "Dialog",
|
||||
pureMessage: "Message Tips",
|
||||
pureVideo: "Video",
|
||||
pureSegmented: "Segmented",
|
||||
pureWaterfall: "Waterfall",
|
||||
pureMap: "Map",
|
||||
pureDraggable: "Draggable",
|
||||
pureSplitPane: "Split Pane",
|
||||
pureText: "Text Ellipsis",
|
||||
pureElButton: "Button",
|
||||
pureButton: "Button Animation",
|
||||
pureCheckButton: "Check Button",
|
||||
pureCropping: "Picture Cropping",
|
||||
pureAnimatecss: "AnimateCss Selector",
|
||||
pureCountTo: "Digital Animation",
|
||||
pureSelector: "Scope Selector",
|
||||
pureFlowChart: "Flow Chart",
|
||||
pureSeamless: "Seamless Scroll",
|
||||
pureContextmenu: "Context Menu",
|
||||
pureTypeit: "Typeit",
|
||||
pureJsonEditor: "JSON Editor",
|
||||
pureColorPicker: "Color Picker",
|
||||
pureDatePicker: "Date Picker",
|
||||
pureDateTimePicker: "DateTimePicker",
|
||||
pureTimePicker: "TimePicker",
|
||||
pureTag: "Tag",
|
||||
pureStatistic: "Statistic",
|
||||
pureCollapse: "Collapse",
|
||||
pureGanttastic: "Gantt Chart",
|
||||
pureProgress: "Progress",
|
||||
pureUpload: "File Upload",
|
||||
pureCheckCard: "CheckCard",
|
||||
pureMenus: "MultiLevel Menu",
|
||||
pureMenu1: "Menu1",
|
||||
pureMenu2: "Menu2",
|
||||
purePermission: "Permission Manage",
|
||||
purePermissionPage: "Page Permission",
|
||||
purePermissionButton: "Button Permission",
|
||||
pureTabs: "Tabs Operate",
|
||||
pureGuide: "Guide",
|
||||
pureAble: "Able",
|
||||
pureMenuTree: "Menu Tree",
|
||||
pureVideoFrame: "Video Frame Capture",
|
||||
pureWavesurfer: "Audio Visualization",
|
||||
pureRipple: "Ripple",
|
||||
pureMqtt: "Mqtt Client",
|
||||
pureOptimize: "Debounce、Throttle、Copy、Longpress Directives",
|
||||
pureVerify: "Captcha",
|
||||
pureWatermark: "Water Mark",
|
||||
purePrint: "Print",
|
||||
pureDownload: "Download",
|
||||
pureExternalPage: "External Page",
|
||||
pureExternalDoc: "Docs External",
|
||||
pureEmbeddedDoc: "Docs Embedded",
|
||||
pureExternalLink: "Vue-Pure-Admin",
|
||||
pureUtilsLink: "Pure-Admin-Utils",
|
||||
pureColorHuntDoc: "ColorHunt",
|
||||
pureUiGradients: "UiGradients",
|
||||
pureEpDoc: "Element-Plus",
|
||||
pureTailwindcssDoc: "Tailwindcss",
|
||||
pureVueDoc: "Vue3",
|
||||
pureViteDoc: "Vite",
|
||||
purePiniaDoc: "Pinia",
|
||||
pureRouterDoc: "Vue-Router",
|
||||
pureAbout: "About",
|
||||
pureResult: "Result Page",
|
||||
pureSuccess: "Success Page",
|
||||
pureFail: "Fail Page",
|
||||
pureIconSelect: "Icon Select",
|
||||
pureTimeline: "Time Line",
|
||||
pureLineTree: "LineTree",
|
||||
pureList: "List Page",
|
||||
pureCardList: "Card List Page",
|
||||
pureDebounce: "Debounce & Throttle",
|
||||
pureFormDesign: "Form Design",
|
||||
pureBarcode: "Barcode",
|
||||
pureQrcode: "Qrcode",
|
||||
pureCascader: "Area Cascader",
|
||||
pureSwiper: "Swiper Plugin",
|
||||
pureVirtualList: "Virtual List",
|
||||
purePdf: "PDF Preview",
|
||||
pureExcel: "Export Excel",
|
||||
pureInfiniteScroll: "Table Infinite Scroll",
|
||||
pureSensitive: "Sensitive Filter",
|
||||
purePinyin: "PinYin",
|
||||
pureDanmaku: "Danmaku",
|
||||
pureSchemaForm: "Form",
|
||||
pureTableBase: "Base Usage",
|
||||
pureTableHigh: "High Usage",
|
||||
pureTableEdit: "Edit Usage",
|
||||
pureVxeTable: "Virtual Usage",
|
||||
pureBoard: "Paint Board",
|
||||
pureMindMap: "Mind Map",
|
||||
pureMenuOverflow: "Menu Overflow Show Tooltip Text",
|
||||
pureChildMenuOverflow: "Child Menu Overflow Show Tooltip Text",
|
||||
systemctlTest: "Systemctl lTest"
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
export const panel = {
|
||||
pureSystemSet: "System Configs",
|
||||
pureCloseSystemSet: "Close System Configs",
|
||||
pureClearCacheAndToLogin: "Clear cache and return to login page",
|
||||
pureClearCache: "Clear Cache",
|
||||
pureOverallStyle: "Overall Style",
|
||||
pureOverallStyleLight: "Light",
|
||||
pureOverallStyleLightTip:
|
||||
"Set sail freshly and light up the comfortable work interface",
|
||||
pureOverallStyleDark: "Dark",
|
||||
pureOverallStyleDarkTip:
|
||||
"Moonlight Overture, indulge in the tranquility and elegance of the night",
|
||||
pureOverallStyleSystem: "Auto",
|
||||
pureOverallStyleSystemTip:
|
||||
"Synchronize time, the interface naturally responds to morning and dusk",
|
||||
pureThemeColor: "Theme Color",
|
||||
pureLayoutModel: "Layout Model",
|
||||
pureVerticalTip: "The menu on the left is familiar and friendly",
|
||||
pureHorizontalTip: "Top menu, concise overview",
|
||||
pureMixTip: "Mixed menu, flexible",
|
||||
pureStretch: "Stretch Page",
|
||||
pureStretchFixed: "Fixed",
|
||||
pureStretchFixedTip:
|
||||
"Compact pages make it easy to find the information you need",
|
||||
pureStretchCustom: "Custom",
|
||||
pureStretchCustomTip: "Minimum 1280, maximum 1600",
|
||||
pureTagsStyle: "Tags Style",
|
||||
pureTagsStyleSmart: "Smart",
|
||||
pureTagsStyleSmartTip: "Smart tags add fun and brilliance",
|
||||
pureTagsStyleCard: "Card",
|
||||
pureTagsStyleCardTip: "Card tags for efficient browsing",
|
||||
pureInterfaceDisplay: "Interface Display",
|
||||
pureGreyModel: "Grey Model",
|
||||
pureWeakModel: "Weak Model",
|
||||
pureHiddenTags: "Hidden Tags",
|
||||
pureHiddenFooter: "Hidden Footer",
|
||||
pureMultiTagsCache: "MultiTags Cache"
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
export const search = {
|
||||
pureTotal: "Total",
|
||||
pureHistory: "History",
|
||||
pureCollect: "Collect",
|
||||
pureDragSort: "(Drag Sort)",
|
||||
pureEmpty: "Empty",
|
||||
purePlaceholder: "Search Menu"
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
export const status = {
|
||||
pureLoad: "Loading...",
|
||||
pureMessage: "Message",
|
||||
pureNotify: "Notify",
|
||||
pureTodo: "Todo",
|
||||
pureNoMessage: "No Message",
|
||||
pureNoNotify: "No Notify",
|
||||
pureNoTodo: "No Todo",
|
||||
enable: "enable",
|
||||
disable: "disable"
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
export const style = {
|
||||
larger: "Larger",
|
||||
default: "Default",
|
||||
small: "Small"
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
export const system = {
|
||||
carousel: "carousel setting",
|
||||
config: "system config",
|
||||
favicon: "system favicon",
|
||||
feedback: "system feedback",
|
||||
emailUsers: "email users",
|
||||
log: "system log"
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { buttons } from "./zh/buttons";
|
||||
import { search } from "./zh/search";
|
||||
import { panel } from "./zh/panel";
|
||||
import { menus } from "./zh/menus";
|
||||
import { status } from "./zh/status";
|
||||
import { login } from "./zh/login";
|
||||
import { style } from "./zh/style";
|
||||
import { system } from "./zh/system";
|
||||
|
||||
export default {
|
||||
name: "zh",
|
||||
buttons,
|
||||
search,
|
||||
panel,
|
||||
menus,
|
||||
status,
|
||||
login,
|
||||
style,
|
||||
system
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
export const buttons = {
|
||||
openSystemSet: "打开系统配置",
|
||||
pureOpenSystemSet: "权限设定",
|
||||
|
||||
pureAccountSettings: "账户设置",
|
||||
pureLoginOut: "退出系统",
|
||||
pureLogin: "登录",
|
||||
pureReload: "重新加载",
|
||||
pureCloseCurrentTab: "关闭当前标签页",
|
||||
pureCloseLeftTabs: "关闭左侧标签页",
|
||||
pureCloseRightTabs: "关闭右侧标签页",
|
||||
pureCloseOtherTabs: "关闭其他标签页",
|
||||
pureCloseAllTabs: "关闭全部标签页",
|
||||
pureContentFullScreen: "内容区全屏",
|
||||
pureContentExitFullScreen: "内容区退出全屏",
|
||||
pureClickCollapse: "点击折叠",
|
||||
pureClickExpand: "点击展开",
|
||||
confirm: "确认",
|
||||
pureSwitch: "切换",
|
||||
close: "关闭",
|
||||
pureBackTop: "回到顶部",
|
||||
pureOpenText: "开",
|
||||
pureCloseText: "关",
|
||||
rest: "重置",
|
||||
search: "搜索"
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
export const login = {
|
||||
loginSuccess: "登录成功",
|
||||
loginFail: "登录失败",
|
||||
usernameRegex: "请输入账号",
|
||||
username: "输入用户名",
|
||||
password: "输入密码",
|
||||
login: "登录",
|
||||
|
||||
email: "输入邮箱",
|
||||
repeatPassword: "确认密码",
|
||||
emailCode: "邮箱验证码",
|
||||
verifyCode: "图形验证码",
|
||||
emailRegex: "输入邮箱",
|
||||
passwordRegex: "请输入密码",
|
||||
passwordSureRegex: "请输入确认密码",
|
||||
repeatPasswordRegex: "请输入确认密码",
|
||||
passwordDifferentRegex: "两次密码不一致!",
|
||||
emailCodeRegex: "请输入邮箱验证码",
|
||||
verifyCodeRegex: "输入验证码",
|
||||
getEmailCode: "获取邮箱验证码",
|
||||
rememberMe: "天内免登录",
|
||||
rememberInfo: "勾选并登录后,规定天数内无需输入用户名和密码会自动登入系统",
|
||||
forgetPassword: "忘记密码?",
|
||||
getVerifyCode: "获取验证码",
|
||||
definite: "确定",
|
||||
back: "返回",
|
||||
getCodeInfo: "秒后重新获取",
|
||||
passWordUpdateReg: "修改密码成功",
|
||||
pureRegisterSuccess: "注册成功",
|
||||
pureTickPrivacy: "请勾选隐私政策",
|
||||
pureReadAccept: "我已仔细阅读并接受",
|
||||
purePrivacyPolicy: "《隐私政策》",
|
||||
pureVerifyCodeCorrectReg: "请输入正确的验证码",
|
||||
pureVerifyCodeSixReg: "请输入6位数字验证码",
|
||||
purePhoneReg: "请输入手机号码",
|
||||
purePhoneCorrectReg: "请输入正确的手机号码格式",
|
||||
purePassWordRuleReg: "密码格式应为8-18位数字、字母、符号的任意两种组合"
|
||||
};
|
|
@ -1,120 +0,0 @@
|
|||
export const menus = {
|
||||
home: "首页",
|
||||
purePermissionButtonRouter: "权限1",
|
||||
purePermissionButtonLogin: "权限2",
|
||||
pureLogin: "登录",
|
||||
pureEmpty: "无Layout页",
|
||||
pureTable: "表格",
|
||||
pureSysManagement: "系统管理",
|
||||
pureUser: "用户管理",
|
||||
pureRole: "角色管理",
|
||||
pureSystemMenu: "菜单管理",
|
||||
pureDept: "部门管理",
|
||||
pureSysMonitor: "系统监控",
|
||||
pureOnlineUser: "在线用户",
|
||||
pureLoginLog: "登录日志",
|
||||
pureOperationLog: "操作日志",
|
||||
pureSystemLog: "系统日志",
|
||||
pureEditor: "编辑器",
|
||||
pureAbnormal: "异常页面",
|
||||
pureFourZeroFour: "404",
|
||||
pureFourZeroOne: "403",
|
||||
pureFive: "500",
|
||||
pureComponents: "组件",
|
||||
pureDialog: "函数式弹框",
|
||||
pureMessage: "消息提示",
|
||||
pureVideo: "视频",
|
||||
pureSegmented: "分段控制器",
|
||||
pureWaterfall: "瀑布流无限滚动",
|
||||
pureMap: "地图",
|
||||
pureDraggable: "拖拽",
|
||||
pureSplitPane: "切割面板",
|
||||
pureText: "文本省略",
|
||||
pureElButton: "按钮",
|
||||
pureCheckButton: "可选按钮",
|
||||
pureButton: "按钮动效",
|
||||
pureCropping: "图片裁剪",
|
||||
pureAnimatecss: "animate.css选择器",
|
||||
pureCountTo: "数字动画",
|
||||
pureSelector: "范围选择器",
|
||||
pureFlowChart: "流程图",
|
||||
pureSeamless: "无缝滚动",
|
||||
pureContextmenu: "右键菜单",
|
||||
pureTypeit: "打字机",
|
||||
pureJsonEditor: "JSON编辑器",
|
||||
pureColorPicker: "颜色选择器",
|
||||
pureDatePicker: "日期选择器",
|
||||
pureDateTimePicker: "日期时间选择器",
|
||||
pureTimePicker: "时间选择器",
|
||||
pureTag: "标签",
|
||||
pureStatistic: "统计组件",
|
||||
pureCollapse: "折叠面板",
|
||||
pureGanttastic: "甘特图",
|
||||
pureProgress: "进度条",
|
||||
pureUpload: "文件上传",
|
||||
pureCheckCard: "多选卡片",
|
||||
pureMenus: "多级菜单",
|
||||
pureMenu1: "菜单1",
|
||||
pureMenu2: "菜单2",
|
||||
purePermission: "权限管理",
|
||||
purePermissionPage: "页面权限",
|
||||
purePermissionButton: "按钮权限",
|
||||
pureTabs: "标签页操作",
|
||||
pureGuide: "引导页",
|
||||
pureAble: "功能",
|
||||
pureMenuTree: "菜单树结构",
|
||||
pureVideoFrame: "视频帧截取-wasm版",
|
||||
pureWavesurfer: "音频可视化",
|
||||
pureRipple: "波纹(Ripple)",
|
||||
pureMqtt: "MQTT客户端(mqtt)",
|
||||
pureOptimize: "防抖、截流、复制、长按指令",
|
||||
pureVerify: "图形验证码",
|
||||
pureWatermark: "水印",
|
||||
purePrint: "打印",
|
||||
pureDownload: "下载",
|
||||
pureExternalPage: "外部页面",
|
||||
pureExternalDoc: "文档外链",
|
||||
pureEmbeddedDoc: "文档内嵌",
|
||||
pureExternalLink: "vue-pure-admin",
|
||||
pureUtilsLink: "pure-admin-utils",
|
||||
pureColorHuntDoc: "调色板",
|
||||
pureUiGradients: "渐变色",
|
||||
pureEpDoc: "element-plus",
|
||||
pureTailwindcssDoc: "tailwindcss",
|
||||
pureVueDoc: "vue3",
|
||||
pureViteDoc: "vite",
|
||||
purePiniaDoc: "pinia",
|
||||
pureRouterDoc: "vue-router",
|
||||
pureAbout: "关于",
|
||||
pureResult: "结果页面",
|
||||
pureSuccess: "成功页面",
|
||||
pureFail: "失败页面",
|
||||
pureIconSelect: "图标选择器",
|
||||
pureTimeline: "时间线",
|
||||
pureLineTree: "树形连接线",
|
||||
pureList: "列表页面",
|
||||
pureCardList: "卡片列表页",
|
||||
pureDebounce: "防抖节流",
|
||||
pureFormDesign: "表单设计器",
|
||||
pureBarcode: "条形码",
|
||||
pureQrcode: "二维码",
|
||||
pureCascader: "区域级联选择器",
|
||||
pureSwiper: "Swiper插件",
|
||||
pureVirtualList: "虚拟列表",
|
||||
purePdf: "PDF预览",
|
||||
pureExcel: "导出Excel",
|
||||
pureInfiniteScroll: "表格无限滚动",
|
||||
pureSensitive: "敏感词过滤",
|
||||
purePinyin: "汉语拼音",
|
||||
pureDanmaku: "弹幕",
|
||||
pureSchemaForm: "表单",
|
||||
pureTableBase: "基础用法",
|
||||
pureTableHigh: "高级用法",
|
||||
pureTableEdit: "可编辑用法",
|
||||
pureVxeTable: "虚拟滚动",
|
||||
pureBoard: "艺术画板",
|
||||
pureMindMap: "思维导图",
|
||||
pureMenuOverflow: "目录超出显示 Tooltip 文字提示",
|
||||
pureChildMenuOverflow: "菜单超出显示 Tooltip 文字提示",
|
||||
systemctlTest: "系统测试"
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
export const panel = {
|
||||
pureSystemSet: "系统配置",
|
||||
pureCloseSystemSet: "关闭配置",
|
||||
pureClearCacheAndToLogin: "清空缓存并返回登录页",
|
||||
pureClearCache: "清空缓存",
|
||||
pureOverallStyle: "整体风格",
|
||||
pureOverallStyleLight: "浅色",
|
||||
pureOverallStyleLightTip: "清新启航,点亮舒适的工作界面",
|
||||
pureOverallStyleDark: "深色",
|
||||
pureOverallStyleDarkTip: "月光序曲,沉醉于夜的静谧雅致",
|
||||
pureOverallStyleSystem: "自动",
|
||||
pureOverallStyleSystemTip: "同步时光,界面随晨昏自然呼应",
|
||||
pureThemeColor: "主题色",
|
||||
pureLayoutModel: "导航模式",
|
||||
pureVerticalTip: "左侧菜单,亲切熟悉",
|
||||
pureHorizontalTip: "顶部菜单,简洁概览",
|
||||
pureMixTip: "混合菜单,灵活多变",
|
||||
pureStretch: "页宽",
|
||||
pureStretchFixed: "固定",
|
||||
pureStretchFixedTip: "紧凑页面,轻松找到所需信息",
|
||||
pureStretchCustom: "自定义",
|
||||
pureStretchCustomTip: "最小1280、最大1600",
|
||||
pureTagsStyle: "页签风格",
|
||||
pureTagsStyleSmart: "灵动",
|
||||
pureTagsStyleSmartTip: "灵动标签,添趣生辉",
|
||||
pureTagsStyleCard: "卡片",
|
||||
pureTagsStyleCardTip: "卡片标签,高效浏览",
|
||||
pureInterfaceDisplay: "界面显示",
|
||||
pureGreyModel: "灰色模式",
|
||||
pureWeakModel: "色弱模式",
|
||||
pureHiddenTags: "隐藏标签页",
|
||||
pureHiddenFooter: "隐藏页脚",
|
||||
pureMultiTagsCache: "页签持久化"
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
export const search = {
|
||||
search: {
|
||||
pureTotal: "共",
|
||||
pureHistory: "搜索历史",
|
||||
pureCollect: "收藏",
|
||||
pureDragSort: "(可拖拽排序)",
|
||||
pureEmpty: "暂无搜索结果",
|
||||
purePlaceholder: "搜索菜单(支持拼音搜索)"
|
||||
}
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
export const status = {
|
||||
pureLoad: "加载中...",
|
||||
pureMessage: "消息",
|
||||
pureNotify: "通知",
|
||||
pureTodo: "待办",
|
||||
pureNoMessage: "暂无消息",
|
||||
pureNoNotify: "暂无通知",
|
||||
pureNoTodo: "暂无待办",
|
||||
enable: "启用",
|
||||
disable: "不启用"
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
export const style = {
|
||||
larger: "宽松",
|
||||
default: "默认",
|
||||
small: "紧凑"
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
export const system = {
|
||||
config: "系统设置",
|
||||
carousel: "轮播图设置",
|
||||
favicon: "图标设置",
|
||||
feedback: "用户反馈",
|
||||
emailUsers: "邮件用户",
|
||||
log: "系统日志"
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
// 根据角色动态生成路由
|
||||
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: "/mock/login",
|
||||
method: "post",
|
||||
response: ({ body }) => {
|
||||
if (body.username === "admin") {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||
username: "admin",
|
||||
nickname: "小铭",
|
||||
// 一个用户可能有多个角色
|
||||
roles: ["admin"],
|
||||
// 按钮级别权限
|
||||
permissions: ["*:*:*"],
|
||||
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
|
||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
|
||||
expires: "2030/10/30 00:00:00"
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatar: "https://avatars.githubusercontent.com/u/52823142",
|
||||
username: "common",
|
||||
nickname: "小林",
|
||||
roles: ["common"],
|
||||
permissions: ["permission:btn:add", "permission:btn:edit"],
|
||||
accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
|
||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
|
||||
expires: "2030/10/30 00:00:00"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
|
@ -82,7 +82,7 @@ class PureHttp {
|
|||
return whiteList.some(url => config.url.endsWith(url))
|
||||
? config
|
||||
: new Promise(resolve => {
|
||||
const data = getToken();
|
||||
const data: any = getToken();
|
||||
if (data) {
|
||||
const now = new Date().getTime();
|
||||
const expired = parseInt(data.expires) - now <= 0;
|
||||
|
|
|
@ -154,8 +154,6 @@ class PureHttp {
|
|||
|
||||
// 关闭进度条动画
|
||||
NProgress.done();
|
||||
message(error.message, { type: 'error' });
|
||||
|
||||
return error;
|
||||
},
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ export const fetchGetI18n = () => {
|
|||
* 多语言管理---获取多语言列表
|
||||
*/
|
||||
export const fetchGetI18nList = (data: any) => {
|
||||
return http.request<BaseResult<ResultTable>>('get', `i18n/getI18nList/${data.currentPage}/${data.pageSize}`, { data });
|
||||
return http.request<BaseResult<ResultTable>>('get', `i18n/getI18nList/${data.currentPage}/${data.pageSize}`, { params: data });
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,114 +1,114 @@
|
|||
export const animates = [
|
||||
/* Attention seekers */
|
||||
"bounce",
|
||||
"flash",
|
||||
"pulse",
|
||||
"rubberBand",
|
||||
"shakeX",
|
||||
"headShake",
|
||||
"swing",
|
||||
"tada",
|
||||
"wobble",
|
||||
"jello",
|
||||
"heartBeat",
|
||||
/* Back entrances */
|
||||
"backInDown",
|
||||
"backInLeft",
|
||||
"backInRight",
|
||||
"backInUp",
|
||||
/* Back exits */
|
||||
"backOutDown",
|
||||
"backOutLeft",
|
||||
"backOutRight",
|
||||
"backOutUp",
|
||||
/* Bouncing entrances */
|
||||
"bounceIn",
|
||||
"bounceInDown",
|
||||
"bounceInLeft",
|
||||
"bounceInRight",
|
||||
"bounceInUp",
|
||||
/* Bouncing exits */
|
||||
"bounceOut",
|
||||
"bounceOutDown",
|
||||
"bounceOutLeft",
|
||||
"bounceOutRight",
|
||||
"bounceOutUp",
|
||||
/* Fading entrances */
|
||||
"fadeIn",
|
||||
"fadeInDown",
|
||||
"fadeInDownBig",
|
||||
"fadeInLeft",
|
||||
"fadeInLeftBig",
|
||||
"fadeInRight",
|
||||
"fadeInRightBig",
|
||||
"fadeInUp",
|
||||
"fadeInUpBig",
|
||||
"fadeInTopLeft",
|
||||
"fadeInTopRight",
|
||||
"fadeInBottomLeft",
|
||||
"fadeInBottomRight",
|
||||
/* Fading exits */
|
||||
"fadeOut",
|
||||
"fadeOutDown",
|
||||
"fadeOutDownBig",
|
||||
"fadeOutLeft",
|
||||
"fadeOutLeftBig",
|
||||
"fadeOutRight",
|
||||
"fadeOutRightBig",
|
||||
"fadeOutUp",
|
||||
"fadeOutUpBig",
|
||||
"fadeOutTopLeft",
|
||||
"fadeOutTopRight",
|
||||
"fadeOutBottomRight",
|
||||
"fadeOutBottomLeft",
|
||||
/* Flippers */
|
||||
"flip",
|
||||
"flipInX",
|
||||
"flipInY",
|
||||
"flipOutX",
|
||||
"flipOutY",
|
||||
/* Lightspeed */
|
||||
"lightSpeedInRight",
|
||||
"lightSpeedInLeft",
|
||||
"lightSpeedOutRight",
|
||||
"lightSpeedOutLeft",
|
||||
/* Rotating entrances */
|
||||
"rotateIn",
|
||||
"rotateInDownLeft",
|
||||
"rotateInDownRight",
|
||||
"rotateInUpLeft",
|
||||
"rotateInUpRight",
|
||||
/* Rotating exits */
|
||||
"rotateOut",
|
||||
"rotateOutDownLeft",
|
||||
"rotateOutDownRight",
|
||||
"rotateOutUpLeft",
|
||||
"rotateOutUpRight",
|
||||
/* Specials */
|
||||
"hinge",
|
||||
"jackInTheBox",
|
||||
"rollIn",
|
||||
"rollOut",
|
||||
/* Zooming entrances */
|
||||
"zoomIn",
|
||||
"zoomInDown",
|
||||
"zoomInLeft",
|
||||
"zoomInRight",
|
||||
"zoomInUp",
|
||||
/* Zooming exits */
|
||||
"zoomOut",
|
||||
"zoomOutDown",
|
||||
"zoomOutLeft",
|
||||
"zoomOutRight",
|
||||
"zoomOutUp",
|
||||
/* Sliding entrances */
|
||||
"slideInDown",
|
||||
"slideInLeft",
|
||||
"slideInRight",
|
||||
"slideInUp",
|
||||
/* Sliding exits */
|
||||
"slideOutDown",
|
||||
"slideOutLeft",
|
||||
"slideOutRight",
|
||||
"slideOutUp"
|
||||
/* Attention seekers */
|
||||
'bounce',
|
||||
'flash',
|
||||
'pulse',
|
||||
'rubberBand',
|
||||
'shakeX',
|
||||
'headShake',
|
||||
'swing',
|
||||
'tada',
|
||||
'wobble',
|
||||
'jello',
|
||||
'heartBeat',
|
||||
/* Back entrances */
|
||||
'backInDown',
|
||||
'backInLeft',
|
||||
'backInRight',
|
||||
'backInUp',
|
||||
/* Back exits */
|
||||
'backOutDown',
|
||||
'backOutLeft',
|
||||
'backOutRight',
|
||||
'backOutUp',
|
||||
/* Bouncing entrances */
|
||||
'bounceIn',
|
||||
'bounceInDown',
|
||||
'bounceInLeft',
|
||||
'bounceInRight',
|
||||
'bounceInUp',
|
||||
/* Bouncing exits */
|
||||
'bounceOut',
|
||||
'bounceOutDown',
|
||||
'bounceOutLeft',
|
||||
'bounceOutRight',
|
||||
'bounceOutUp',
|
||||
/* Fading entrances */
|
||||
'fadeIn',
|
||||
'fadeInDown',
|
||||
'fadeInDownBig',
|
||||
'fadeInLeft',
|
||||
'fadeInLeftBig',
|
||||
'fadeInRight',
|
||||
'fadeInRightBig',
|
||||
'fadeInUp',
|
||||
'fadeInUpBig',
|
||||
'fadeInTopLeft',
|
||||
'fadeInTopRight',
|
||||
'fadeInBottomLeft',
|
||||
'fadeInBottomRight',
|
||||
/* Fading exits */
|
||||
'fadeOut',
|
||||
'fadeOutDown',
|
||||
'fadeOutDownBig',
|
||||
'fadeOutLeft',
|
||||
'fadeOutLeftBig',
|
||||
'fadeOutRight',
|
||||
'fadeOutRightBig',
|
||||
'fadeOutUp',
|
||||
'fadeOutUpBig',
|
||||
'fadeOutTopLeft',
|
||||
'fadeOutTopRight',
|
||||
'fadeOutBottomRight',
|
||||
'fadeOutBottomLeft',
|
||||
/* Flippers */
|
||||
'flip',
|
||||
'flipInX',
|
||||
'flipInY',
|
||||
'flipOutX',
|
||||
'flipOutY',
|
||||
/* Lightspeed */
|
||||
'lightSpeedInRight',
|
||||
'lightSpeedInLeft',
|
||||
'lightSpeedOutRight',
|
||||
'lightSpeedOutLeft',
|
||||
/* Rotating entrances */
|
||||
'rotateIn',
|
||||
'rotateInDownLeft',
|
||||
'rotateInDownRight',
|
||||
'rotateInUpLeft',
|
||||
'rotateInUpRight',
|
||||
/* Rotating exits */
|
||||
'rotateOut',
|
||||
'rotateOutDownLeft',
|
||||
'rotateOutDownRight',
|
||||
'rotateOutUpLeft',
|
||||
'rotateOutUpRight',
|
||||
/* Specials */
|
||||
'hinge',
|
||||
'jackInTheBox',
|
||||
'rollIn',
|
||||
'rollOut',
|
||||
/* Zooming entrances */
|
||||
'zoomIn',
|
||||
'zoomInDown',
|
||||
'zoomInLeft',
|
||||
'zoomInRight',
|
||||
'zoomInUp',
|
||||
/* Zooming exits */
|
||||
'zoomOut',
|
||||
'zoomOutDown',
|
||||
'zoomOutLeft',
|
||||
'zoomOutRight',
|
||||
'zoomOutUp',
|
||||
/* Sliding entrances */
|
||||
'slideInDown',
|
||||
'slideInLeft',
|
||||
'slideInRight',
|
||||
'slideInUp',
|
||||
/* Sliding exits */
|
||||
'slideOutDown',
|
||||
'slideOutLeft',
|
||||
'slideOutRight',
|
||||
'slideOutUp',
|
||||
];
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { animates } from "./animate";
|
||||
import { cloneDeep } from "@pureadmin/utils";
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { animates } from './animate';
|
||||
import { cloneDeep } from '@pureadmin/utils';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
defineOptions({
|
||||
name: "ReAnimateSelector"
|
||||
name: 'ReAnimateSelector',
|
||||
});
|
||||
|
||||
defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择动画"
|
||||
}
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: $t('please_select_animation'),
|
||||
},
|
||||
});
|
||||
|
||||
const inputValue = defineModel({ type: String });
|
||||
|
@ -21,116 +22,78 @@ const animatesList = ref(animates);
|
|||
const copyAnimatesList = cloneDeep(animatesList);
|
||||
|
||||
const animateClass = computed(() => {
|
||||
return [
|
||||
"mt-1",
|
||||
"flex",
|
||||
"border",
|
||||
"w-[130px]",
|
||||
"h-[100px]",
|
||||
"items-center",
|
||||
"cursor-pointer",
|
||||
"transition-all",
|
||||
"justify-center",
|
||||
"border-[#e5e7eb]",
|
||||
"hover:text-primary",
|
||||
"hover:duration-[700ms]"
|
||||
];
|
||||
return ['mt-1', 'flex', 'border', 'w-[130px]', 'h-[100px]', 'items-center', 'cursor-pointer', 'transition-all', 'justify-center', 'border-[#e5e7eb]', 'hover:text-primary', 'hover:duration-[700ms]'];
|
||||
});
|
||||
|
||||
const animateStyle = computed(
|
||||
() => (i: string) =>
|
||||
inputValue.value === i
|
||||
? {
|
||||
borderColor: "var(--el-color-primary)",
|
||||
color: "var(--el-color-primary)"
|
||||
}
|
||||
: ""
|
||||
() => (i: string) =>
|
||||
inputValue.value === i
|
||||
? {
|
||||
borderColor: 'var(--el-color-primary)',
|
||||
color: 'var(--el-color-primary)',
|
||||
}
|
||||
: '',
|
||||
);
|
||||
|
||||
function onChangeIcon(animate: string) {
|
||||
inputValue.value = animate;
|
||||
inputValue.value = animate;
|
||||
}
|
||||
|
||||
function onClear() {
|
||||
inputValue.value = "";
|
||||
inputValue.value = '';
|
||||
}
|
||||
|
||||
function filterMethod(value: any) {
|
||||
searchVal.value = value;
|
||||
animatesList.value = copyAnimatesList.value.filter((i: string | any[]) =>
|
||||
i.includes(value)
|
||||
);
|
||||
searchVal.value = value;
|
||||
animatesList.value = copyAnimatesList.value.filter((i: string | any[]) => i.includes(value));
|
||||
}
|
||||
|
||||
const animateMap = ref({});
|
||||
|
||||
function onMouseEnter(index: string | number) {
|
||||
animateMap.value[index] = animateMap.value[index]?.loading
|
||||
? Object.assign({}, animateMap.value[index], {
|
||||
loading: false
|
||||
})
|
||||
: Object.assign({}, animateMap.value[index], {
|
||||
loading: true
|
||||
});
|
||||
animateMap.value[index] = animateMap.value[index]?.loading
|
||||
? Object.assign({}, animateMap.value[index], {
|
||||
loading: false,
|
||||
})
|
||||
: Object.assign({}, animateMap.value[index], {
|
||||
loading: true,
|
||||
});
|
||||
}
|
||||
|
||||
function onMouseleave() {
|
||||
animateMap.value = {};
|
||||
animateMap.value = {};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-select
|
||||
clearable
|
||||
filterable
|
||||
:placeholder="placeholder"
|
||||
popper-class="pure-animate-popper"
|
||||
:model-value="inputValue"
|
||||
:filter-method="filterMethod"
|
||||
@clear="onClear"
|
||||
>
|
||||
<template #empty>
|
||||
<div class="w-[280px]">
|
||||
<el-scrollbar
|
||||
noresize
|
||||
height="212px"
|
||||
:view-style="{ overflow: 'hidden' }"
|
||||
class="border-t border-[#e5e7eb]"
|
||||
>
|
||||
<ul class="flex flex-wrap justify-around mb-1">
|
||||
<li
|
||||
v-for="(animate, index) in animatesList"
|
||||
:key="index"
|
||||
:class="animateClass"
|
||||
:style="animateStyle(animate)"
|
||||
@mouseenter.prevent="onMouseEnter(index)"
|
||||
@mouseleave.prevent="onMouseleave"
|
||||
@click="onChangeIcon(animate)"
|
||||
>
|
||||
<h4
|
||||
:class="[
|
||||
`animate__animated animate__${
|
||||
animateMap[index]?.loading
|
||||
? animate + ' animate__infinite'
|
||||
: ''
|
||||
} `
|
||||
]"
|
||||
>
|
||||
{{ animate }}
|
||||
</h4>
|
||||
</li>
|
||||
</ul>
|
||||
<el-empty
|
||||
v-show="animatesList.length === 0"
|
||||
:description="`${searchVal} 动画不存在`"
|
||||
:image-size="60"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-select :filter-method="filterMethod" :model-value="inputValue" :placeholder="placeholder" clearable filterable popper-class="pure-animate-popper" @clear="onClear">
|
||||
<template #empty>
|
||||
<div class="w-[280px]">
|
||||
<el-scrollbar :view-style="{ overflow: 'hidden' }" class="border-t border-[#e5e7eb]" height="212px" noresize>
|
||||
<ul class="flex flex-wrap justify-around mb-1">
|
||||
<li
|
||||
v-for="(animate, index) in animatesList"
|
||||
:key="index"
|
||||
:class="animateClass"
|
||||
:style="animateStyle(animate)"
|
||||
@click="onChangeIcon(animate)"
|
||||
@mouseenter.prevent="onMouseEnter(index)"
|
||||
@mouseleave.prevent="onMouseleave"
|
||||
>
|
||||
<h4 :class="[`animate__animated animate__${animateMap[index]?.loading ? animate + ' animate__infinite' : ''} `]">
|
||||
{{ animate }}
|
||||
</h4>
|
||||
</li>
|
||||
</ul>
|
||||
<el-empty v-show="animatesList.length === 0" :description="`${searchVal} ${$t('animation_not_exist')}`" :image-size="60" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.pure-animate-popper {
|
||||
min-width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import { defineComponent, Fragment } from "vue";
|
||||
import { hasAuth } from "@/router/utils";
|
||||
import { defineComponent, Fragment } from 'vue';
|
||||
import { hasAuth } from '@/router/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: "Auth",
|
||||
props: {
|
||||
value: {
|
||||
type: undefined,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
if (!slots) return null;
|
||||
return hasAuth(props.value) ? (
|
||||
<Fragment>{slots.default?.()}</Fragment>
|
||||
) : null;
|
||||
};
|
||||
}
|
||||
name: 'Auth',
|
||||
props: {
|
||||
value: {
|
||||
type: undefined,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
if (!slots) return null;
|
||||
return hasAuth(props.value) ? <Fragment>{slots.default?.()}</Fragment> : null;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,39 +1,32 @@
|
|||
import { ref } from "vue";
|
||||
import reDialog from "./index.vue";
|
||||
import { useTimeoutFn } from "@vueuse/core";
|
||||
import { withInstall } from "@pureadmin/utils";
|
||||
import type {
|
||||
EventType,
|
||||
ArgsType,
|
||||
DialogProps,
|
||||
ButtonProps,
|
||||
DialogOptions
|
||||
} from "./type";
|
||||
import { ref } from 'vue';
|
||||
import reDialog from './index.vue';
|
||||
import { useTimeoutFn } from '@vueuse/core';
|
||||
import { withInstall } from '@pureadmin/utils';
|
||||
import type { ArgsType, ButtonProps, DialogOptions, DialogProps, EventType } from './type';
|
||||
|
||||
const dialogStore = ref<Array<DialogOptions>>([]);
|
||||
|
||||
/** 打开弹框 */
|
||||
const addDialog = (options: DialogOptions) => {
|
||||
const open = () =>
|
||||
dialogStore.value.push(Object.assign(options, { visible: true }));
|
||||
if (options?.openDelay) {
|
||||
useTimeoutFn(() => {
|
||||
open();
|
||||
}, options.openDelay);
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
const open = () => dialogStore.value.push(Object.assign(options, { visible: true }));
|
||||
if (options?.openDelay) {
|
||||
useTimeoutFn(() => {
|
||||
open();
|
||||
}, options.openDelay);
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
};
|
||||
|
||||
/** 关闭弹框 */
|
||||
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
|
||||
dialogStore.value[index].visible = false;
|
||||
options.closeCallBack && options.closeCallBack({ options, index, args });
|
||||
dialogStore.value[index].visible = false;
|
||||
options.closeCallBack && options.closeCallBack({ options, index, args });
|
||||
|
||||
const closeDelay = options?.closeDelay ?? 200;
|
||||
useTimeoutFn(() => {
|
||||
dialogStore.value.splice(index, 1);
|
||||
}, closeDelay);
|
||||
const closeDelay = options?.closeDelay ?? 200;
|
||||
useTimeoutFn(() => {
|
||||
dialogStore.value.splice(index, 1);
|
||||
}, closeDelay);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -42,13 +35,13 @@ const closeDialog = (options: DialogOptions, index: number, args?: any) => {
|
|||
* @param key 属性,默认`title`
|
||||
* @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`)
|
||||
*/
|
||||
const updateDialog = (value: any, key = "title", index = 0) => {
|
||||
dialogStore.value[index][key] = value;
|
||||
const updateDialog = (value: any, key = 'title', index = 0) => {
|
||||
dialogStore.value[index][key] = value;
|
||||
};
|
||||
|
||||
/** 关闭所有弹框 */
|
||||
const closeAllDialog = () => {
|
||||
dialogStore.value = [];
|
||||
dialogStore.value = [];
|
||||
};
|
||||
|
||||
/** 千万别忘了在下面这三处引入并注册下,放心注册,不使用`addDialog`调用就不会被挂载
|
||||
|
@ -59,11 +52,4 @@ const closeAllDialog = () => {
|
|||
const ReDialog = withInstall(reDialog);
|
||||
|
||||
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
|
||||
export {
|
||||
ReDialog,
|
||||
dialogStore,
|
||||
addDialog,
|
||||
closeDialog,
|
||||
updateDialog,
|
||||
closeAllDialog
|
||||
};
|
||||
export { ReDialog, dialogStore, addDialog, closeDialog, updateDialog, closeAllDialog };
|
||||
|
|
|
@ -1,206 +1,154 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
type EventType,
|
||||
type ButtonProps,
|
||||
type DialogOptions,
|
||||
closeDialog,
|
||||
dialogStore
|
||||
} from "./index";
|
||||
import { ref, computed } from "vue";
|
||||
import { isFunction } from "@pureadmin/utils";
|
||||
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
||||
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
||||
<script lang="ts" setup>
|
||||
import { type ButtonProps, closeDialog, type DialogOptions, dialogStore, type EventType } from './index';
|
||||
import { computed, ref } from 'vue';
|
||||
import { isFunction } from '@pureadmin/utils';
|
||||
import Fullscreen from '@iconify-icons/ri/fullscreen-fill';
|
||||
import ExitFullscreen from '@iconify-icons/ri/fullscreen-exit-fill';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
defineOptions({
|
||||
name: "ReDialog"
|
||||
name: 'ReDialog',
|
||||
});
|
||||
|
||||
const sureBtnMap = ref({});
|
||||
const fullscreen = ref(false);
|
||||
|
||||
const footerButtons = computed(() => {
|
||||
return (options: DialogOptions) => {
|
||||
return options?.footerButtons?.length > 0
|
||||
? options.footerButtons
|
||||
: ([
|
||||
{
|
||||
label: "取消",
|
||||
text: true,
|
||||
bg: true,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
const done = () =>
|
||||
closeDialog(options, index, { command: "cancel" });
|
||||
if (options?.beforeCancel && isFunction(options?.beforeCancel)) {
|
||||
options.beforeCancel(done, { options, index });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "确定",
|
||||
type: "primary",
|
||||
text: true,
|
||||
bg: true,
|
||||
popconfirm: options?.popconfirm,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
if (options?.sureBtnLoading) {
|
||||
sureBtnMap.value[index] = Object.assign(
|
||||
{},
|
||||
sureBtnMap.value[index],
|
||||
{
|
||||
loading: true
|
||||
}
|
||||
);
|
||||
}
|
||||
const closeLoading = () => {
|
||||
if (options?.sureBtnLoading) {
|
||||
sureBtnMap.value[index].loading = false;
|
||||
}
|
||||
};
|
||||
const done = () => {
|
||||
closeLoading();
|
||||
closeDialog(options, index, { command: "sure" });
|
||||
};
|
||||
if (options?.beforeSure && isFunction(options?.beforeSure)) {
|
||||
options.beforeSure(done, { options, index, closeLoading });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
] as Array<ButtonProps>);
|
||||
};
|
||||
return (options: DialogOptions) => {
|
||||
return options?.footerButtons?.length > 0
|
||||
? options.footerButtons
|
||||
: ([
|
||||
{
|
||||
label: $t('cancel'),
|
||||
text: true,
|
||||
bg: true,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
const done = () => closeDialog(options, index, { command: 'cancel' });
|
||||
if (options?.beforeCancel && isFunction(options?.beforeCancel)) {
|
||||
options.beforeCancel(done, { options, index });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: $t('confirm'),
|
||||
type: 'primary',
|
||||
text: true,
|
||||
bg: true,
|
||||
popconfirm: options?.popconfirm,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
if (options?.sureBtnLoading) {
|
||||
sureBtnMap.value[index] = Object.assign({}, sureBtnMap.value[index], {
|
||||
loading: true,
|
||||
});
|
||||
}
|
||||
const closeLoading = () => {
|
||||
if (options?.sureBtnLoading) {
|
||||
sureBtnMap.value[index].loading = false;
|
||||
}
|
||||
};
|
||||
const done = () => {
|
||||
closeLoading();
|
||||
closeDialog(options, index, { command: 'sure' });
|
||||
};
|
||||
if (options?.beforeSure && isFunction(options?.beforeSure)) {
|
||||
options.beforeSure(done, { options, index, closeLoading });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
] as Array<ButtonProps>);
|
||||
};
|
||||
});
|
||||
|
||||
const fullscreenClass = computed(() => {
|
||||
return [
|
||||
"el-icon",
|
||||
"el-dialog__close",
|
||||
"-translate-x-2",
|
||||
"cursor-pointer",
|
||||
"hover:!text-[red]"
|
||||
];
|
||||
return ['el-icon', 'el-dialog__close', '-translate-x-2', 'cursor-pointer', 'hover:!text-[red]'];
|
||||
});
|
||||
|
||||
function eventsCallBack(
|
||||
event: EventType,
|
||||
options: DialogOptions,
|
||||
index: number,
|
||||
isClickFullScreen = false
|
||||
) {
|
||||
if (!isClickFullScreen) fullscreen.value = options?.fullscreen ?? false;
|
||||
if (options?.[event] && isFunction(options?.[event])) {
|
||||
return options?.[event]({ options, index });
|
||||
}
|
||||
function eventsCallBack(event: EventType, options: DialogOptions, index: number, isClickFullScreen = false) {
|
||||
if (!isClickFullScreen) fullscreen.value = options?.fullscreen ?? false;
|
||||
if (options?.[event] && isFunction(options?.[event])) {
|
||||
return options?.[event]({ options, index });
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose(
|
||||
options: DialogOptions,
|
||||
index: number,
|
||||
args = { command: "close" }
|
||||
) {
|
||||
closeDialog(options, index, args);
|
||||
eventsCallBack("close", options, index);
|
||||
function handleClose(options: DialogOptions, index: number, args = { command: 'close' }) {
|
||||
closeDialog(options, index, args);
|
||||
eventsCallBack('close', options, index);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-for="(options, index) in dialogStore"
|
||||
:key="index"
|
||||
v-bind="options"
|
||||
v-model="options.visible"
|
||||
class="pure-dialog"
|
||||
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
|
||||
@closed="handleClose(options, index)"
|
||||
@opened="eventsCallBack('open', options, index)"
|
||||
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
|
||||
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
|
||||
>
|
||||
<!-- header -->
|
||||
<template
|
||||
v-if="options?.fullscreenIcon || options?.headerRenderer"
|
||||
#header="{ close, titleId, titleClass }"
|
||||
>
|
||||
<div
|
||||
v-if="options?.fullscreenIcon"
|
||||
class="flex items-center justify-between"
|
||||
>
|
||||
<span :id="titleId" :class="titleClass">{{ options?.title }}</span>
|
||||
<i
|
||||
v-if="!options?.fullscreen"
|
||||
:class="fullscreenClass"
|
||||
@click="
|
||||
() => {
|
||||
fullscreen = !fullscreen;
|
||||
eventsCallBack(
|
||||
'fullscreenCallBack',
|
||||
{ ...options, fullscreen },
|
||||
index,
|
||||
true
|
||||
);
|
||||
}
|
||||
"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
class="pure-dialog-svg"
|
||||
:icon="
|
||||
options?.fullscreen
|
||||
? ExitFullscreen
|
||||
: fullscreen
|
||||
? ExitFullscreen
|
||||
: Fullscreen
|
||||
"
|
||||
/>
|
||||
</i>
|
||||
</div>
|
||||
<component
|
||||
:is="options?.headerRenderer({ close, titleId, titleClass })"
|
||||
v-else
|
||||
/>
|
||||
</template>
|
||||
<component
|
||||
v-bind="options?.props"
|
||||
:is="options.contentRenderer({ options, index })"
|
||||
@close="args => handleClose(options, index, args)"
|
||||
/>
|
||||
<!-- footer -->
|
||||
<template v-if="!options?.hideFooter" #footer>
|
||||
<template v-if="options?.footerRenderer">
|
||||
<component :is="options?.footerRenderer({ options, index })" />
|
||||
</template>
|
||||
<span v-else>
|
||||
<template v-for="(btn, key) in footerButtons(options)" :key="key">
|
||||
<el-popconfirm
|
||||
v-if="btn.popconfirm"
|
||||
v-bind="btn.popconfirm"
|
||||
@confirm="
|
||||
btn.btnClick({
|
||||
dialog: { options, index },
|
||||
button: { btn, index: key }
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button v-bind="btn">{{ btn?.label }}</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button
|
||||
v-else
|
||||
v-bind="btn"
|
||||
:loading="key === 1 && sureBtnMap[index]?.loading"
|
||||
@click="
|
||||
btn.btnClick({
|
||||
dialog: { options, index },
|
||||
button: { btn, index: key }
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ btn?.label }}
|
||||
</el-button>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-for="(options, index) in dialogStore"
|
||||
:key="index"
|
||||
v-model="options.visible"
|
||||
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
|
||||
class="pure-dialog"
|
||||
v-bind="options"
|
||||
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
|
||||
@closed="handleClose(options, index)"
|
||||
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
|
||||
@opened="eventsCallBack('open', options, index)"
|
||||
>
|
||||
<!-- header -->
|
||||
<template v-if="options?.fullscreenIcon || options?.headerRenderer" #header="{ close, titleId, titleClass }">
|
||||
<div v-if="options?.fullscreenIcon" class="flex items-center justify-between">
|
||||
<span :id="titleId" :class="titleClass">{{ options?.title }}</span>
|
||||
<i
|
||||
v-if="!options?.fullscreen"
|
||||
:class="fullscreenClass"
|
||||
@click="
|
||||
() => {
|
||||
fullscreen = !fullscreen;
|
||||
eventsCallBack('fullscreenCallBack', { ...options, fullscreen }, index, true);
|
||||
}
|
||||
"
|
||||
>
|
||||
<IconifyIconOffline :icon="options?.fullscreen ? ExitFullscreen : fullscreen ? ExitFullscreen : Fullscreen" class="pure-dialog-svg" />
|
||||
</i>
|
||||
</div>
|
||||
<component :is="options?.headerRenderer({ close, titleId, titleClass })" v-else />
|
||||
</template>
|
||||
<component :is="options.contentRenderer({ options, index })" v-bind="options?.props" @close="args => handleClose(options, index, args)" />
|
||||
<!-- footer -->
|
||||
<template v-if="!options?.hideFooter" #footer>
|
||||
<template v-if="options?.footerRenderer">
|
||||
<component :is="options?.footerRenderer({ options, index })" />
|
||||
</template>
|
||||
<span v-else>
|
||||
<template v-for="(btn, key) in footerButtons(options)" :key="key">
|
||||
<el-popconfirm
|
||||
v-if="btn.popconfirm"
|
||||
v-bind="btn.popconfirm"
|
||||
@confirm="
|
||||
btn.btnClick({
|
||||
dialog: { options, index },
|
||||
button: { btn, index: key },
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button v-bind="btn">{{ btn?.label }}</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button
|
||||
v-else
|
||||
:loading="key === 1 && sureBtnMap[index]?.loading"
|
||||
v-bind="btn"
|
||||
@click="
|
||||
btn.btnClick({
|
||||
dialog: { options, index },
|
||||
button: { btn, index: key },
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ btn?.label }}
|
||||
</el-button>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
|
|
@ -1,275 +1,206 @@
|
|||
import type { CSSProperties, VNode, Component } from "vue";
|
||||
import type { Component, CSSProperties, VNode } from 'vue';
|
||||
|
||||
type DoneFn = (cancel?: boolean) => void;
|
||||
type EventType =
|
||||
| "open"
|
||||
| "close"
|
||||
| "openAutoFocus"
|
||||
| "closeAutoFocus"
|
||||
| "fullscreenCallBack";
|
||||
type EventType = 'open' | 'close' | 'openAutoFocus' | 'closeAutoFocus' | 'fullscreenCallBack';
|
||||
type ArgsType = {
|
||||
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
command: "cancel" | "sure" | "close";
|
||||
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
command: 'cancel' | 'sure' | 'close';
|
||||
};
|
||||
type ButtonType =
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "info"
|
||||
| "text";
|
||||
type ButtonType = 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'text';
|
||||
|
||||
/** https://element-plus.org/zh-CN/component/dialog.html#attributes */
|
||||
type DialogProps = {
|
||||
/** `Dialog` 的显示与隐藏 */
|
||||
visible?: boolean;
|
||||
/** `Dialog` 的标题 */
|
||||
title?: string;
|
||||
/** `Dialog` 的宽度,默认 `50%` */
|
||||
width?: string | number;
|
||||
/** 是否为全屏 `Dialog`(会一直处于全屏状态,除非弹框关闭),默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||
fullscreen?: boolean;
|
||||
/** 是否显示全屏操作图标,默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||
fullscreenIcon?: boolean;
|
||||
/** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */
|
||||
top?: string;
|
||||
/** 是否需要遮罩层,默认 `true` */
|
||||
modal?: boolean;
|
||||
/** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */
|
||||
appendToBody?: boolean;
|
||||
/** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */
|
||||
lockScroll?: boolean;
|
||||
/** `Dialog` 的自定义类名 */
|
||||
class?: string;
|
||||
/** `Dialog` 的自定义样式 */
|
||||
style?: CSSProperties;
|
||||
/** `Dialog` 打开的延时时间,单位毫秒,默认 `0` */
|
||||
openDelay?: number;
|
||||
/** `Dialog` 关闭的延时时间,单位毫秒,默认 `0` */
|
||||
closeDelay?: number;
|
||||
/** 是否可以通过点击 `modal` 关闭 `Dialog`,默认 `true` */
|
||||
closeOnClickModal?: boolean;
|
||||
/** 是否可以通过按下 `ESC` 关闭 `Dialog`,默认 `true` */
|
||||
closeOnPressEscape?: boolean;
|
||||
/** 是否显示关闭按钮,默认 `true` */
|
||||
showClose?: boolean;
|
||||
/** 关闭前的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeClose?: (done: DoneFn) => void;
|
||||
/** 为 `Dialog` 启用可拖拽功能,默认 `false` */
|
||||
draggable?: boolean;
|
||||
/** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */
|
||||
center?: boolean;
|
||||
/** 是否水平垂直对齐对话框,默认 `false` */
|
||||
alignCenter?: boolean;
|
||||
/** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */
|
||||
destroyOnClose?: boolean;
|
||||
/** `Dialog` 的显示与隐藏 */
|
||||
visible?: boolean;
|
||||
/** `Dialog` 的标题 */
|
||||
title?: string;
|
||||
/** `Dialog` 的宽度,默认 `50%` */
|
||||
width?: string | number;
|
||||
/** 是否为全屏 `Dialog`(会一直处于全屏状态,除非弹框关闭),默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||
fullscreen?: boolean;
|
||||
/** 是否显示全屏操作图标,默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||
fullscreenIcon?: boolean;
|
||||
/** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */
|
||||
top?: string;
|
||||
/** 是否需要遮罩层,默认 `true` */
|
||||
modal?: boolean;
|
||||
/** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */
|
||||
appendToBody?: boolean;
|
||||
/** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */
|
||||
lockScroll?: boolean;
|
||||
/** `Dialog` 的自定义类名 */
|
||||
class?: string;
|
||||
/** `Dialog` 的自定义样式 */
|
||||
style?: CSSProperties;
|
||||
/** `Dialog` 打开的延时时间,单位毫秒,默认 `0` */
|
||||
openDelay?: number;
|
||||
/** `Dialog` 关闭的延时时间,单位毫秒,默认 `0` */
|
||||
closeDelay?: number;
|
||||
/** 是否可以通过点击 `modal` 关闭 `Dialog`,默认 `true` */
|
||||
closeOnClickModal?: boolean;
|
||||
/** 是否可以通过按下 `ESC` 关闭 `Dialog`,默认 `true` */
|
||||
closeOnPressEscape?: boolean;
|
||||
/** 是否显示关闭按钮,默认 `true` */
|
||||
showClose?: boolean;
|
||||
/** 关闭前的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeClose?: (done: DoneFn) => void;
|
||||
/** 为 `Dialog` 启用可拖拽功能,默认 `false` */
|
||||
draggable?: boolean;
|
||||
/** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */
|
||||
center?: boolean;
|
||||
/** 是否水平垂直对齐对话框,默认 `false` */
|
||||
alignCenter?: boolean;
|
||||
/** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */
|
||||
destroyOnClose?: boolean;
|
||||
};
|
||||
|
||||
//element-plus.org/zh-CN/component/popconfirm.html#attributes
|
||||
type Popconfirm = {
|
||||
/** 标题 */
|
||||
title?: string;
|
||||
/** 确定按钮文字 */
|
||||
confirmButtonText?: string;
|
||||
/** 取消按钮文字 */
|
||||
cancelButtonText?: string;
|
||||
/** 确定按钮类型,默认 `primary` */
|
||||
confirmButtonType?: ButtonType;
|
||||
/** 取消按钮类型,默认 `text` */
|
||||
cancelButtonType?: ButtonType;
|
||||
/** 自定义图标,默认 `QuestionFilled` */
|
||||
icon?: string | Component;
|
||||
/** `Icon` 颜色,默认 `#f90` */
|
||||
iconColor?: string;
|
||||
/** 是否隐藏 `Icon`,默认 `false` */
|
||||
hideIcon?: boolean;
|
||||
/** 关闭时的延迟,默认 `200` */
|
||||
hideAfter?: number;
|
||||
/** 是否将 `popover` 的下拉列表插入至 `body` 元素,默认 `true` */
|
||||
teleported?: boolean;
|
||||
/** 当 `popover` 组件长时间不触发且 `persistent` 属性设置为 `false` 时, `popover` 将会被删除,默认 `false` */
|
||||
persistent?: boolean;
|
||||
/** 弹层宽度,最小宽度 `150px`,默认 `150` */
|
||||
width?: string | number;
|
||||
/** 标题 */
|
||||
title?: string;
|
||||
/** 确定按钮文字 */
|
||||
confirmButtonText?: string;
|
||||
/** 取消按钮文字 */
|
||||
cancelButtonText?: string;
|
||||
/** 确定按钮类型,默认 `primary` */
|
||||
confirmButtonType?: ButtonType;
|
||||
/** 取消按钮类型,默认 `text` */
|
||||
cancelButtonType?: ButtonType;
|
||||
/** 自定义图标,默认 `QuestionFilled` */
|
||||
icon?: string | Component;
|
||||
/** `Icon` 颜色,默认 `#f90` */
|
||||
iconColor?: string;
|
||||
/** 是否隐藏 `Icon`,默认 `false` */
|
||||
hideIcon?: boolean;
|
||||
/** 关闭时的延迟,默认 `200` */
|
||||
hideAfter?: number;
|
||||
/** 是否将 `popover` 的下拉列表插入至 `body` 元素,默认 `true` */
|
||||
teleported?: boolean;
|
||||
/** 当 `popover` 组件长时间不触发且 `persistent` 属性设置为 `false` 时, `popover` 将会被删除,默认 `false` */
|
||||
persistent?: boolean;
|
||||
/** 弹层宽度,最小宽度 `150px`,默认 `150` */
|
||||
width?: string | number;
|
||||
};
|
||||
|
||||
type BtnClickDialog = {
|
||||
options?: DialogOptions;
|
||||
index?: number;
|
||||
options?: DialogOptions;
|
||||
index?: number;
|
||||
};
|
||||
type BtnClickButton = {
|
||||
btn?: ButtonProps;
|
||||
index?: number;
|
||||
btn?: ButtonProps;
|
||||
index?: number;
|
||||
};
|
||||
/** https://element-plus.org/zh-CN/component/button.html#button-attributes */
|
||||
type ButtonProps = {
|
||||
/** 按钮文字 */
|
||||
label: string;
|
||||
/** 按钮尺寸 */
|
||||
size?: "large" | "default" | "small";
|
||||
/** 按钮类型 */
|
||||
type?: "primary" | "success" | "warning" | "danger" | "info";
|
||||
/** 是否为朴素按钮,默认 `false` */
|
||||
plain?: boolean;
|
||||
/** 是否为文字按钮,默认 `false` */
|
||||
text?: boolean;
|
||||
/** 是否显示文字按钮背景颜色,默认 `false` */
|
||||
bg?: boolean;
|
||||
/** 是否为链接按钮,默认 `false` */
|
||||
link?: boolean;
|
||||
/** 是否为圆角按钮,默认 `false` */
|
||||
round?: boolean;
|
||||
/** 是否为圆形按钮,默认 `false` */
|
||||
circle?: boolean;
|
||||
/** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */
|
||||
popconfirm?: Popconfirm;
|
||||
/** 是否为加载中状态,默认 `false` */
|
||||
loading?: boolean;
|
||||
/** 自定义加载中状态图标组件 */
|
||||
loadingIcon?: string | Component;
|
||||
/** 按钮是否为禁用状态,默认 `false` */
|
||||
disabled?: boolean;
|
||||
/** 图标组件 */
|
||||
icon?: string | Component;
|
||||
/** 是否开启原生 `autofocus` 属性,默认 `false` */
|
||||
autofocus?: boolean;
|
||||
/** 原生 `type` 属性,默认 `button` */
|
||||
nativeType?: "button" | "submit" | "reset";
|
||||
/** 自动在两个中文字符之间插入空格 */
|
||||
autoInsertSpace?: boolean;
|
||||
/** 自定义按钮颜色, 并自动计算 `hover` 和 `active` 触发后的颜色 */
|
||||
color?: string;
|
||||
/** `dark` 模式, 意味着自动设置 `color` 为 `dark` 模式的颜色,默认 `false` */
|
||||
dark?: boolean;
|
||||
/** 自定义元素标签 */
|
||||
tag?: string | Component;
|
||||
/** 点击按钮后触发的回调 */
|
||||
btnClick?: ({
|
||||
dialog,
|
||||
button
|
||||
}: {
|
||||
/** 当前 `Dialog` 信息 */
|
||||
dialog: BtnClickDialog;
|
||||
/** 当前 `button` 信息 */
|
||||
button: BtnClickButton;
|
||||
}) => void;
|
||||
/** 按钮文字 */
|
||||
label: string;
|
||||
/** 按钮尺寸 */
|
||||
size?: 'large' | 'default' | 'small';
|
||||
/** 按钮类型 */
|
||||
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
/** 是否为朴素按钮,默认 `false` */
|
||||
plain?: boolean;
|
||||
/** 是否为文字按钮,默认 `false` */
|
||||
text?: boolean;
|
||||
/** 是否显示文字按钮背景颜色,默认 `false` */
|
||||
bg?: boolean;
|
||||
/** 是否为链接按钮,默认 `false` */
|
||||
link?: boolean;
|
||||
/** 是否为圆角按钮,默认 `false` */
|
||||
round?: boolean;
|
||||
/** 是否为圆形按钮,默认 `false` */
|
||||
circle?: boolean;
|
||||
/** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */
|
||||
popconfirm?: Popconfirm;
|
||||
/** 是否为加载中状态,默认 `false` */
|
||||
loading?: boolean;
|
||||
/** 自定义加载中状态图标组件 */
|
||||
loadingIcon?: string | Component;
|
||||
/** 按钮是否为禁用状态,默认 `false` */
|
||||
disabled?: boolean;
|
||||
/** 图标组件 */
|
||||
icon?: string | Component;
|
||||
/** 是否开启原生 `autofocus` 属性,默认 `false` */
|
||||
autofocus?: boolean;
|
||||
/** 原生 `type` 属性,默认 `button` */
|
||||
nativeType?: 'button' | 'submit' | 'reset';
|
||||
/** 自动在两个中文字符之间插入空格 */
|
||||
autoInsertSpace?: boolean;
|
||||
/** 自定义按钮颜色, 并自动计算 `hover` 和 `active` 触发后的颜色 */
|
||||
color?: string;
|
||||
/** `dark` 模式, 意味着自动设置 `color` 为 `dark` 模式的颜色,默认 `false` */
|
||||
dark?: boolean;
|
||||
/** 自定义元素标签 */
|
||||
tag?: string | Component;
|
||||
/** 点击按钮后触发的回调 */
|
||||
btnClick?: ({
|
||||
dialog,
|
||||
button,
|
||||
}: {
|
||||
/** 当前 `Dialog` 信息 */
|
||||
dialog: BtnClickDialog;
|
||||
/** 当前 `button` 信息 */
|
||||
button: BtnClickButton;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
interface DialogOptions extends DialogProps {
|
||||
/** 内容区组件的 `props`,可通过 `defineProps` 接收 */
|
||||
props?: any;
|
||||
/** 是否隐藏 `Dialog` 按钮操作区的内容 */
|
||||
hideFooter?: boolean;
|
||||
/** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */
|
||||
popconfirm?: Popconfirm;
|
||||
/** 点击确定按钮后是否开启 `loading` 加载动画 */
|
||||
sureBtnLoading?: boolean;
|
||||
/**
|
||||
* @description 自定义对话框标题的内容渲染器
|
||||
* @see {@link https://element-plus.org/zh-CN/component/dialog.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%B4%E9%83%A8}
|
||||
*/
|
||||
headerRenderer?: ({
|
||||
close,
|
||||
titleId,
|
||||
titleClass
|
||||
}: {
|
||||
close: Function;
|
||||
titleId: string;
|
||||
titleClass: string;
|
||||
}) => VNode | Component;
|
||||
/** 自定义内容渲染器 */
|
||||
contentRenderer?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => VNode | Component;
|
||||
/** 自定义按钮操作区的内容渲染器,会覆盖`footerButtons`以及默认的 `取消` 和 `确定` 按钮 */
|
||||
footerRenderer?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => VNode | Component;
|
||||
/** 自定义底部按钮操作 */
|
||||
footerButtons?: Array<ButtonProps>;
|
||||
/** `Dialog` 打开后的回调 */
|
||||
open?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */
|
||||
close?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
closeCallBack?: ({
|
||||
options,
|
||||
index,
|
||||
args
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
args: any;
|
||||
}) => void;
|
||||
/** 点击全屏按钮时的回调 */
|
||||
fullscreenCallBack?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** 输入焦点聚焦在 `Dialog` 内容时的回调 */
|
||||
openAutoFocus?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** 输入焦点从 `Dialog` 内容失焦时的回调 */
|
||||
closeAutoFocus?: ({
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** 点击底部取消按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeCancel?: (
|
||||
done: Function,
|
||||
{
|
||||
options,
|
||||
index
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}
|
||||
) => void;
|
||||
/** 点击底部确定按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeSure?: (
|
||||
done: Function,
|
||||
{
|
||||
options,
|
||||
index,
|
||||
closeLoading
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
/** 关闭确定按钮的 `loading` 加载动画 */
|
||||
closeLoading: Function;
|
||||
}
|
||||
) => void;
|
||||
/** 内容区组件的 `props`,可通过 `defineProps` 接收 */
|
||||
props?: any;
|
||||
/** 是否隐藏 `Dialog` 按钮操作区的内容 */
|
||||
hideFooter?: boolean;
|
||||
/** 确定按钮的 `Popconfirm` 气泡确认框相关配置 */
|
||||
popconfirm?: Popconfirm;
|
||||
/** 点击确定按钮后是否开启 `loading` 加载动画 */
|
||||
sureBtnLoading?: boolean;
|
||||
/**
|
||||
* @description 自定义对话框标题的内容渲染器
|
||||
* @see {@link https://element-plus.org/zh-CN/component/dialog.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%B4%E9%83%A8}
|
||||
*/
|
||||
headerRenderer?: ({ close, titleId, titleClass }: { close: Function; titleId: string; titleClass: string }) => VNode | Component;
|
||||
/** 自定义内容渲染器 */
|
||||
contentRenderer?: ({ options, index }: { options: DialogOptions; index: number }) => VNode | Component;
|
||||
/** 自定义按钮操作区的内容渲染器,会覆盖`footerButtons`以及默认的 `取消` 和 `确定` 按钮 */
|
||||
footerRenderer?: ({ options, index }: { options: DialogOptions; index: number }) => VNode | Component;
|
||||
/** 自定义底部按钮操作 */
|
||||
footerButtons?: Array<ButtonProps>;
|
||||
/** `Dialog` 打开后的回调 */
|
||||
open?: ({ options, index }: { options: DialogOptions; index: number }) => void;
|
||||
/** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */
|
||||
close?: ({ options, index }: { options: DialogOptions; index: number }) => void;
|
||||
/** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
closeCallBack?: ({ options, index, args }: { options: DialogOptions; index: number; args: any }) => void;
|
||||
/** 点击全屏按钮时的回调 */
|
||||
fullscreenCallBack?: ({ options, index }: { options: DialogOptions; index: number }) => void;
|
||||
/** 输入焦点聚焦在 `Dialog` 内容时的回调 */
|
||||
openAutoFocus?: ({ options, index }: { options: DialogOptions; index: number }) => void;
|
||||
/** 输入焦点从 `Dialog` 内容失焦时的回调 */
|
||||
closeAutoFocus?: ({ options, index }: { options: DialogOptions; index: number }) => void;
|
||||
/** 点击底部取消按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeCancel?: (
|
||||
done: Function,
|
||||
{
|
||||
options,
|
||||
index,
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
},
|
||||
) => void;
|
||||
/** 点击底部确定按钮的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||
beforeSure?: (
|
||||
done: Function,
|
||||
{
|
||||
options,
|
||||
index,
|
||||
closeLoading,
|
||||
}: {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
/** 关闭确定按钮的 `loading` 加载动画 */
|
||||
closeLoading: Function;
|
||||
},
|
||||
) => void;
|
||||
}
|
||||
|
||||
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import iconifyIconOffline from "./src/iconifyIconOffline";
|
||||
import iconifyIconOnline from "./src/iconifyIconOnline";
|
||||
import fontIcon from "./src/iconfont";
|
||||
import iconifyIconOffline from './src/iconifyIconOffline';
|
||||
import iconifyIconOnline from './src/iconifyIconOnline';
|
||||
import fontIcon from './src/iconfont';
|
||||
|
||||
/** 本地图标组件 */
|
||||
const IconifyIconOffline = iconifyIconOffline;
|
||||
|
|
|
@ -1,48 +1,45 @@
|
|||
import { h, defineComponent } from "vue";
|
||||
import { defineComponent, h } from 'vue';
|
||||
|
||||
// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
|
||||
export default defineComponent({
|
||||
name: "FontIcon",
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const attrs = this.$attrs;
|
||||
if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") {
|
||||
return h(
|
||||
"i",
|
||||
{
|
||||
class: "iconfont",
|
||||
...attrs
|
||||
},
|
||||
this.icon
|
||||
);
|
||||
} else if (
|
||||
Object.keys(attrs).includes("svg") ||
|
||||
attrs?.iconType === "svg"
|
||||
) {
|
||||
return h(
|
||||
"svg",
|
||||
{
|
||||
class: "icon-svg",
|
||||
"aria-hidden": true
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h("use", {
|
||||
"xlink:href": `#${this.icon}`
|
||||
})
|
||||
]
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return h("i", {
|
||||
class: `iconfont ${this.icon}`,
|
||||
...attrs
|
||||
});
|
||||
}
|
||||
}
|
||||
name: 'FontIcon',
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const attrs = this.$attrs;
|
||||
if (Object.keys(attrs).includes('uni') || attrs?.iconType === 'uni') {
|
||||
return h(
|
||||
'i',
|
||||
{
|
||||
class: 'iconfont',
|
||||
...attrs,
|
||||
},
|
||||
this.icon,
|
||||
);
|
||||
} else if (Object.keys(attrs).includes('svg') || attrs?.iconType === 'svg') {
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
class: 'icon-svg',
|
||||
'aria-hidden': true,
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h('use', {
|
||||
'xlink:href': `#${this.icon}`,
|
||||
}),
|
||||
],
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return h('i', {
|
||||
class: `iconfont ${this.icon}`,
|
||||
...attrs,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
import { h, defineComponent } from "vue";
|
||||
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
|
||||
import { defineComponent, h } from 'vue';
|
||||
import { addIcon, Icon as IconifyIcon } from '@iconify/vue/dist/offline';
|
||||
|
||||
// Iconify Icon在Vue里本地使用(用于内网环境)
|
||||
export default defineComponent({
|
||||
name: "IconifyIconOffline",
|
||||
components: { IconifyIcon },
|
||||
props: {
|
||||
icon: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
render() {
|
||||
if (typeof this.icon === "object") addIcon(this.icon, this.icon);
|
||||
const attrs = this.$attrs;
|
||||
return h(
|
||||
IconifyIcon,
|
||||
{
|
||||
icon: this.icon,
|
||||
style: attrs?.style
|
||||
? Object.assign(attrs.style, { outline: "none" })
|
||||
: { outline: "none" },
|
||||
...attrs
|
||||
},
|
||||
{
|
||||
default: () => []
|
||||
}
|
||||
);
|
||||
}
|
||||
name: 'IconifyIconOffline',
|
||||
components: { IconifyIcon },
|
||||
props: {
|
||||
icon: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (typeof this.icon === 'object') addIcon(this.icon, this.icon);
|
||||
const attrs = this.$attrs;
|
||||
return h(
|
||||
IconifyIcon,
|
||||
{
|
||||
icon: this.icon,
|
||||
style: attrs?.style ? Object.assign(attrs.style, { outline: 'none' }) : { outline: 'none' },
|
||||
...attrs,
|
||||
},
|
||||
{
|
||||
default: () => [],
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
import { h, defineComponent } from "vue";
|
||||
import { Icon as IconifyIcon } from "@iconify/vue";
|
||||
import { defineComponent, h } from 'vue';
|
||||
import { Icon as IconifyIcon } from '@iconify/vue';
|
||||
|
||||
// Iconify Icon在Vue里在线使用(用于外网环境)
|
||||
export default defineComponent({
|
||||
name: "IconifyIconOnline",
|
||||
components: { IconifyIcon },
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const attrs = this.$attrs;
|
||||
return h(
|
||||
IconifyIcon,
|
||||
{
|
||||
icon: `${this.icon}`,
|
||||
style: attrs?.style
|
||||
? Object.assign(attrs.style, { outline: "none" })
|
||||
: { outline: "none" },
|
||||
...attrs
|
||||
},
|
||||
{
|
||||
default: () => []
|
||||
}
|
||||
);
|
||||
}
|
||||
name: 'IconifyIconOnline',
|
||||
components: { IconifyIcon },
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const attrs = this.$attrs;
|
||||
return h(
|
||||
IconifyIcon,
|
||||
{
|
||||
icon: `${this.icon}`,
|
||||
style: attrs?.style ? Object.assign(attrs.style, { outline: 'none' }) : { outline: 'none' },
|
||||
...attrs,
|
||||
},
|
||||
{
|
||||
default: () => [],
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
// 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
|
||||
import { addIcon } from "@iconify/vue/dist/offline";
|
||||
import { addIcon } from '@iconify/vue/dist/offline';
|
||||
|
||||
// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标
|
||||
// @iconify-icons/ep
|
||||
import Lollipop from "@iconify-icons/ep/lollipop";
|
||||
import HomeFilled from "@iconify-icons/ep/home-filled";
|
||||
addIcon("ep:lollipop", Lollipop);
|
||||
addIcon("ep:home-filled", HomeFilled);
|
||||
import Lollipop from '@iconify-icons/ep/lollipop';
|
||||
import HomeFilled from '@iconify-icons/ep/home-filled';
|
||||
// @iconify-icons/ri
|
||||
import Search from "@iconify-icons/ri/search-line";
|
||||
import InformationLine from "@iconify-icons/ri/information-line";
|
||||
addIcon("ri:search-line", Search);
|
||||
addIcon("ri:information-line", InformationLine);
|
||||
import Search from '@iconify-icons/ri/search-line';
|
||||
import InformationLine from '@iconify-icons/ri/information-line';
|
||||
|
||||
addIcon('ep:lollipop', Lollipop);
|
||||
addIcon('ep:home-filled', HomeFilled);
|
||||
|
||||
addIcon('ri:search-line', Search);
|
||||
addIcon('ri:information-line', InformationLine);
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
export interface iconType {
|
||||
// iconify (https://docs.iconify.design/icon-components/vue/#properties)
|
||||
inline?: boolean;
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
horizontalFlip?: boolean;
|
||||
verticalFlip?: boolean;
|
||||
flip?: string;
|
||||
rotate?: number | string;
|
||||
color?: string;
|
||||
horizontalAlign?: boolean;
|
||||
verticalAlign?: boolean;
|
||||
align?: string;
|
||||
onLoad?: Function;
|
||||
includes?: Function;
|
||||
// svg 需要什么SVG属性自行添加
|
||||
fill?: string;
|
||||
// all icon
|
||||
style?: object;
|
||||
// iconify (https://docs.iconify.design/icon-components/vue/#properties)
|
||||
inline?: boolean;
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
horizontalFlip?: boolean;
|
||||
verticalFlip?: boolean;
|
||||
flip?: string;
|
||||
rotate?: number | string;
|
||||
color?: string;
|
||||
horizontalAlign?: boolean;
|
||||
verticalAlign?: boolean;
|
||||
align?: string;
|
||||
onLoad?: Function;
|
||||
includes?: Function;
|
||||
// svg 需要什么SVG属性自行添加
|
||||
fill?: string;
|
||||
// all icon
|
||||
style?: object;
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
normal 普通数字动画组件
|
||||
rebound 回弹式数字动画组件
|
|
@ -1,216 +1,175 @@
|
|||
import { useRenderIcon } from "@/components/CommonIcon/src/hooks";
|
||||
import {
|
||||
isFunction,
|
||||
isNumber,
|
||||
useDark,
|
||||
useResizeObserver
|
||||
} from "@pureadmin/utils";
|
||||
import {
|
||||
type PropType,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
h,
|
||||
nextTick,
|
||||
ref,
|
||||
toRef,
|
||||
watch
|
||||
} from "vue";
|
||||
import "./index.css";
|
||||
import type { OptionsType } from "./type";
|
||||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||
import { isFunction, isNumber, useDark, useResizeObserver } from '@pureadmin/utils';
|
||||
import { defineComponent, getCurrentInstance, h, nextTick, type PropType, ref, toRef, watch } from 'vue';
|
||||
import './index.css';
|
||||
import type { OptionsType } from './type';
|
||||
|
||||
const props = {
|
||||
options: {
|
||||
type: Array<OptionsType>,
|
||||
default: () => []
|
||||
},
|
||||
/** 默认选中,按照第一个索引为 `0` 的模式,可选(`modelValue`只有传`number`类型时才为响应式) */
|
||||
modelValue: {
|
||||
type: undefined,
|
||||
require: false,
|
||||
default: "0"
|
||||
},
|
||||
/** 将宽度调整为父元素宽度 */
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 控件尺寸 */
|
||||
size: {
|
||||
type: String as PropType<"small" | "default" | "large">
|
||||
},
|
||||
/** 是否全局禁用,默认 `false` */
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 当内容发生变化时,设置 `resize` 可使其自适应容器位置 */
|
||||
resize: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
options: {
|
||||
type: Array<OptionsType>,
|
||||
default: () => [],
|
||||
},
|
||||
/** 默认选中,按照第一个索引为 `0` 的模式,可选(`modelValue`只有传`number`类型时才为响应式) */
|
||||
modelValue: {
|
||||
type: undefined,
|
||||
require: false,
|
||||
default: '0',
|
||||
},
|
||||
/** 将宽度调整为父元素宽度 */
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** 控件尺寸 */
|
||||
size: {
|
||||
type: String as PropType<'small' | 'default' | 'large'>,
|
||||
},
|
||||
/** 是否全局禁用,默认 `false` */
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** 当内容发生变化时,设置 `resize` 可使其自适应容器位置 */
|
||||
resize: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "ReSegmented",
|
||||
props,
|
||||
emits: ["change", "update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const width = ref(0);
|
||||
const translateX = ref(0);
|
||||
const { isDark } = useDark();
|
||||
const initStatus = ref(false);
|
||||
const curMouseActive = ref(-1);
|
||||
const segmentedItembg = ref("");
|
||||
const instance = getCurrentInstance()!;
|
||||
const curIndex = isNumber(props.modelValue)
|
||||
? toRef(props, "modelValue")
|
||||
: ref(0);
|
||||
name: 'ReSegmented',
|
||||
props,
|
||||
emits: ['change', 'update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
const width = ref(0);
|
||||
const translateX = ref(0);
|
||||
const { isDark } = useDark();
|
||||
const initStatus = ref(false);
|
||||
const curMouseActive = ref(-1);
|
||||
const segmentedItembg = ref('');
|
||||
const instance = getCurrentInstance()!;
|
||||
const curIndex = isNumber(props.modelValue) ? toRef(props, 'modelValue') : ref(0);
|
||||
|
||||
function handleChange({ option, index }, event: Event) {
|
||||
if (props.disabled || option.disabled) return;
|
||||
event.preventDefault();
|
||||
isNumber(props.modelValue)
|
||||
? emit("update:modelValue", index)
|
||||
: (curIndex.value = index);
|
||||
segmentedItembg.value = "";
|
||||
emit("change", { index, option });
|
||||
}
|
||||
function handleChange({ option, index }, event: Event) {
|
||||
if (props.disabled || option.disabled) return;
|
||||
event.preventDefault();
|
||||
isNumber(props.modelValue) ? emit('update:modelValue', index) : (curIndex.value = index);
|
||||
segmentedItembg.value = '';
|
||||
emit('change', { index, option });
|
||||
}
|
||||
|
||||
function handleMouseenter({ option, index }, event: Event) {
|
||||
if (props.disabled) return;
|
||||
event.preventDefault();
|
||||
curMouseActive.value = index;
|
||||
if (option.disabled || curIndex.value === index) {
|
||||
segmentedItembg.value = "";
|
||||
} else {
|
||||
segmentedItembg.value = isDark.value
|
||||
? "#1f1f1f"
|
||||
: "rgba(0, 0, 0, 0.06)";
|
||||
}
|
||||
}
|
||||
function handleMouseenter({ option, index }, event: Event) {
|
||||
if (props.disabled) return;
|
||||
event.preventDefault();
|
||||
curMouseActive.value = index;
|
||||
if (option.disabled || curIndex.value === index) {
|
||||
segmentedItembg.value = '';
|
||||
} else {
|
||||
segmentedItembg.value = isDark.value ? '#1f1f1f' : 'rgba(0, 0, 0, 0.06)';
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseleave(_, event: Event) {
|
||||
if (props.disabled) return;
|
||||
event.preventDefault();
|
||||
curMouseActive.value = -1;
|
||||
}
|
||||
function handleMouseleave(_, event: Event) {
|
||||
if (props.disabled) return;
|
||||
event.preventDefault();
|
||||
curMouseActive.value = -1;
|
||||
}
|
||||
|
||||
function handleInit(index = curIndex.value) {
|
||||
nextTick(() => {
|
||||
const curLabelRef = instance?.proxy?.$refs[`labelRef${index}`] as ElRef;
|
||||
if (!curLabelRef) return;
|
||||
width.value = curLabelRef.clientWidth;
|
||||
translateX.value = curLabelRef.offsetLeft;
|
||||
initStatus.value = true;
|
||||
});
|
||||
}
|
||||
function handleInit(index = curIndex.value) {
|
||||
nextTick(() => {
|
||||
const curLabelRef = instance?.proxy?.$refs[`labelRef${index}`] as ElRef;
|
||||
if (!curLabelRef) return;
|
||||
width.value = curLabelRef.clientWidth;
|
||||
translateX.value = curLabelRef.offsetLeft;
|
||||
initStatus.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
function handleResizeInit() {
|
||||
useResizeObserver(".pure-segmented", () => {
|
||||
nextTick(() => {
|
||||
handleInit(curIndex.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleResizeInit() {
|
||||
useResizeObserver('.pure-segmented', () => {
|
||||
nextTick(() => {
|
||||
handleInit(curIndex.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
(props.block || props.resize) && handleResizeInit();
|
||||
(props.block || props.resize) && handleResizeInit();
|
||||
|
||||
watch(
|
||||
() => curIndex.value,
|
||||
index => {
|
||||
nextTick(() => {
|
||||
handleInit(index);
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => curIndex.value,
|
||||
index => {
|
||||
nextTick(() => {
|
||||
handleInit(index);
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.size, handleResizeInit, {
|
||||
immediate: true
|
||||
});
|
||||
watch(() => props.size, handleResizeInit, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
const rendLabel = () => {
|
||||
return props.options.map((option, index) => {
|
||||
return (
|
||||
<label
|
||||
ref={`labelRef${index}`}
|
||||
class={[
|
||||
"pure-segmented-item",
|
||||
(props.disabled || option?.disabled) &&
|
||||
"pure-segmented-item-disabled"
|
||||
]}
|
||||
style={{
|
||||
background:
|
||||
curMouseActive.value === index ? segmentedItembg.value : "",
|
||||
color: props.disabled
|
||||
? null
|
||||
: !option.disabled &&
|
||||
(curIndex.value === index || curMouseActive.value === index)
|
||||
? isDark.value
|
||||
? "rgba(255, 255, 255, 0.85)"
|
||||
: "rgba(0,0,0,.88)"
|
||||
: ""
|
||||
}}
|
||||
onMouseenter={event => handleMouseenter({ option, index }, event)}
|
||||
onMouseleave={event => handleMouseleave({ option, index }, event)}
|
||||
onClick={event => handleChange({ option, index }, event)}
|
||||
>
|
||||
<input type="radio" name="segmented" />
|
||||
<div
|
||||
class="pure-segmented-item-label"
|
||||
v-tippy={{
|
||||
content: option?.tip,
|
||||
zIndex: 41000
|
||||
}}
|
||||
>
|
||||
{option.icon && !isFunction(option.label) ? (
|
||||
<span
|
||||
class="pure-segmented-item-icon"
|
||||
style={{ marginRight: option.label ? "6px" : 0 }}
|
||||
>
|
||||
{h(
|
||||
useRenderIcon(option.icon, {
|
||||
...option?.iconAttrs
|
||||
})
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
{option.label ? (
|
||||
isFunction(option.label) ? (
|
||||
h(option.label)
|
||||
) : (
|
||||
<span>{option.label}</span>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
});
|
||||
};
|
||||
const rendLabel = () => {
|
||||
return props.options.map((option, index) => {
|
||||
return (
|
||||
<label
|
||||
ref={`labelRef${index}`}
|
||||
class={['pure-segmented-item', (props.disabled || option?.disabled) && 'pure-segmented-item-disabled']}
|
||||
style={{
|
||||
background: curMouseActive.value === index ? segmentedItembg.value : '',
|
||||
color: props.disabled ? null : !option.disabled && (curIndex.value === index || curMouseActive.value === index) ? (isDark.value ? 'rgba(255, 255, 255, 0.85)' : 'rgba(0,0,0,.88)') : '',
|
||||
}}
|
||||
onMouseenter={event => handleMouseenter({ option, index }, event)}
|
||||
onMouseleave={event => handleMouseleave({ option, index }, event)}
|
||||
onClick={event => handleChange({ option, index }, event)}
|
||||
>
|
||||
<input type='radio' name='segmented' />
|
||||
<div
|
||||
class='pure-segmented-item-label'
|
||||
v-tippy={{
|
||||
content: option?.tip,
|
||||
zIndex: 41000,
|
||||
}}
|
||||
>
|
||||
{option.icon && !isFunction(option.label) ? (
|
||||
<span class='pure-segmented-item-icon' style={{ marginRight: option.label ? '6px' : 0 }}>
|
||||
{h(
|
||||
useRenderIcon(option.icon, {
|
||||
...option?.iconAttrs,
|
||||
}),
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
{option.label ? isFunction(option.label) ? h(option.label) : <span>{option.label}</span> : null}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={{
|
||||
"pure-segmented": true,
|
||||
"pure-segmented-block": props.block,
|
||||
"pure-segmented--large": props.size === "large",
|
||||
"pure-segmented--small": props.size === "small"
|
||||
}}
|
||||
>
|
||||
<div class="pure-segmented-group">
|
||||
<div
|
||||
class="pure-segmented-item-selected"
|
||||
style={{
|
||||
width: `${width.value}px`,
|
||||
transform: `translateX(${translateX.value}px)`,
|
||||
display: initStatus.value ? "block" : "none"
|
||||
}}
|
||||
></div>
|
||||
{rendLabel()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return () => (
|
||||
<div
|
||||
class={{
|
||||
'pure-segmented': true,
|
||||
'pure-segmented-block': props.block,
|
||||
'pure-segmented--large': props.size === 'large',
|
||||
'pure-segmented--small': props.size === 'small',
|
||||
}}
|
||||
>
|
||||
<div class='pure-segmented-group'>
|
||||
<div
|
||||
class='pure-segmented-item-selected'
|
||||
style={{
|
||||
width: `${width.value}px`,
|
||||
transform: `translateX(${translateX.value}px)`,
|
||||
display: initStatus.value ? 'block' : 'none',
|
||||
}}
|
||||
></div>
|
||||
{rendLabel()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -169,7 +169,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const isFixedColumn = (label: string) => {
|
||||
return !!dynamicColumns.value.filter(item => $t(item.label) === $t(label))[0].fixed;
|
||||
return !!dynamicColumns.value.filter(item => item.label === label)[0].fixed;
|
||||
};
|
||||
|
||||
const rendTippyProps = (content: string) => ({
|
||||
|
@ -213,7 +213,7 @@ export default defineComponent({
|
|||
<div class={[topClass.value]}>
|
||||
<el-checkbox class='!-mr-1' label='列展示' v-model={checkAll.value} indeterminate={isIndeterminate.value} onChange={value => handleCheckAllChange(value)} />
|
||||
<el-button type='primary' link onClick={() => onReset()}>
|
||||
重置
|
||||
{$t('buttons.reset')}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
|
@ -226,8 +226,8 @@ export default defineComponent({
|
|||
<div class='flex items-center'>
|
||||
<DragIcon class={['drag-btn w-[16px] mr-2', isFixedColumn(item) ? '!cursor-no-drop' : '!cursor-grab']} onMouseenter={(event: { preventDefault: () => void }) => rowDrop(event)} />
|
||||
<el-checkbox key={index} label={item} value={item} onChange={value => handleCheckColumnListChange(value, item)}>
|
||||
<span title={$t(item)} class='inline-block w-[120px] truncate hover:text-text_color_primary'>
|
||||
{$t(item)}
|
||||
<span title={item} class='inline-block w-[120px] truncate hover:text-text_color_primary'>
|
||||
{item}
|
||||
</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
|
|
|
@ -1,70 +1,71 @@
|
|||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import noAccess from "@/assets/status/403.svg?component";
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import noAccess from '@/assets/status/403.svg?component';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
defineOptions({
|
||||
name: "403"
|
||||
name: '403',
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noAccess />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80
|
||||
}
|
||||
}"
|
||||
>
|
||||
403
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
class="mb-4 text-gray-500"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120
|
||||
}
|
||||
}"
|
||||
>
|
||||
抱歉,你无权访问该页面
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
type="primary"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160
|
||||
}
|
||||
}"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
返回首页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noAccess />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
>
|
||||
403
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="mb-4 text-gray-500"
|
||||
>
|
||||
{{ $t('sorry_no_access') }}
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
type="primary"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
{{ $t('return_to_homepage') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,70 +1,71 @@
|
|||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import noExist from "@/assets/status/404.svg?component";
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import noExist from '@/assets/status/404.svg?component';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
defineOptions({
|
||||
name: "404"
|
||||
name: '404',
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noExist />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80
|
||||
}
|
||||
}"
|
||||
>
|
||||
404
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
class="mb-4 text-gray-500"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120
|
||||
}
|
||||
}"
|
||||
>
|
||||
抱歉,你访问的页面不存在
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
type="primary"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160
|
||||
}
|
||||
}"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
返回首页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noExist />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
>
|
||||
404
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="mb-4 text-gray-500"
|
||||
>
|
||||
{{ $t('sorry_page_not_found') }}
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
type="primary"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
{{ $t('return_to_homepage') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,70 +1,71 @@
|
|||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import noServer from "@/assets/status/500.svg?component";
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import noServer from '@/assets/status/500.svg?component';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
defineOptions({
|
||||
name: "500"
|
||||
name: '500',
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noServer />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80
|
||||
}
|
||||
}"
|
||||
>
|
||||
500
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
class="mb-4 text-gray-500"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120
|
||||
}
|
||||
}"
|
||||
>
|
||||
抱歉,服务器出错了
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
type="primary"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100
|
||||
}"
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160
|
||||
}
|
||||
}"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
返回首页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center items-center h-[640px]">
|
||||
<noServer />
|
||||
<div class="ml-12">
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 80,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="font-medium text-4xl mb-4 dark:text-white"
|
||||
>
|
||||
500
|
||||
</p>
|
||||
<p
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 120,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
class="mb-4 text-gray-500"
|
||||
>
|
||||
{{ $t('sorry_server_error') }}
|
||||
</p>
|
||||
<el-button
|
||||
v-motion
|
||||
:enter="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
delay: 160,
|
||||
},
|
||||
}"
|
||||
:initial="{
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
}"
|
||||
type="primary"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
{{ $t('return_to_homepage') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -209,8 +209,8 @@ const markOptions = computed<Array<OptionsType>>(() => {
|
|||
value: 'card',
|
||||
},
|
||||
{
|
||||
label: 'panel.pureTagsStyleChrome',
|
||||
tip: 'panel.pureTagsStyleChromeTip',
|
||||
label: t('panel.pureTagsStyleChrome'),
|
||||
tip: t('panel.pureTagsStyleChrome'),
|
||||
value: 'chrome',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -545,7 +545,7 @@ onBeforeUnmount(() => {
|
|||
<TagChrome />
|
||||
</div>
|
||||
<span class="tag-title">
|
||||
{{ transformI18n(item.meta.title) }}
|
||||
{{ $t(item.meta.title) }}
|
||||
</span>
|
||||
<span v-if="isFixedTag(item) ? false : index !== 0" class="chrome-close-btn" @click.stop="deleteMenu(item)">
|
||||
<IconifyIconOffline :icon="Close" />
|
||||
|
|
88
src/main.ts
88
src/main.ts
|
@ -1,68 +1,64 @@
|
|||
import { useI18n } from "@/plugins/i18n";
|
||||
import { setupStore } from "@/store";
|
||||
import { MotionPlugin } from "@vueuse/motion";
|
||||
import App from "./App.vue";
|
||||
import { getPlatformConfig } from "./config";
|
||||
import router from "./router";
|
||||
import { useI18n } from '@/plugins/i18n';
|
||||
import { setupStore } from '@/store';
|
||||
import { MotionPlugin } from '@vueuse/motion';
|
||||
import App from './App.vue';
|
||||
import { getPlatformConfig } from './config';
|
||||
import router from './router';
|
||||
// import { useEcharts } from "@/plugins/echarts";
|
||||
import { useElementPlus } from "@/plugins/elementPlus";
|
||||
import { injectResponsiveStorage } from "@/utils/responsive";
|
||||
import { createApp, type Directive } from "vue";
|
||||
import { useElementPlus } from '@/plugins/elementPlus';
|
||||
import { injectResponsiveStorage } from '@/utils/responsive';
|
||||
import { createApp, type Directive } from 'vue';
|
||||
|
||||
import Table from "@pureadmin/table";
|
||||
import Table from '@pureadmin/table';
|
||||
// import PureDescriptions from "@pureadmin/descriptions";
|
||||
// 引入重置样式
|
||||
import "./style/reset.scss";
|
||||
import './style/reset.scss';
|
||||
// 导入公共样式
|
||||
import "./style/index.scss";
|
||||
import './style/index.scss';
|
||||
// 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
|
||||
import "element-plus/dist/index.css";
|
||||
import "./style/tailwind.css";
|
||||
import 'element-plus/dist/index.css';
|
||||
import './style/tailwind.css';
|
||||
// 导入字体图标
|
||||
import "./assets/iconfont/iconfont.css";
|
||||
import "./assets/iconfont/iconfont.js";
|
||||
import './assets/iconfont/iconfont.css';
|
||||
import './assets/iconfont/iconfont.js';
|
||||
// 自定义指令
|
||||
import * as directives from "@/directives";
|
||||
import * as directives from '@/directives';
|
||||
// 全局注册@iconify/vue图标库
|
||||
import {
|
||||
FontIcon,
|
||||
IconifyIconOffline,
|
||||
IconifyIconOnline
|
||||
} from "./components/CommonIcon";
|
||||
import { FontIcon, IconifyIconOffline, IconifyIconOnline } from './components/CommonIcon';
|
||||
// 全局注册按钮级别权限组件
|
||||
import { Auth } from "@/components/Auth";
|
||||
import { Perms } from "@/components/Perms";
|
||||
import { Auth } from '@/components/Auth';
|
||||
import { Perms } from '@/components/Perms';
|
||||
// 全局注册vue-tippy
|
||||
import "tippy.js/dist/tippy.css";
|
||||
import "tippy.js/themes/light.css";
|
||||
import VueTippy from "vue-tippy";
|
||||
import { useEcharts } from "@/plugins/echarts";
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import 'tippy.js/themes/light.css';
|
||||
import VueTippy from 'vue-tippy';
|
||||
import { useEcharts } from '@/plugins/echarts';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
Object.keys(directives).forEach(key => {
|
||||
app.directive(key, (directives as { [key: string]: Directive })[key]);
|
||||
app.directive(key, (directives as { [key: string]: Directive })[key]);
|
||||
});
|
||||
|
||||
app.component("IconifyIconOffline", IconifyIconOffline);
|
||||
app.component("IconifyIconOnline", IconifyIconOnline);
|
||||
app.component("FontIcon", FontIcon);
|
||||
app.component("Auth", Auth);
|
||||
app.component("Perms", Perms);
|
||||
app.component('IconifyIconOffline', IconifyIconOffline);
|
||||
app.component('IconifyIconOnline', IconifyIconOnline);
|
||||
app.component('FontIcon', FontIcon);
|
||||
app.component('Auth', Auth);
|
||||
app.component('Perms', Perms);
|
||||
|
||||
app.use(VueTippy);
|
||||
|
||||
getPlatformConfig(app).then(async config => {
|
||||
setupStore(app);
|
||||
app.use(router);
|
||||
await router.isReady();
|
||||
injectResponsiveStorage(app, config);
|
||||
app
|
||||
.use(MotionPlugin)
|
||||
.use(useI18n)
|
||||
.use(useElementPlus)
|
||||
.use(Table)
|
||||
// .use(PureDescriptions)
|
||||
.use(useEcharts);
|
||||
app.mount("#app");
|
||||
setupStore(app);
|
||||
app.use(router);
|
||||
await router.isReady();
|
||||
injectResponsiveStorage(app, config);
|
||||
app
|
||||
.use(MotionPlugin)
|
||||
.use(useI18n)
|
||||
.use(useElementPlus)
|
||||
.use(Table)
|
||||
// .use(PureDescriptions)
|
||||
.use(useEcharts);
|
||||
app.mount('#app');
|
||||
});
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
// 多组件库的国际化和本地项目国际化兼容
|
||||
import { createI18n } from "vue-i18n";
|
||||
import type { App } from "vue";
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import type { App } from 'vue';
|
||||
|
||||
// ? 从本地存储中获取数据
|
||||
const languageData = localStorage.getItem("i18nStore");
|
||||
const languageData = localStorage.getItem('i18nStore');
|
||||
|
||||
// 配置多语言
|
||||
export const i18n = createI18n({
|
||||
// 如果要支持 compositionAPI,此项必须设置为 false
|
||||
legacy: false,
|
||||
// locale: 'zh',
|
||||
fallbackLocale: "en",
|
||||
// ? 全局注册$t方法
|
||||
globalInjection: true,
|
||||
// 本地内容存在时,首次加载如果本地存储没有多语言需要再刷新
|
||||
messages: languageData ? JSON.parse(languageData).i18n : {}
|
||||
// 如果要支持 compositionAPI,此项必须设置为 false
|
||||
legacy: false,
|
||||
// locale: 'zh',
|
||||
fallbackLocale: 'en',
|
||||
// ? 全局注册$t方法
|
||||
globalInjection: true,
|
||||
// 本地内容存在时,首次加载如果本地存储没有多语言需要再刷新
|
||||
messages: languageData ? JSON.parse(languageData).i18n : {},
|
||||
});
|
||||
|
||||
export const $t: any = (i18n.global as any).t as any;
|
||||
|
||||
export function useI18n(app: App) {
|
||||
app.use(i18n);
|
||||
app.use(i18n);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { fetchAddI18n, fetchDeleteI18n, fetchGetI18n, fetchGetI18nList, fetchUpdateI18n } from '@/api/v1/i18n';
|
||||
import { pageSizes } from '@/enums/baseConstant';
|
||||
import { message, storeMessage } from '@/utils/message';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
|
||||
export const userI18nStore = defineStore('i18nStore', {
|
||||
persist: true,
|
||||
|
@ -12,7 +12,7 @@ export const userI18nStore = defineStore('i18nStore', {
|
|||
// 多语言列表
|
||||
datalist: [],
|
||||
// 查询表单
|
||||
form: { keyName: undefined, translation: undefined },
|
||||
form: { keyName: '', translation: '', typeName: '' },
|
||||
isAddShown: false,
|
||||
// ? 分页查询结果
|
||||
pagination: {
|
||||
|
@ -50,6 +50,9 @@ export const userI18nStore = defineStore('i18nStore', {
|
|||
*/
|
||||
async getI18nMangeList() {
|
||||
const data = { ...this.pagination, ...this.form };
|
||||
delete data.pageSizes;
|
||||
delete data.total;
|
||||
delete data.background;
|
||||
const result = await fetchGetI18nList(data);
|
||||
|
||||
// 如果成功赋值内容
|
||||
|
@ -61,7 +64,6 @@ export const userI18nStore = defineStore('i18nStore', {
|
|||
return true;
|
||||
}
|
||||
|
||||
message(result.message, { type: 'error' });
|
||||
return false;
|
||||
},
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ export const closeAllMessage = (): void => ElMessage.closeAll();
|
|||
*/
|
||||
export const storeMessage = (result: BaseResult<any>) => {
|
||||
if (result.code !== 200) {
|
||||
message(result.message, { type: 'warning' });
|
||||
return false;
|
||||
}
|
||||
message(result.message, { type: 'success' });
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { rules } from '@/views/i18n/i18n-setting/utils/columns';
|
||||
import { FormProps } from '@/views/i18n/i18n-setting/utils/types';
|
||||
|
@ -9,7 +9,7 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||
formInline: () => ({
|
||||
keyName: '',
|
||||
translation: '',
|
||||
typeId: '',
|
||||
typeName: '',
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -17,14 +17,17 @@ const ruleFormRef = ref<FormInstance>();
|
|||
const form = ref(props.formInline);
|
||||
const i18nTypeStore = userI18nTypeStore();
|
||||
|
||||
onMounted(() => {
|
||||
i18nTypeStore.getI18nTypeList();
|
||||
});
|
||||
defineExpose({ ruleFormRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="ruleFormRef" :model="form" :rules="rules" isDefault-icon label-position="left" label-width="135px">
|
||||
<el-form-item label="选择添加语言分类" prop="typeId">
|
||||
<el-select v-model="form.typeId" filterable placeholder="选择添加语言分类">
|
||||
<el-option v-for="item in i18nTypeStore.datalist" :key="item.id" :label="item.typeName" :value="item.id" />
|
||||
<el-form-item label="选择添加语言分类" prop="typeName">
|
||||
<el-select v-model="form.typeName" filterable placeholder="选择添加语言分类">
|
||||
<el-option v-for="item in i18nTypeStore.datalist" :key="item.typeName" :label="item.typeName" :value="item.typeName" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
|
|
@ -10,17 +10,17 @@ import PureTableBar from '@/components/TableBar/src/bar';
|
|||
import PureTable from '@pureadmin/table';
|
||||
import { columns } from '@/views/i18n/i18n-setting/utils/columns';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
import { resetForm } from '@/views/menu/utils/hook';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
const tableRef = ref();
|
||||
const formRef = ref();
|
||||
const pageFormRef = ref();
|
||||
const i18nStore = userI18nStore();
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
* @param formEl
|
||||
*/
|
||||
const resetForm = async formEl => {
|
||||
const resetForm = async (formEl: any) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
await onSearch();
|
||||
|
@ -58,22 +58,25 @@ onMounted(() => {
|
|||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form ref="formRef" :inline="true" :model="i18nStore.form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="多语言key" prop="keyName">
|
||||
<el-input v-model="i18nStore.form.keyName" class="!w-[180px]" clearable placeholder="输入多语言key" />
|
||||
<el-form ref="pageFormRef" :inline="true" :model="i18nStore.form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item :label="$t('i18n.keyName')" prop="keyName">
|
||||
<el-input v-model="i18nStore.form.keyName" :placeholder="`${$t('input')}${$t('i18n.keyName')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="多语言翻译" prop="translation">
|
||||
<el-input v-model="i18nStore.form.translation" class="!w-[180px]" clearable placeholder="输入多语言翻译" />
|
||||
<el-form-item :label="$t('i18n.translation')" prop="translation">
|
||||
<el-input v-model="i18nStore.form.translation" :placeholder="`${$t('input')}${$t('i18n.translation')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('i18n.typeName')" prop="typeName">
|
||||
<el-input v-model="i18nStore.form.typeName" :placeholder="`${$t('input')}${$t('i18n.typeName')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="i18nStore.loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="i18nStore.loading" type="primary" @click="onSearch"> {{ $t('search') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(pageFormRef)"> {{ $t('buttons.reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar :columns="columns" :tableRef="tableRef?.getTableRef()" title="多语言管理" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<PureTableBar :columns="columns" :tableRef="tableRef?.getTableRef()" :title="$t('multilingual_management')" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> 添加多语言</el-button>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> {{ $t('add_multilingual') }} </el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
|
@ -87,6 +90,8 @@ onMounted(() => {
|
|||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
|
@ -95,11 +100,13 @@ onMounted(() => {
|
|||
@page-current-change="onCurrentPageChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)"> 修改 </el-button>
|
||||
<el-button v-show="row.menuType !== 3" :icon="useRenderIcon(AddFill)" :size="size" class="reset-margin" link type="primary"> 新增 </el-button>
|
||||
<el-popconfirm :title="`是否确认删除 ${row.translation}数据`" @confirm="onDelete(row)">
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)"> {{ $t('modify') }} </el-button>
|
||||
<el-button v-show="row.menuType !== 3" :icon="useRenderIcon(AddFill)" :size="size" class="reset-margin" link type="primary"> {{ $t('add_new') }} </el-button>
|
||||
<el-popconfirm :title="`${$t('confirm_delete')} ${row.translation}`" @confirm="onDelete(row)">
|
||||
<template #reference>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary"> 删除 </el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary">
|
||||
{{ $t('delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
// 多语言表格列字段
|
||||
import { reactive } from 'vue';
|
||||
import type { FormRules } from 'element-plus';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
export const columns: TableColumnList = [
|
||||
{ label: 'id', prop: 'id' },
|
||||
{ label: 'i18n_keyName', prop: 'keyName' },
|
||||
{ label: 'i18n_translation', prop: 'translation' },
|
||||
{ label: 'i18n_typeId', prop: 'typeId' },
|
||||
{ label: 'updateTime', prop: 'updateTime' },
|
||||
{ label: 'createTime', prop: 'createTime' },
|
||||
{ label: 'createUser', prop: 'createUser' },
|
||||
{ label: 'updateUser', prop: 'updateUser' },
|
||||
{ label: 'operation', fixed: 'right', width: 210, slot: 'operation' },
|
||||
{ label: $t('id'), prop: 'id' },
|
||||
{ label: $t('i18n.keyName'), prop: 'keyName' },
|
||||
{ label: $t('i18n.translation'), prop: 'translation' },
|
||||
{ label: $t('i18n.typeName'), prop: 'typeName' },
|
||||
{ label: $t('table.updateTime'), prop: 'updateTime' },
|
||||
{ label: $t('table.createTime'), prop: 'createTime' },
|
||||
{ label: $t('table.createUser'), prop: 'createUser' },
|
||||
{ label: $t('table.updateUser'), prop: 'updateUser' },
|
||||
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
|
||||
];
|
||||
|
||||
// 添加多语言表单规则
|
||||
export const rules = reactive<FormRules>({
|
||||
keyName: [{ required: true, message: '输入多语言key', trigger: 'blur' }],
|
||||
translation: [{ required: true, message: '输入多语言翻译', trigger: 'blur' }],
|
||||
typeId: [{ required: true, message: '选择添加语言分类', trigger: 'blur' }],
|
||||
keyName: [{ required: true, message: `${$t('input')}${$t('i18n.keyName')}`, trigger: 'blur' }],
|
||||
translation: [{ required: true, message: `${$t('input')}${$t('i18n.translation')}`, trigger: 'blur' }],
|
||||
typeName: [{ required: true, message: `${$t('input')}${$t('i18n.typeName')}`, trigger: 'blur' }],
|
||||
});
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import { h, reactive, ref } from 'vue';
|
||||
import { h, ref } from 'vue';
|
||||
import { userI18nStore } from '@/store/i18n/i18n';
|
||||
import { messageBox } from '@/utils/message';
|
||||
import { addDialog } from '@/components/BaseDialog/index';
|
||||
import { addDialog, closeDialog } from '@/components/BaseDialog/index';
|
||||
import { deviceDetection } from '@pureadmin/utils';
|
||||
import I18nDialog from '@/views/i18n/i18n-setting/i18n-dialog.vue';
|
||||
import type { FormProps } from '@/views/i18n/i18n-setting/utils/types';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
export const formRef = ref();
|
||||
const i18nStore = userI18nStore();
|
||||
const ids = ref<string[]>([]);
|
||||
|
||||
// 更新表单数据
|
||||
export const updateForm = reactive({
|
||||
id: '',
|
||||
languageId: '',
|
||||
keyName: '',
|
||||
translate: '',
|
||||
parentId: '',
|
||||
});
|
||||
export const ids = ref<string[]>([]);
|
||||
|
||||
/**
|
||||
* * 查询内容
|
||||
|
@ -34,25 +26,56 @@ export const onSearch = async () => {
|
|||
*/
|
||||
export const onAdd = () => {
|
||||
addDialog({
|
||||
title: `添加多语言`,
|
||||
title: `${$t('add_multilingual')}`,
|
||||
width: '30%',
|
||||
props: { formInline: { keyName: '', translation: '', typeId: '' } },
|
||||
props: { formInline: { keyName: '', translation: '', typeName: '' } },
|
||||
draggable: true,
|
||||
fullscreen: deviceDetection(),
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(I18nDialog, { ref: formRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const form = options.props.formInline as FormProps;
|
||||
formRef.value.ruleFormRef.validate(async (valid: any) => {
|
||||
if (!valid) return;
|
||||
footerButtons: [
|
||||
{
|
||||
label: '取消',
|
||||
text: true,
|
||||
bg: true,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
closeDialog(options, index);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: $t('buttons.pureConfirm'),
|
||||
type: 'primary',
|
||||
text: true,
|
||||
bg: true,
|
||||
btnClick: ({ dialog: { options, index } }) => {
|
||||
const form = options.props.formInline as FormProps;
|
||||
formRef.value.ruleFormRef.validate(async (valid: any) => {
|
||||
if (!valid) return;
|
||||
|
||||
const result = await i18nStore.addI18n(form);
|
||||
if (!result) return;
|
||||
done();
|
||||
await onSearch();
|
||||
});
|
||||
},
|
||||
const result = await i18nStore.addI18n(form);
|
||||
if (!result) return;
|
||||
closeDialog(options, index);
|
||||
await onSearch();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '继续添加',
|
||||
type: 'success',
|
||||
text: true,
|
||||
bg: true,
|
||||
btnClick: ({ dialog: { options } }) => {
|
||||
const form = options.props.formInline as FormProps;
|
||||
formRef.value.ruleFormRef.validate(async (valid: any) => {
|
||||
if (!valid) return;
|
||||
const result = await i18nStore.addI18n(form);
|
||||
if (!result) return;
|
||||
await onSearch();
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
export interface FormItemProps {
|
||||
keyName: string;
|
||||
translation: string;
|
||||
typeId: string;
|
||||
typeName: string;
|
||||
}
|
||||
|
||||
// 添加或修改表单Props
|
||||
|
|
|
@ -10,7 +10,7 @@ import { onAdd, onDelete, onSearch, onUpdate } from '@/views/i18n/i18n-type-sett
|
|||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import TableIsDefaultTag from '@/components/TableBar/src/TableIsDefaultTag.vue';
|
||||
import { resetForm } from '@/views/menu/utils/hook';
|
||||
import { resetForm } from '@/views/system/menu/utils/hook';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
|
||||
const tableRef = ref();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import ReCol from '@/components/MyCol';
|
||||
import { formRules } from './utils/rule';
|
||||
import { FormProps } from './utils/types';
|
||||
import { formRules } from '@/views/system/menu/utils/rule';
|
||||
import { FormProps } from '@/views/system/menu/utils/types';
|
||||
import { IconSelect } from '@/components/ReIcon';
|
||||
import Segmented from '@/components/ReSegmented';
|
||||
import ReAnimateSelector from '@/components/AnimateSelector';
|
|
@ -7,10 +7,10 @@ import Delete from '@iconify-icons/ep/delete';
|
|||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
import AddFill from '@iconify-icons/ri/add-circle-line';
|
||||
import { dataList, handleDelete, loading, onSearch, openDialog, resetForm } from '@/views/menu/utils/hook';
|
||||
import { dataList, handleDelete, loading, onSearch, openDialog, resetForm } from '@/views/system/menu/utils/hook';
|
||||
import form from '@/views/role/form.vue';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { columns } from '@/views/menu/utils/rule';
|
||||
import { columns } from '@/views/system/menu/utils/rule';
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemMenu',
|
|
@ -3,9 +3,9 @@ import { handleTree } from '@/utils/tree';
|
|||
import { message } from '@/utils/message';
|
||||
import { getMenuList } from '@/api/v1/system';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { addDialog } from '@/components/BaseDialog';
|
||||
import { addDialog } from '@/components/BaseDialog/index';
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import type { FormItemProps } from '../utils/types';
|
||||
import type { FormItemProps } from './types';
|
||||
|
||||
import { cloneDeep, deviceDetection, isAllEmpty } from '@pureadmin/utils';
|
||||
import { userRouterStore } from '@/store/modules/router';
|
|
@ -3,7 +3,7 @@ import type { FormRules } from 'element-plus';
|
|||
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { isAllEmpty } from '@pureadmin/utils';
|
||||
import { getMenuType } from '@/views/menu/utils/hook';
|
||||
import { getMenuType } from '@/views/system/menu/utils/hook';
|
||||
|
||||
export const columns: TableColumnList = [
|
||||
{
|
Loading…
Reference in New Issue