diff --git a/components.d.ts b/components.d.ts index 2b4a381..cb95b2d 100644 --- a/components.d.ts +++ b/components.d.ts @@ -6,12 +6,17 @@ declare module 'vue' { export interface GlobalComponents { 404: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Error/404.vue')['default'] CountTo: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/CountTo/index.vue')['default'] + Dialog: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Dialog/index.vue')['default'] Echart: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Echart/index.vue')['default'] + Editor: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Editor/index.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] ElBacktop: typeof import('element-plus/es')['ElBacktop'] ElButton: typeof import('element-plus/es')['ElButton'] ElCard: typeof import('element-plus/es')['ElCard'] ElCol: typeof import('element-plus/es')['ElCol'] + ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDialog: typeof import('element-plus/es')['ElDialog'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] @@ -19,21 +24,31 @@ declare module 'vue' { ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElInput: typeof import('element-plus/es')['ElInput'] + ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElRadio: typeof import('element-plus/es')['ElRadio'] + ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] + ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] + ElSelect: typeof import('element-plus/es')['ElSelect'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] + ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] + ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] HelloWorld: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/HelloWorld.vue')['default'] ParentView: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/ParentView/index.vue')['default'] Preview: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Preview/index.vue')['default'] + Qrcode: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Qrcode/index.vue')['default'] Redirect: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Redirect/index.vue')['default'] + Search: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/Search/index.vue')['default'] SvgIcon: typeof import('C:/Users/Saber/Documents/HBuilderProjects/vue-element-plus-admin/src/components/SvgIcon/index.vue')['default'] } } -export {} +export { } diff --git a/package.json b/package.json index 6369855..933a1f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-element-plus-admin", "version": "1.0.0", - "description": "一套基于vue3、element-plus、typesScript、vite的后台集成方案", + "description": "一套基于vue3、element-plus、typesScript、vite2的后台集成方案", "author": "Archer <502431556@qq.com>", "private": false, "scripts": { @@ -26,9 +26,11 @@ "@element-plus/icons": "^0.0.11", "@vueuse/core": "^6.5.3", "axios": "^0.22.0", + "clipboard": "^2.0.8", "echarts": "^5.2.1", "echarts-wordcloud": "^2.0.0", "element-plus": "1.1.0-beta.20", + "highlight.js": "^11.2.0", "intro.js": "^4.2.2", "lodash-es": "^4.17.21", "mockjs": "^1.1.0", @@ -36,9 +38,11 @@ "path-browserify": "^1.0.1", "path-to-regexp": "^6.2.0", "pinia": "^2.0.0-rc.13", + "qrcode": "^1.4.4", "qs": "^6.10.1", "vue": "^3.2.16", "vue-router": "^4.0.11", + "wangeditor": "^4.7.9", "web-storage-cache": "^1.1.1" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index f1d3a5c..5af12d1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,14 @@ - + + + diff --git a/src/assets/img/avatar.png b/src/assets/img/avatar.png new file mode 100644 index 0000000..e16488e Binary files /dev/null and b/src/assets/img/avatar.png differ diff --git a/src/components/CountTo/index.vue b/src/components/CountTo/index.vue index 94bb406..164ad3b 100644 --- a/src/components/CountTo/index.vue +++ b/src/components/CountTo/index.vue @@ -15,7 +15,9 @@ const emit = defineEmits(['mounted', 'callback']) defineExpose({ pauseResume, - reset + reset, + start, + pause }) const state = reactive<{ diff --git a/src/components/Dialog/index.vue b/src/components/Dialog/index.vue new file mode 100644 index 0000000..25537ec --- /dev/null +++ b/src/components/Dialog/index.vue @@ -0,0 +1,193 @@ + + + + + {{ title }} + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue new file mode 100644 index 0000000..6648f19 --- /dev/null +++ b/src/components/Editor/index.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/src/components/Editor/types.ts b/src/components/Editor/types.ts new file mode 100644 index 0000000..260669d --- /dev/null +++ b/src/components/Editor/types.ts @@ -0,0 +1,13 @@ +export interface EditorConfig { + height?: number // 富文本高度 + zIndex?: number // 层级 + placeholder?: string // 提示文字 + focus?: boolean // 是否聚焦 + onchangeTimeout?: number // 几秒监听一次变化 + customAlert?: (s: string, t: string) => {} // 自定义提示 + menus?: string[] // 按钮菜单 + colors?: string[] // 颜色 + fontNames?: string[] // 字体 + lineHeights?: string[] // 行间距 + showFullScreen?: boolean // 是否全屏 +} diff --git a/src/components/Qrcode/index.vue b/src/components/Qrcode/index.vue new file mode 100644 index 0000000..09f6a6a --- /dev/null +++ b/src/components/Qrcode/index.vue @@ -0,0 +1,267 @@ + + + + + + + {{ disabledText }} + + + + + + + + diff --git a/src/components/Qrcode/types.ts b/src/components/Qrcode/types.ts new file mode 100644 index 0000000..86edb6f --- /dev/null +++ b/src/components/Qrcode/types.ts @@ -0,0 +1,9 @@ +export interface LogoTypes { + src?: string + logoSize?: number + bgColor?: string + borderSize?: number + crossOrigin?: string + borderRadius?: number + logoRadius?: number +} diff --git a/src/components/Search/index.vue b/src/components/Search/index.vue new file mode 100644 index 0000000..1a27387 --- /dev/null +++ b/src/components/Search/index.vue @@ -0,0 +1,337 @@ + + + + + + + + { + changeVal(val, item) + } + " + /> + + + + { + changeVal(val, item) + } + " + /> + + + + { + changeVal(val, item) + } + " + > + + + + + + { + changeVal(val, item) + } + " + > + + + {{ item.optionLabel ? v[item.optionLabel] : v.label }} + + + + + {{ item.optionLabel ? v[item.optionLabel] : v.label }} + + + + + + + + + + { + changeVal(val, item) + } + " + /> + + + + { + changeVal(val, item) + } + " + /> + + + + { + changeVal(val, item) + } + " + /> + + + + 查询 + + 重置 + + + + + + + + 查询 + + + + 重置 + + + + + + + + + + + diff --git a/src/components/index.ts b/src/components/index.ts index bf18304..c99ec5a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,6 +1,10 @@ import type { App } from 'vue' import SvgIcon from './SvgIcon/index.vue' // svg组件 +import ComSearch from './Search/index.vue' // search组件 +import ComDialog from './Dialog/index.vue' // dialog组件 export function setupGlobCom(app: App): void { app.component('SvgIcon', SvgIcon) + app.component('ComSearch', ComSearch) + app.component('ComDialog', ComDialog) } diff --git a/src/directives/clipboard/index.ts b/src/directives/clipboard/index.ts new file mode 100644 index 0000000..c502937 --- /dev/null +++ b/src/directives/clipboard/index.ts @@ -0,0 +1,71 @@ +import Clipboard from 'clipboard' +import { Directive, DirectiveBinding } from 'vue' +import { Message } from '_c/Message' + +if (!Clipboard) { + throw new Error('you should npm install `clipboard` --save at first ') +} + +export const clipboard: Directive = { + beforeMount(el: HTMLElement, binding: DirectiveBinding) { + createdClipboard(el, binding.arg, binding.value) + }, + updated(el: HTMLElement | any, binding: DirectiveBinding) { + if (binding.arg === 'success') { + el._v_clipboard_success = binding.value + } else if (binding.arg === 'error') { + el._v_clipboard_error = binding.value + } else { + el._v_clipboard.text = function () { + return binding.value + } + el._v_clipboard.action = function () { + return 'copy' + } + } + }, + unmounted(el: HTMLElement | any, binding: DirectiveBinding) { + if (binding.arg === 'success') { + delete el._v_clipboard_success + } else if (binding.arg === 'error') { + delete el._v_clipboard_error + } else { + el._v_clipboard.destroy() + delete el._v_clipboard + } + } +} + +function createdClipboard(el: HTMLElement | any, arg: string | undefined, value: any) { + if (arg === 'success') { + el._v_clipboard_success = value + } else if (arg === 'error') { + el._v_clipboard_error = value + } else { + const clipboard = new Clipboard(el, { + text() { + return value + }, + action() { + return 'copy' + } + }) + clipboard.on('success', (e) => { + const callback = el._v_clipboard_success + if (callback) { + callback(e) + } else { + Message.success('复制成功') + } + }) + clipboard.on('error', (e) => { + const callback = el._v_clipboard_error + if (callback) { + callback(e) + } else { + Message.success('复制失败') + } + }) + el._v_clipboard = clipboard + } +} diff --git a/src/directives/index.ts b/src/directives/index.ts new file mode 100644 index 0000000..9fb6f77 --- /dev/null +++ b/src/directives/index.ts @@ -0,0 +1,7 @@ +import type { App } from 'vue' + +import { clipboard } from './clipboard' + +export function setupDirectives(app: App) { + app.directive('clipboard', clipboard) +} diff --git a/src/layout/components/UserInfo/index.vue b/src/layout/components/UserInfo/index.vue index 2660983..3513a4b 100644 --- a/src/layout/components/UserInfo/index.vue +++ b/src/layout/components/UserInfo/index.vue @@ -27,7 +27,7 @@ const tagsViewStore = useTagsViewStore() import { useCache } from '@/hooks/web/useCache' const { wsCache } = useCache() // @ts-ignore -import avatarImg from '@/assets/img/avatar.gif' +import avatarImg from '@/assets/img/avatar.png' const { replace, push } = useRouter() async function loginOut(): Promise { diff --git a/src/main.ts b/src/main.ts index 0ed35bb..33f09e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,8 +6,12 @@ import router, { setupRouter } from './router' // 路由 import { setupStore } from './store' // 状态管理 +import { setupDirectives } from '@/directives' // 自定义指令 + import { setupGlobCom } from './components' +import { setupElement } from '@/plugins/element-plus' + import '@/styles/index.less' import 'virtual:svg-icons-register' @@ -23,6 +27,10 @@ setupStore(app) // 引入状态管理 setupRouter(app) // 引入路由 +setupDirectives(app) + +setupElement(app) + setupGlobCom(app) // 引入全局组件 router.isReady().then(() => { diff --git a/src/plugins/element-plus/element.config.ts b/src/plugins/element-plus/element.config.ts new file mode 100644 index 0000000..595193c --- /dev/null +++ b/src/plugins/element-plus/element.config.ts @@ -0,0 +1,18 @@ +/** + * 为了保持多页element组件的样式统一,提供全局配置的方法。 + */ +import { ConfigElement } from './types' + +const elementConfig: ConfigElement = { + /** + * 尺寸 + */ + size: 'medium', + + /** + * 层级 + */ + zIndex: 2000 +} + +export default elementConfig diff --git a/src/plugins/element-plus/index.ts b/src/plugins/element-plus/index.ts new file mode 100644 index 0000000..26c5e48 --- /dev/null +++ b/src/plugins/element-plus/index.ts @@ -0,0 +1,23 @@ +// 按需加载element +// 目前需要手动引入loading等插件,无法自动导入 +// size和zIndex也需要这样设置,暂时还无法在全局配置组件中去设置 +// 需要看看后面官方是不是能优化这点 +import type { App } from 'vue' + +import ElementConfig from './element.config' + +// element全局配置项 +const { size, zIndex } = ElementConfig + +import { ElLoading } from 'element-plus' + +const plugins = [ElLoading] + +export function setupElement(app: App): void { + plugins.forEach((plugin: any) => { + app.use(plugin) + }) + + // 全局配置 + app.config.globalProperties.$ELEMENT = { size: size, zIndex: zIndex } +} diff --git a/src/plugins/element-plus/types.ts b/src/plugins/element-plus/types.ts new file mode 100644 index 0000000..807725a --- /dev/null +++ b/src/plugins/element-plus/types.ts @@ -0,0 +1,7 @@ +/** + * element配置 + */ +export interface ConfigElement { + zIndex: number + size: 'medium' | 'small' | 'mini' +} diff --git a/src/router/index.ts b/src/router/index.ts index 377fd79..ed9dcbe 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -144,71 +144,47 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [ meta: { title: '图片预览' } - } - // { - // path: 'button', - // component: () => import('_v/components-demo/button/index.vue'), - // name: 'ButtonDemo', - // meta: { - // title: '按钮' - // } - // }, - // { - // path: 'message', - // component: () => import('_v/components-demo/message/index.vue'), - // name: 'MessageDemo', - // meta: { - // title: '消息提示' - // } - // }, - // { - // path: 'count-to', - // component: () => import('_v/components-demo/count-to/index.vue'), - // name: 'CountToDemo', - // meta: { - // title: '数字动画' - // } - // }, - // { - // path: 'search', - // component: () => import('_v/components-demo/search/index.vue'), - // name: 'SearchDemo', - // meta: { - // title: '查询' - // } - // }, - // { - // path: 'editor', - // component: () => import('_v/components-demo/editor/index.vue'), - // name: 'EditorDemo', - // meta: { - // title: '富文本编辑器' - // } - // }, - // { - // path: 'markdown', - // component: () => import('_v/components-demo/markdown/index.vue'), - // name: 'MarkdownDemo', - // meta: { - // title: 'markdown编辑器' - // } - // }, - // { - // path: 'dialog', - // component: () => import('_v/components-demo/dialog/index.vue'), - // name: 'DialogDemo', - // meta: { - // title: '弹窗' - // } - // }, - // { - // path: 'more', - // component: () => import('_v/components-demo/more/index.vue'), - // name: 'MoreDemo', - // meta: { - // title: '显示更多' - // } - // }, + }, + { + path: 'message', + component: () => import('_v/components-demo/message/index.vue'), + name: 'MessageDemo', + meta: { + title: '消息提示' + } + }, + { + path: 'count-to', + component: () => import('_v/components-demo/count-to/index.vue'), + name: 'CountToDemo', + meta: { + title: '数字动画' + } + }, + { + path: 'search', + component: () => import('_v/components-demo/search/index.vue'), + name: 'SearchDemo', + meta: { + title: '查询' + } + }, + { + path: 'editor', + component: () => import('_v/components-demo/editor/index.vue'), + name: 'EditorDemo', + meta: { + title: '富文本编辑器' + } + }, + { + path: 'dialog', + component: () => import('_v/components-demo/dialog/index.vue'), + name: 'DialogDemo', + meta: { + title: '弹窗' + } + }, // { // path: 'detail', // component: () => import('_v/components-demo/detail/index.vue'), @@ -217,14 +193,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [ // title: '详情组件' // } // }, - // { - // path: 'qrcode', - // component: () => import('_v/components-demo/qrcode/index.vue'), - // name: 'QrcodeDemo', - // meta: { - // title: '二维码组件' - // } - // }, + { + path: 'qrcode', + component: () => import('_v/components-demo/qrcode/index.vue'), + name: 'QrcodeDemo', + meta: { + title: '二维码' + } + } // { // path: 'avatars', // component: () => import('_v/components-demo/avatars/index.vue'), diff --git a/src/views/components-demo/count-to/index.vue b/src/views/components-demo/count-to/index.vue new file mode 100644 index 0000000..d2be3ec --- /dev/null +++ b/src/views/components-demo/count-to/index.vue @@ -0,0 +1,111 @@ + + + + + + + + + + + + startVal: + + + + + endVal: + + + + + duration: + + + + separator: + + + prefix: + + + suffix: + + + + start + pause/resume + + + + + + + + + + diff --git a/src/views/components-demo/dialog/index.vue b/src/views/components-demo/dialog/index.vue new file mode 100644 index 0000000..b3f833c --- /dev/null +++ b/src/views/components-demo/dialog/index.vue @@ -0,0 +1,27 @@ + + + + 打开弹窗 + + + 我是弹窗内容 + + 取消 + 确定 + + + + + + + + diff --git a/src/views/components-demo/editor/index.vue b/src/views/components-demo/editor/index.vue new file mode 100644 index 0000000..39c5a2a --- /dev/null +++ b/src/views/components-demo/editor/index.vue @@ -0,0 +1,45 @@ + + + + + + + + 获取TTML(请在控制台查看) + 获取TEXT(请在控制台查看) + 获取JSON(请在控制台查看) + + + + + + + diff --git a/src/views/components-demo/message/index.vue b/src/views/components-demo/message/index.vue new file mode 100644 index 0000000..f1584c2 --- /dev/null +++ b/src/views/components-demo/message/index.vue @@ -0,0 +1,24 @@ + + + + 显示 + + + + + + diff --git a/src/views/components-demo/qrcode/index.vue b/src/views/components-demo/qrcode/index.vue new file mode 100644 index 0000000..b8133a6 --- /dev/null +++ b/src/views/components-demo/qrcode/index.vue @@ -0,0 +1,89 @@ + + + + + 基础用法,默认canvas + + + + img标签 + + + + 样式配置 + + + + 点击 + + + + 异步内容 + + + + 二维码失效 + + + + logo配置 + + + + logo样式配置 + + + + 大小配置 + + + + + + + + + diff --git a/src/views/components-demo/search/data.ts b/src/views/components-demo/search/data.ts new file mode 100644 index 0000000..f67fb9b --- /dev/null +++ b/src/views/components-demo/search/data.ts @@ -0,0 +1,149 @@ +export const classicData = [ + { + label: '即时配送', + value: true, + itemType: 'switch', + field: 'delivery' + }, + { + label: '活动名称', + value: '', + itemType: 'input', + field: 'name', + placeholder: '活动名称', + clearable: true, + rules: [ + { + required: true, + message: '请输入活动名称' + } + ] + }, + { + label: '活动区域', + value: '', + itemType: 'select', + placeholder: '活动区域', + clearable: true, + field: 'region', + options: [ + { + title: '区域一', + value: 'fujian' + }, + { + title: '区域二', + value: 'beijing' + } + ], + rules: [ + { + itemType: 'string', + required: true, + message: '请选择活动区域' + } + ] + }, + { + label: '特殊资源', + value: '2', + itemType: 'radio', + field: 'resource', + radioType: 'button', // button or radio + options: [ + { + label: '线上品牌商赞助', + value: '1' + }, + { + label: '线下场地免费', + value: '2' + } + ] + }, + // { + // label: '组织机构', + // value: [], + // itemType: 'treeSelect', + // field: 'company', + // allowClear: true, + // placeholder: '请选择组织机构', + // treeCheckable: false, + // maxTagCount: 2, + // options: [ + // { + // title: 'Node1', + // value: '0-0', + // key: '0-0', + // children: [ + // { + // title: 'Child Node1', + // value: '0-0-0', + // key: '0-0-0' + // } + // ] + // }, + // { + // title: 'Node2', + // value: '0-1', + // key: '0-1', + // children: [ + // { + // title: 'Child Node3', + // value: '0-1-0', + // key: '0-1-0', + // disabled: true + // }, + // { + // title: 'Child Node4', + // value: '0-1-1', + // key: '0-1-1' + // }, + // { + // title: 'Child Node5', + // value: '0-1-2', + // key: '0-1-2' + // } + // ] + // } + // ] + // }, + { + label: '日选择器', + value: '', + itemType: 'datePicker', + field: 'date1', + clearable: true, + format: 'YYYY-MM-DD', + placeholder: '请选择日期' + }, + { + label: '月选择器', + value: '', + itemType: 'datePicker', + field: 'date2', + clearable: true, + format: 'YYYY-MM', + placeholder: '请选择日期' + }, + { + label: '范围选择器', + value: [], + itemType: 'datePicker', + field: 'date3', + clearable: true, + type: 'daterange', + rangeSeparator: '至', + startPlaceholder: '开始日期', + endPlaceholder: '结束日期' + }, + { + label: '周选择器', + value: '', + itemType: 'datePicker', + field: 'date4', + type: 'week', + clearable: true, + placeholder: '请选择日期' + } +] diff --git a/src/views/components-demo/search/index.vue b/src/views/components-demo/search/index.vue new file mode 100644 index 0000000..77413ef --- /dev/null +++ b/src/views/components-demo/search/index.vue @@ -0,0 +1,95 @@ + + + + + + + 查询/重置后的数据:{{ formData1 }} + + + + + + 查询/重置后的数据:{{ formData2 }} + + + + + + 查询/重置后的数据:{{ formData3 }} + + + + + + +