From 20f838adde8e391bcca48468ced2b488e5a3862b Mon Sep 17 00:00:00 2001 From: hw Date: Thu, 30 Oct 2025 16:33:33 +0800 Subject: [PATCH] fix: eslint errors --- apps/web-ele/.env.development | 2 +- apps/web-ele/package.json | 1 + apps/web-ele/src/store/index.ts | 11 + apps/web-ele/src/store/tagsView.ts | 202 ++++++++ apps/web-ele/src/utils/index.ts | 28 ++ apps/web-ele/src/utils/routerHelper.ts | 22 + apps/web-ele/src/utils/tree.ts | 442 ++++++++++++++++++ .../mp/components/wx-account-select/index.ts | 1 + .../mp/components/wx-account-select/main.vue | 83 ++++ .../views/mp/components/wx-location/index.ts | 1 + .../views/mp/components/wx-location/main.vue | 61 +++ .../mp/components/wx-material-select/index.ts | 3 + .../mp/components/wx-material-select/main.vue | 295 ++++++++++++ .../mp/components/wx-material-select/types.ts | 11 + .../src/views/mp/components/wx-msg/card.scss | 116 +++++ .../views/mp/components/wx-msg/comment.scss | 109 +++++ .../mp/components/wx-msg/components/Msg.vue | 85 ++++ .../components/wx-msg/components/MsgEvent.vue | 54 +++ .../components/wx-msg/components/MsgList.vue | 77 +++ .../src/views/mp/components/wx-msg/index.ts | 3 + .../src/views/mp/components/wx-msg/main.vue | 210 +++++++++ .../src/views/mp/components/wx-msg/types.ts | 17 + .../src/views/mp/components/wx-music/index.ts | 1 + .../src/views/mp/components/wx-music/main.vue | 70 +++ .../src/views/mp/components/wx-news/index.ts | 1 + .../src/views/mp/components/wx-news/main.vue | 123 +++++ .../wx-reply/components/TabImage.vue | 186 ++++++++ .../wx-reply/components/TabMusic.vue | 137 ++++++ .../wx-reply/components/TabNews.vue | 92 ++++ .../wx-reply/components/TabText.vue | 29 ++ .../wx-reply/components/TabVideo.vue | 148 ++++++ .../wx-reply/components/TabVoice.vue | 174 +++++++ .../components/wx-reply/components/types.ts | 58 +++ .../src/views/mp/components/wx-reply/index.ts | 8 + .../src/views/mp/components/wx-reply/main.vue | 215 +++++++++ .../mp/components/wx-video-play/index.ts | 1 + .../mp/components/wx-video-play/main.vue | 76 +++ .../mp/components/wx-voice-play/index.ts | 1 + .../mp/components/wx-voice-play/main.vue | 108 +++++ apps/web-ele/src/views/mp/hooks/useUpload.ts | 67 +++ .../views/mp/menu/assets/iphone_backImg.png | Bin 0 -> 34272 bytes .../src/views/mp/menu/assets/menu_foot.png | Bin 0 -> 1348 bytes .../src/views/mp/menu/assets/menu_head.png | Bin 0 -> 12673 bytes .../views/mp/menu/components/menuOptions.ts | 42 ++ .../src/views/mp/menu/components/types.ts | 73 +++ apps/web-ele/src/views/mp/menu/data.ts | 9 + apps/web-ele/src/views/mp/menu/index.vue | 411 ++++++++++++++++ .../src/views/mp/menu/modules/menu-editor.vue | 278 +++++++++++ .../views/mp/menu/modules/menu-previewer.vue | 250 ++++++++++ playground/package.json | 3 +- pnpm-lock.yaml | 59 ++- 51 files changed, 4433 insertions(+), 21 deletions(-) create mode 100644 apps/web-ele/src/store/tagsView.ts create mode 100644 apps/web-ele/src/utils/tree.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-account-select/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-account-select/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-location/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-location/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-material-select/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-material-select/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-material-select/types.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/card.scss create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/comment.scss create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/components/Msg.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/components/MsgEvent.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/components/MsgList.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-msg/types.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-music/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-music/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-news/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-news/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabImage.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabMusic.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabNews.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabText.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabVideo.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/TabVoice.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/components/types.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-reply/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-video-play/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-video-play/main.vue create mode 100644 apps/web-ele/src/views/mp/components/wx-voice-play/index.ts create mode 100644 apps/web-ele/src/views/mp/components/wx-voice-play/main.vue create mode 100644 apps/web-ele/src/views/mp/hooks/useUpload.ts create mode 100644 apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png create mode 100644 apps/web-ele/src/views/mp/menu/assets/menu_foot.png create mode 100644 apps/web-ele/src/views/mp/menu/assets/menu_head.png create mode 100644 apps/web-ele/src/views/mp/menu/components/menuOptions.ts create mode 100644 apps/web-ele/src/views/mp/menu/components/types.ts create mode 100644 apps/web-ele/src/views/mp/menu/data.ts create mode 100644 apps/web-ele/src/views/mp/menu/index.vue create mode 100644 apps/web-ele/src/views/mp/menu/modules/menu-editor.vue create mode 100644 apps/web-ele/src/views/mp/menu/modules/menu-previewer.vue diff --git a/apps/web-ele/.env.development b/apps/web-ele/.env.development index 77c13d398..cf25c6dd5 100644 --- a/apps/web-ele/.env.development +++ b/apps/web-ele/.env.development @@ -6,7 +6,7 @@ VITE_BASE=/ # 请求路径 VITE_BASE_URL=http://127.0.0.1:48080 # 接口地址 -VITE_GLOB_API_URL=/admin-api +VITE_GLOB_API_URL=http://47.103.66.220:48080/admin-api # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server # 是否打开 devtools,true 为打开,false 为关闭 diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json index aa96e8564..9ded8eee4 100644 --- a/apps/web-ele/package.json +++ b/apps/web-ele/package.json @@ -45,6 +45,7 @@ "@vben/utils": "workspace:*", "@vueuse/core": "catalog:", "@vueuse/integrations": "catalog:", + "benz-amr-recorder": "^1.1.5", "cropperjs": "catalog:", "dayjs": "catalog:", "element-plus": "catalog:", diff --git a/apps/web-ele/src/store/index.ts b/apps/web-ele/src/store/index.ts index 269586ee8..af0c4ab6f 100644 --- a/apps/web-ele/src/store/index.ts +++ b/apps/web-ele/src/store/index.ts @@ -1 +1,12 @@ +import type { App } from 'vue'; + +import { createPinia } from 'pinia'; + export * from './auth'; +const store = createPinia(); + +export const setupStore = (app: App) => { + app.use(store); +}; + +export { store }; diff --git a/apps/web-ele/src/store/tagsView.ts b/apps/web-ele/src/store/tagsView.ts new file mode 100644 index 000000000..396f581e8 --- /dev/null +++ b/apps/web-ele/src/store/tagsView.ts @@ -0,0 +1,202 @@ +import type { RouteLocationNormalizedLoaded } from 'vue-router'; + +import { useUserStore } from '@vben/stores'; + +import { defineStore } from 'pinia'; + +import { router } from '#/router'; +import { findIndex } from '#/utils'; +import { getRawRoute } from '#/utils/routerHelper'; + +import { store } from './index'; + +const userStore = useUserStore(); + +export interface TagsViewState { + visitedViews: RouteLocationNormalizedLoaded[]; + cachedViews: Set; + selectedTag?: RouteLocationNormalizedLoaded; +} + +export const useTagsViewStore = defineStore('tagsView', { + state: (): TagsViewState => ({ + visitedViews: [], + cachedViews: new Set(), + selectedTag: undefined, + }), + getters: { + getVisitedViews(): RouteLocationNormalizedLoaded[] { + return this.visitedViews; + }, + getCachedViews(): string[] { + return [...this.cachedViews]; + }, + getSelectedTag(): RouteLocationNormalizedLoaded | undefined { + return this.selectedTag; + }, + }, + actions: { + // 新增缓存和tag + addView(view: RouteLocationNormalizedLoaded): void { + this.addVisitedView(view); + this.addCachedView(); + }, + // 新增tag + addVisitedView(view: RouteLocationNormalizedLoaded) { + if (this.visitedViews.some((v) => v.fullPath === view.fullPath)) return; + if (view.meta?.noTagsView) return; + const visitedView = Object.assign({}, view, { + title: view.meta?.title || 'no-name', + }); + + if (visitedView.meta) { + const titleSuffixList: string[] = []; + this.visitedViews.forEach((v) => { + if ( + v.path === visitedView.path && + v.meta?.title === visitedView.meta?.title + ) { + titleSuffixList.push((v.meta?.titleSuffix as string) || '1'); + } + }); + if (titleSuffixList.length > 0) { + let titleSuffix = 1; + while (titleSuffixList.includes(`${titleSuffix}`)) { + titleSuffix += 1; + } + visitedView.meta.titleSuffix = + titleSuffix === 1 ? undefined : `${titleSuffix}`; + } + } + + this.visitedViews.push(visitedView); + }, + // 新增缓存 + addCachedView() { + const cacheMap: Set = new Set(); + for (const v of this.visitedViews) { + const item = getRawRoute(v); + const needCache = !item.meta?.noCache; + if (!needCache) { + continue; + } + const name = item.name as string; + cacheMap.add(name); + } + if ( + [...this.cachedViews].sort().toString() === + [...cacheMap].sort().toString() + ) + return; + this.cachedViews = cacheMap; + }, + // 删除某个 + delView(view: RouteLocationNormalizedLoaded) { + this.delVisitedView(view); + this.delCachedView(); + }, + // 删除tag + delVisitedView(view: RouteLocationNormalizedLoaded) { + for (const [i, v] of this.visitedViews.entries()) { + if (v.fullPath === view.fullPath) { + this.visitedViews.splice(i, 1); + break; + } + } + }, + // 删除缓存 + delCachedView() { + const route = router.currentRoute.value; + const index = findIndex( + this.getCachedViews, + (v) => v === route.name, + ); + for (const v of this.visitedViews) { + if (v.name === route.name) { + return; + } + } + if (index > -1) { + this.cachedViews.delete( + this.getCachedViews[index] as unknown as string, + ); + } + }, + // 删除所有缓存和tag + delAllViews() { + this.delAllVisitedViews(); + this.delCachedView(); + }, + // 删除所有tag + delAllVisitedViews() { + // const userStore = useUserStoreWithOut(); + + // const affixTags = this.visitedViews.filter((tag) => tag.meta.affix) + this.visitedViews = userStore.userInfo + ? this.visitedViews.filter((tag) => tag?.meta?.affix) + : []; + }, + // 删除其他 + delOthersViews(view: RouteLocationNormalizedLoaded) { + this.delOthersVisitedViews(view); + this.addCachedView(); + }, + // 删除其他tag + delOthersVisitedViews(view: RouteLocationNormalizedLoaded) { + this.visitedViews = this.visitedViews.filter((v) => { + return v?.meta?.affix || v.fullPath === view.fullPath; + }); + }, + // 删除左侧 + delLeftViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.fullPath === view.fullPath || i > index; + }); + this.addCachedView(); + } + }, + // 删除右侧 + delRightViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.fullPath === view.fullPath || i < index; + }); + this.addCachedView(); + } + }, + updateVisitedView(view: RouteLocationNormalizedLoaded) { + for (let v of this.visitedViews) { + if (v.fullPath === view.fullPath) { + v = Object.assign(v, view); + break; + } + } + }, + // 设置当前选中的 tag + setSelectedTag(tag: RouteLocationNormalizedLoaded) { + this.selectedTag = tag; + }, + setTitle(title: string, path?: string) { + for (const v of this.visitedViews) { + if (v.path === (path ?? this.selectedTag?.path)) { + v.meta.title = title; + break; + } + } + }, + }, + persist: false, +}); + +export const useTagsViewStoreWithOut = () => { + return useTagsViewStore(store); +}; diff --git a/apps/web-ele/src/utils/index.ts b/apps/web-ele/src/utils/index.ts index 3a066fd4a..e64084b6c 100644 --- a/apps/web-ele/src/utils/index.ts +++ b/apps/web-ele/src/utils/index.ts @@ -1,2 +1,30 @@ +import type { Recordable } from '@vben/types'; + export * from './rangePickerProps'; export * from './routerHelper'; + +/** + * 查找数组对象的某个下标 + * @param {Array} ary 查找的数组 + * @param {Function} fn 判断的方法 + */ +type Fn = (item: T, index: number, array: Array) => boolean; + +export const findIndex = >( + ary: Array, + fn: Fn, +): number => { + if (ary.findIndex) { + return ary.findIndex((item, index, array) => fn(item, index, array)); + } + let index = -1; + ary.some((item: T, i: number, ary: Array) => { + const ret: boolean = fn(item, i, ary); + if (ret) { + index = i; + return true; + } + return false; + }); + return index; +}; diff --git a/apps/web-ele/src/utils/routerHelper.ts b/apps/web-ele/src/utils/routerHelper.ts index 477a640ce..31164d7ba 100644 --- a/apps/web-ele/src/utils/routerHelper.ts +++ b/apps/web-ele/src/utils/routerHelper.ts @@ -1,3 +1,8 @@ +import type { + RouteLocationNormalized, + RouteRecordNormalized, +} from 'vue-router'; + import { defineAsyncComponent } from 'vue'; const modules = import.meta.glob('../views/**/*.{vue,tsx}'); @@ -13,3 +18,20 @@ export function registerComponent(componentPath: string) { } } } + +export const getRawRoute = ( + route: RouteLocationNormalized, +): RouteLocationNormalized => { + if (!route) return route; + const { matched, ...opt } = route; + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path, + })) + : undefined) as RouteRecordNormalized[], + }; +}; diff --git a/apps/web-ele/src/utils/tree.ts b/apps/web-ele/src/utils/tree.ts new file mode 100644 index 000000000..172ed2ba1 --- /dev/null +++ b/apps/web-ele/src/utils/tree.ts @@ -0,0 +1,442 @@ +interface TreeHelperConfig { + id: string; + children: string; + pid: string; +} + +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid', +}; +export const defaultProps = { + children: 'children', + label: 'name', + value: 'id', + isLeaf: 'leaf', + emitPath: false, // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 +}; + +const getConfig = (config: Partial) => + Object.assign({}, DEFAULT_CONFIG, config); + +// tree from list +export const listToTree = ( + list: any[], + config: Partial = {}, +): T[] => { + const conf = getConfig(config) as TreeHelperConfig; + const nodeMap = new Map(); + const result: T[] = []; + const { id, children, pid } = conf; + + for (const node of list) { + node[children] = node[children] || []; + nodeMap.set(node[id], node); + } + for (const node of list) { + const parent = nodeMap.get(node[pid]); + (parent ? parent.children : result).push(node); + } + return result; +}; + +export const treeToList = ( + tree: any, + config: Partial = {}, +): T => { + config = getConfig(config); + const { children } = config; + const result: any = [...tree]; + for (let i = 0; i < result.length; i++) { + const childNodes = result[i][children]; + if (!childNodes) continue; + result.splice(i + 1, 0, ...childNodes); + } + return result; +}; + +export const findNode = ( + tree: any, + func: Fn, + config: Partial = {}, +): null | T => { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + for (const node of list) { + if (func(node)) return node; + const childNodes = node[children]; + if (childNodes) { + list.push(...childNodes); + } + } + return null; +}; + +export const findNodeAll = ( + tree: any, + func: Fn, + config: Partial = {}, +): T[] => { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + const result: T[] = []; + for (const node of list) { + func(node) && result.push(node); + const childNodes = node[children]; + if (childNodes) { + list.push(...childNodes); + } + } + return result; +}; + +export const findPath = ( + tree: any, + func: Fn, + config: Partial = {}, +): null | T | T[] => { + config = getConfig(config); + const path: T[] = []; + const list = [...tree]; + const visitedSet = new Set(); + const { children } = config; + while (list.length > 0) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + const childNodes = node[children]; + if (childNodes) { + list.unshift(...childNodes); + } + path.push(node); + if (func(node)) { + return path; + } + } + } + return null; +}; + +export const findPathAll = ( + tree: any, + func: Fn, + config: Partial = {}, +) => { + config = getConfig(config); + const path: any[] = []; + const list = [...tree]; + const result: any[] = []; + const visitedSet = new Set(); + const { children } = config; + while (list.length > 0) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + const childNodes = node[children]; + if (childNodes) { + list.unshift(...childNodes); + } + path.push(node); + func(node) && result.push([...path]); + } + } + return result; +}; + +export const filter = ( + tree: T[], + func: (n: T) => boolean, + config: Partial = {}, +): T[] => { + config = getConfig(config); + const children = config.children as string; + + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + node[children] = node[children] && listFilter(node[children]); + return func(node) || node[children]?.length > 0; + }); + } + + return listFilter(tree); +}; + +export const forEach = ( + tree: T[], + func: (n: T) => any, + config: Partial = {}, +): void => { + config = getConfig(config); + const list: any[] = [...tree]; + const { children } = config; + for (let i = 0; i < list.length; i++) { + // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return; + } + children && + list[i][children] && + list.splice(i + 1, 0, ...list[i][children]); + } +}; + +/** + * @description: Extract tree specified structure + */ +export const treeMap = ( + treeData: T[], + opt: { children?: string; conversion: Fn }, +): T[] => { + return treeData.map((item) => treeMapEach(item, opt)); +}; + +/** + * @description: Extract tree specified structure + */ +export const treeMapEach = ( + data: any, + { children = 'children', conversion }: { children?: string; conversion: Fn }, +) => { + const haveChildren = + Array.isArray(data[children]) && data[children].length > 0; + const conversionData = conversion(data) || {}; + return haveChildren + ? { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion, + }), + ), + } + : { + ...conversionData, + }; +}; + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element; + if (element.children) { + eachTree(element.children, callBack, newNode); + } + }); +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = ( + data: any[], + id?: string, + parentId?: string, + children?: string, +) => { + if (!Array.isArray(data)) { + console.warn('data must be an array'); + return []; + } + const config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children', + }; + + const childrenListMap = {}; + const nodeIds = {}; + const tree: any[] = []; + + for (const d of data) { + const parentId = d[config.parentId]; + if ( + childrenListMap[parentId] === null || + childrenListMap[parentId] === undefined + ) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (const d of data) { + const parentId = d[config.parentId]; + if (nodeIds[parentId] === null || nodeIds[parentId] === undefined) { + tree.push(d); + } + } + + for (const t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (const c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + + return tree; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +// @ts-ignore: 遗留函数,保持原有逻辑不变 +export const handleTree2 = (data, id, parentId, children, rootId) => { + id = id || 'id'; + parentId = parentId || 'parentId'; + // children = children || 'children' + rootId = + rootId || + Math.min( + ...data.map((item) => { + return item[parentId]; + }), + ) || + 0; + // 对源数据深度克隆 + const cloneData = structuredClone(data); + // 循环所有项 + const treeData = cloneData.filter((father) => { + const branchArr = cloneData.filter((child) => { + // 返回每一项的子级数组 + return father[id] === child[parentId]; + }); + branchArr.length > 0 ? (father.children = branchArr) : ''; + // 返回第一层 + return father[parentId] === rootId; + }); + return treeData === '' ? data : treeData; +}; + +/** + * 校验选中的节点,是否为指定 level + * + * @param tree 要操作的树结构数据 + * @param nodeId 需要判断在什么层级的数据 + * @param level 检查的级别, 默认检查到二级 + * @return true 是;false 否 + */ +export const checkSelectedNode = ( + tree: any[], + nodeId: any, + level = 2, +): boolean => { + if (tree === undefined || !Array.isArray(tree) || tree.length === 0) { + console.warn('tree must be an array'); + return false; + } + + // 校验是否是一级节点 + if (tree.some((item) => item.id === nodeId)) { + return false; + } + + // 递归计数 + let count = 1; + + // 深层次校验 + function performAThoroughValidation(arr: any[]): boolean { + count += 1; + for (const item of arr) { + if (item.id === nodeId) { + return true; + } else if ( + item.children !== undefined && + item.children.length > 0 && + performAThoroughValidation(item.children) + ) { + return true; + } + } + return false; + } + + for (const item of tree) { + count = 1; + if ( + performAThoroughValidation(item.children) && // 找到后对比是否是期望的层级 + count >= level + ) { + return true; + } + } + + return false; +}; + +/** + * 获取节点的完整结构 + * @param tree 树数据 + * @param nodeId 节点 id + */ +export const treeToString = (tree: any[], nodeId) => { + if (tree === undefined || !Array.isArray(tree) || tree.length === 0) { + console.warn('tree must be an array'); + return ''; + } + // 校验是否是一级节点 + const node = tree.find((item) => item.id === nodeId); + if (node !== undefined) { + return node.name; + } + let str = ''; + + function performAThoroughValidation(arr) { + if (arr === undefined || !Array.isArray(arr) || arr.length === 0) { + return false; + } + for (const item of arr) { + if (item.id === nodeId) { + str += ` / ${item.name}`; + return true; + } else if (item.children !== undefined && item.children.length > 0) { + str += ` / ${item.name}`; + if (performAThoroughValidation(item.children)) { + return true; + } + } + } + return false; + } + + for (const item of tree) { + str = `${item.name}`; + if (performAThoroughValidation(item.children)) { + break; + } + } + return str; +}; diff --git a/apps/web-ele/src/views/mp/components/wx-account-select/index.ts b/apps/web-ele/src/views/mp/components/wx-account-select/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-account-select/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-account-select/main.vue b/apps/web-ele/src/views/mp/components/wx-account-select/main.vue new file mode 100644 index 000000000..e2c1b552e --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-account-select/main.vue @@ -0,0 +1,83 @@ + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-location/index.ts b/apps/web-ele/src/views/mp/components/wx-location/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-location/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-location/main.vue b/apps/web-ele/src/views/mp/components/wx-location/main.vue new file mode 100644 index 000000000..784b45553 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-location/main.vue @@ -0,0 +1,61 @@ + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-material-select/index.ts b/apps/web-ele/src/views/mp/components/wx-material-select/index.ts new file mode 100644 index 000000000..f4fcea0ba --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-material-select/index.ts @@ -0,0 +1,3 @@ +export { default } from './main.vue'; + +export { MaterialType, NewsType } from './types'; diff --git a/apps/web-ele/src/views/mp/components/wx-material-select/main.vue b/apps/web-ele/src/views/mp/components/wx-material-select/main.vue new file mode 100644 index 000000000..467636110 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-material-select/main.vue @@ -0,0 +1,295 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-material-select/types.ts b/apps/web-ele/src/views/mp/components/wx-material-select/types.ts new file mode 100644 index 000000000..3d44c5db7 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-material-select/types.ts @@ -0,0 +1,11 @@ +export enum NewsType { + Draft = '2', + Published = '1', +} + +export enum MaterialType { + Image = 'image', + News = 'news', + Video = 'video', + Voice = 'voice', +} diff --git a/apps/web-ele/src/views/mp/components/wx-msg/card.scss b/apps/web-ele/src/views/mp/components/wx-msg/card.scss new file mode 100644 index 000000000..58fde79fd --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/card.scss @@ -0,0 +1,116 @@ +.avue-card { + &__item { + box-sizing: border-box; + height: 200px; + margin-bottom: 16px; + font-size: 14px; + font-feature-settings: 'tnum'; + font-variant: tabular-nums; + line-height: 1.5; + color: rgb(0 0 0 / 65%); + cursor: pointer; + list-style: none; + background-color: #fff; + border: 1px solid #e8e8e8; + + &:hover { + border-color: rgb(0 0 0 / 9%); + box-shadow: 0 2px 8px rgb(0 0 0 / 9%); + } + + &--add { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + font-size: 16px; + color: rgb(0 0 0 / 45%); + background-color: #fff; + border: 1px dashed #000; + border-color: #d9d9d9; + border-radius: 2px; + + i { + margin-right: 10px; + } + + &:hover { + color: #40a9ff; + background-color: #fff; + border-color: #40a9ff; + } + } + } + + &__body { + display: flex; + padding: 24px; + } + + &__detail { + flex: 1; + } + + &__avatar { + width: 48px; + height: 48px; + margin-right: 12px; + overflow: hidden; + border-radius: 48px; + + img { + width: 100%; + height: 100%; + } + } + + &__title { + margin-bottom: 12px; + font-size: 16px; + color: rgb(0 0 0 / 85%); + + &:hover { + color: #1890ff; + } + } + + &__info { + display: -webkit-box; + height: 64px; + overflow: hidden; + -webkit-line-clamp: 3; + color: rgb(0 0 0 / 45%); + -webkit-box-orient: vertical; + } + + &__menu { + display: flex; + justify-content: space-around; + height: 50px; + line-height: 50px; + color: rgb(0 0 0 / 45%); + text-align: center; + background: #f7f9fa; + + &:hover { + color: #1890ff; + } + } +} + +/** joolun 额外加的 */ +.avue-comment__main { + flex: unset !important; + margin: 0 8px !important; + border-radius: 5px !important; +} + +.avue-comment__header { + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.avue-comment__body { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} diff --git a/apps/web-ele/src/views/mp/components/wx-msg/comment.scss b/apps/web-ele/src/views/mp/components/wx-msg/comment.scss new file mode 100644 index 000000000..219e2e5d4 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/comment.scss @@ -0,0 +1,109 @@ +/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */ +.avue-comment { + display: flex; + align-items: flex-start; + margin-bottom: 30px; + + &--reverse { + flex-direction: row-reverse; + + .avue-comment__main { + &::before, + &::after { + right: -8px; + left: auto; + border-width: 8px 0 8px 8px; + } + + &::before { + border-left-color: #dedede; + } + + &::after { + margin-right: 1px; + margin-left: auto; + border-left-color: #f8f8f8; + } + } + } + + &__avatar { + box-sizing: border-box; + width: 48px; + height: 48px; + vertical-align: middle; + border: 1px solid transparent; + border-radius: 50%; + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 5px 15px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + } + + &__author { + font-size: 14px; + font-weight: 700; + color: #999; + } + + &__main { + position: relative; + flex: 1; + margin: 0 20px; + border: 1px solid #dedede; + border-radius: 2px; + + &::before, + &::after { + position: absolute; + top: 10px; + right: 100%; + left: -8px; + display: block; + width: 0; + height: 0; + pointer-events: none; + content: ' '; + border-color: transparent; + border-style: solid solid outset; + border-width: 8px 8px 8px 0; + } + + &::before { + z-index: 1; + border-right-color: #dedede; + } + + &::after { + z-index: 2; + margin-left: 1px; + border-right-color: #f8f8f8; + } + } + + &__body { + padding: 15px; + overflow: hidden; + font-family: + 'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei', + FreeSans, Arimo, 'Droid Sans', 'wenquanyi micro hei', 'Hiragino Sans GB', + 'Hiragino Sans GB W3', FontAwesome, sans-serif; + font-size: 14px; + color: #333; + background: #fff; + } + + blockquote { + padding: 1px 0 1px 15px; + margin: 0; + font-family: + Georgia, 'Times New Roman', Times, Kai, 'Kaiti SC', KaiTi, BiauKai, + FontAwesome, serif; + border-left: 4px solid #ddd; + } +} diff --git a/apps/web-ele/src/views/mp/components/wx-msg/components/Msg.vue b/apps/web-ele/src/views/mp/components/wx-msg/components/Msg.vue new file mode 100644 index 000000000..8daa0fd51 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/components/Msg.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/components/MsgEvent.vue b/apps/web-ele/src/views/mp/components/wx-msg/components/MsgEvent.vue new file mode 100644 index 000000000..70c193082 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/components/MsgEvent.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/components/MsgList.vue b/apps/web-ele/src/views/mp/components/wx-msg/components/MsgList.vue new file mode 100644 index 000000000..36b574d83 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/components/MsgList.vue @@ -0,0 +1,77 @@ + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/index.ts b/apps/web-ele/src/views/mp/components/wx-msg/index.ts new file mode 100644 index 000000000..048afc95f --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/index.ts @@ -0,0 +1,3 @@ +export { default } from './main.vue'; + +export { MsgType } from './types'; diff --git a/apps/web-ele/src/views/mp/components/wx-msg/main.vue b/apps/web-ele/src/views/mp/components/wx-msg/main.vue new file mode 100644 index 000000000..4e2bb6469 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/main.vue @@ -0,0 +1,210 @@ + + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/types.ts b/apps/web-ele/src/views/mp/components/wx-msg/types.ts new file mode 100644 index 000000000..e988954ad --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/types.ts @@ -0,0 +1,17 @@ +export enum MsgType { + Event = 'event', + Image = 'image', + Link = 'link', + Location = 'location', + Music = 'music', + News = 'news', + Text = 'text', + Video = 'video', + Voice = 'voice', +} + +export interface User { + nickname: string; + avatar: string; + accountId: number; +} diff --git a/apps/web-ele/src/views/mp/components/wx-music/index.ts b/apps/web-ele/src/views/mp/components/wx-music/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-music/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-music/main.vue b/apps/web-ele/src/views/mp/components/wx-music/main.vue new file mode 100644 index 000000000..e097e1dfc --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-music/main.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-news/index.ts b/apps/web-ele/src/views/mp/components/wx-news/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-news/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-news/main.vue b/apps/web-ele/src/views/mp/components/wx-news/main.vue new file mode 100644 index 000000000..f93893c92 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-news/main.vue @@ -0,0 +1,123 @@ + + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabImage.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabImage.vue new file mode 100644 index 000000000..073934088 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabImage.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabMusic.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabMusic.vue new file mode 100644 index 000000000..fe74ea779 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabMusic.vue @@ -0,0 +1,137 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabNews.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabNews.vue new file mode 100644 index 000000000..85ede8813 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabNews.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabText.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabText.vue new file mode 100644 index 000000000..a7e389db0 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabText.vue @@ -0,0 +1,29 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabVideo.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabVideo.vue new file mode 100644 index 000000000..3ddcb2cd9 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabVideo.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/TabVoice.vue b/apps/web-ele/src/views/mp/components/wx-reply/components/TabVoice.vue new file mode 100644 index 000000000..9467120c6 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/TabVoice.vue @@ -0,0 +1,174 @@ + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/components/types.ts b/apps/web-ele/src/views/mp/components/wx-reply/components/types.ts new file mode 100644 index 000000000..67571ef59 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/components/types.ts @@ -0,0 +1,58 @@ +import type { Ref } from 'vue'; + +import { unref } from 'vue'; + +enum ReplyType { + Image = 'image', + Music = 'music', + News = 'news', + Text = 'text', + Video = 'video', + Voice = 'voice', +} + +interface _Reply { + accountId: number; + type: ReplyType; + name?: null | string; + content?: null | string; + mediaId?: null | string; + url?: null | string; + title?: null | string; + description?: null | string; + thumbMediaId?: null | string; + thumbMediaUrl?: null | string; + musicUrl?: null | string; + hqMusicUrl?: null | string; + introduction?: null | string; + articles?: any[]; +} + +type Reply = _Reply; // Partial<_Reply> + +enum NewsType { + Draft = '2', + Published = '1', +} + +/** 利用旧的reply[accountId, type]初始化新的Reply */ +const createEmptyReply = (old: Ref | Reply): Reply => { + return { + accountId: unref(old).accountId, + type: unref(old).type, + name: null, + content: null, + mediaId: null, + url: null, + title: null, + description: null, + thumbMediaId: null, + thumbMediaUrl: null, + musicUrl: null, + hqMusicUrl: null, + introduction: null, + articles: [], + }; +}; + +export { createEmptyReply, NewsType, type Reply, ReplyType }; diff --git a/apps/web-ele/src/views/mp/components/wx-reply/index.ts b/apps/web-ele/src/views/mp/components/wx-reply/index.ts new file mode 100644 index 000000000..a3f675bb1 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/index.ts @@ -0,0 +1,8 @@ +export { + createEmptyReply, + NewsType, + type Reply, + ReplyType, +} from './components/types'; + +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-reply/main.vue b/apps/web-ele/src/views/mp/components/wx-reply/main.vue new file mode 100644 index 000000000..d3be2557a --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/main.vue @@ -0,0 +1,215 @@ + + + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-video-play/index.ts b/apps/web-ele/src/views/mp/components/wx-video-play/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-video-play/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-video-play/main.vue b/apps/web-ele/src/views/mp/components/wx-video-play/main.vue new file mode 100644 index 000000000..364b05890 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-video-play/main.vue @@ -0,0 +1,76 @@ + + + + diff --git a/apps/web-ele/src/views/mp/components/wx-voice-play/index.ts b/apps/web-ele/src/views/mp/components/wx-voice-play/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-voice-play/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-voice-play/main.vue b/apps/web-ele/src/views/mp/components/wx-voice-play/main.vue new file mode 100644 index 000000000..23b721380 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-voice-play/main.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/hooks/useUpload.ts b/apps/web-ele/src/views/mp/hooks/useUpload.ts new file mode 100644 index 000000000..05da55f58 --- /dev/null +++ b/apps/web-ele/src/views/mp/hooks/useUpload.ts @@ -0,0 +1,67 @@ +import type { UploadRawFile } from 'element-plus'; + +import { ElMessage } from 'element-plus'; + +const message = ElMessage; // 消息 + +enum UploadType { + Image = 'image', + Video = 'video', + Voice = 'voice', +} + +const useBeforeUpload = (type: UploadType, maxSizeMB: number) => { + const fn = (rawFile: UploadRawFile): boolean => { + let allowTypes: string[] = []; + let name = ''; + + switch (type) { + case UploadType.Image: { + allowTypes = [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/bmp', + 'image/jpg', + ]; + maxSizeMB = 2; + name = '图片'; + break; + } + case UploadType.Video: { + allowTypes = ['video/mp4']; + maxSizeMB = 10; + name = '视频'; + break; + } + case UploadType.Voice: { + allowTypes = [ + 'audio/mp3', + 'audio/mpeg', + 'audio/wma', + 'audio/wav', + 'audio/amr', + ]; + maxSizeMB = 2; + name = '语音'; + break; + } + } + // 格式不正确 + if (!allowTypes.includes(rawFile.type)) { + message.error(`上传${name}格式不对!`); + return false; + } + // 大小不正确 + if (rawFile.size / 1024 / 1024 > maxSizeMB) { + message.error(`上传${name}大小不能超过${maxSizeMB}M!`); + return false; + } + + return true; + }; + + return fn; +}; + +export { UploadType, useBeforeUpload }; diff --git a/apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png b/apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png new file mode 100644 index 0000000000000000000000000000000000000000..bb09591a7912730aab07287e0bb2bc4e3383abc5 GIT binary patch literal 34272 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 n1Y$yl2Y%)kp^j~lTlnWvqUJ7=Y>Ul zie~nzd{&J4&Iy++n!1UB_c`fi7Uepdtji{Puq?jK~K{wxS=clLl zGQY|1mF6 z@EdB+xwb-Iw}k<%z=FHyv?e(;=nn~6XKk#gFO@F})E4e1%gP*Y1feoR5?ZlGmC={W|=pt@yA?nY0DzBOSY%ET=&4 zUO<2BIH$`-fp=ebTKB3rjC~PUUna8bC7IAjRvO^nwV+9Auu3fa(m1Q7U)94t@jMZC zhlRj|dDf9928Xmry7#0RQSj#S`G+LPeLQD2k5i|0K^@?V1fjWI)f3vgrB6=3bI1QA z)8-0Wy$`C-P;M>BmWEbI`{0D2iS*wZ8>00+{N76hzAFgY~1G>A3AoPL>~m^D|ugE7zS*JuD()OGe1(!~TNMf=efoQl8m6*N3 zC*qsFjBfdPf8B~Keaxr}De2GtaI0J@ax{j?%4{y-F>RDnLpo`;x^W@FK82886~>sZ z;9+$7+SHRR9Kb>JY(mAJSCP4*SIcAq(`W>e^Ds zd|1C5q4vjUuG)3mYh*OHBk;?E#;7P$_;D!8yf(BBGEqAcln)-MRiVz5?VyeGJesTE zqnr6=9J%}6c5K=lp6hj0pYdH$Rv)u*xiYeR)%hfjESu#JPd=(iRZ<`Ed)AmFkZ1`n zdTyS?g8HTS-Ha8A;mN4?Q3Xrr&Ec;DV6R__y)mUE1H)JH@O^#UV{YpDb%iXiUESZP zwd)CcyN5IG6CrEvamlL;Wr(C(?>WsEn?jI7{n@CWk|o!+hrpt-USE^e$EQ zL%KEJu00gnKP5vOomW463ZPTSq4 z(k0?*oEoqI-j0_az+<7|js>8C4Lm1%`8>vG_V77BYLI%(tY_yrqAr>aw)!!?9Xv0w z247+-Z@V4TRM3DCPE+jOGJn}~qmKF%8fxC5d`l}r#$#M|k~ z0d2+cEQuZXk-VPAzSD#>u34F_nG$WZ|DTgp8@CHg>p130maMu&p%V*)3u3W?nX`I* z<@J%Q_#B{H1u{#CcAL|c=N>TG&d$Oe?#96%?H4<#%Ohxov)?~{b%EJ%vh!*4-f&q} zJ9CA8eR4cgRqg$9={Jt%t1!-MTUN>IO| zmWw>uHxCeRxYhl^$5%B48XIo&v2n1IWedKtyDF-T5lp$c4@G5ng(~SoRbWu7FY>7M zH=j~6XFLj9HHC#m;+;yGV7yrQ$&s!;dfIF%ajEe0Rg9S5uOlI&PAWdDsYKBa96MR9 zX9;N3*-11Q>IeF8Z}28o0@ZZUyq7g3_!jr+OeA;vTWuhypu9l)cY#vM(3;QLJ8meV z#`UNn6mh5xZRiZ`P%EV~u-=NOGadZ06&pO^)_SrX3ZP+K7lBj$7BMNjSaXhizx^+i zJ^_!6dGQ-As*@~tcQk!?vo|tGjk9xBJ~ayB2QMktsU(V>G0PNnn=9~f>0N!##d3Xa z6Jo`;_A!JAI!DUs`;1;zC~5b7n$o%?ixL_IsQ#sSmIzOk6eL_y^{RuI$m)=s4+-t6 ziJvPq5KN&8$(ftEIj&Fqe|rab!xs0BVNYiJGzDa-#69B=8b zF@&cK%Nby%$1m(e9>mVgb4f6L5oEjdZ!6Lh1Os~QsHX#HUH@SD<(l6uXW`geA9s27_nnk%|c~7e5|(SK<0%*q2U!g z+^6UM*iecDN^)5~VRYM-t-3djXLdvL_N%2az<98+TYvuV`b_*8Ee~ZsVR?7Soum%O zR+^bSd4(t^YB!lxc^5#!Eehq)Y9|M=_i;}=s-(_o>{{0U@(JU#VY%{h<%(P3m7()G z7=32@^KL?x=$RHjYd@V&`S<$MBr1OM1rb8sLj-v}owyA?Lqu14L!8O#z+jQx)V!T2{-*;X7LWt%X94YBs?vgHVsQ^B zE>m7ph7PI-p}!;!YQmZ?3MZ$ICS=WKxIZ0jOX`5c7JP>>a#n-YM_v>MelXg2|yX3->nZW8}-hL^X+$EgtC@@y%!Q zRc+;v?!{V*$K9cW9hZJ;`g-Z>*|Rvu{%9ZaH!3VFBdYjBagkU$C-mF7YgT3Q3(?>; zzw0N2`;sx#FFa$MJXPg|^q2V_2d|Y1iz_`#C`em_O$GMYTj$F4DzruzW-M zPzVP6Wm#G=_c%N<7QNMc76|#u=!U9etOP7Bln&8;P_3@Lt+QbC6Sp;8Kwl~Oy?$Y2 z(9iD3m0#KflaUXO_X>%H+UwE|LWd(+SQ2H$9TDH$yoEd(jZ|x!c%3aq8E80|l0?DH zSjP)Fd3j+m3;0-|-nEFSnfbY{tEF5Q7XAB=oU22hIYj3zgTxVyxz-(b-H|nAYtPlP z5G@tE)z9y)AZS}%)Yd&ZPEGP&P7>kRu;P8B;ykBov0RFcce!K(W|r{2o(XR*SSyt@ zcBiy)xPm0%n%E(4=I1HKNosuWoim$=2IoDNN3|%5vb-a{55fm$N;K`xh{Mlt0hHNg zzZx!44`)Y1w?c)52jvSiD^u3Z7k!g8>wXV^7Q!RPQcAzhBt(s`D-Z2!ZL7qP1n||Jo{?oB9Y_2=dj31slJ2c zf!kvl&$#ctz;toleza_&RYRJZ0q(h-@C$8Wh{$2sg;9S3mE`5{2zRKrr3aO3}Fn zo;8q13l~pJ4aL7YEAZ$=v>9dMCyC-KglT76zuDqdOr_7WwkDH?-|bZqp#nW3y(E1nsCnKd;6Hs z6W848;;8K@8Q<&eds>MoYfnNc1wXW(SlsB?ZJ z7{U%iT;VEJeGk>kO|Kk(!qtBLL*%3mvxTx*_x4HEl&URh(6PpHK?@u0EBfFU9rr~f=@vqXCtw!fYUOT^D z?d+s{kCWbmaGTsPP8=l`r6SJ*Zdl5@t6qW!8i+n%okkvH3qRzfxqXt+gT?ki+(T2P zfa{R$z7#jT=D&o?!>lSgjq84`6@;(+Y9pzygcxeiNO)V=Hy2u-BLW!J|{sREh zbddDB&nHB#*%xxbb9lZQYAAtYwfh-S`CA&WnH>VNUMD@2*FO5w2Je3G=1qE0W0?+* z4*eOf_%Wit4Ls0K59U0XEZnxlU2XHaB0wM4esrE+^*EwKB=VUK>^P&cyWrOz`mL0b zWmlI}Af1-!F|oPo^$&MNG>%1=7yW3Swd)|U$*OMMEBo+NE zT-Qv@f|9%v+fu@?M7n=jq@t`EiG`IcOLTs6x9Jm;0#@|p5eCu?Sx}>Zku-r}d!LS5 zq>0@X`R9tQKH3uss<+F%5Kv*+gxTd->)HMh@$|KoD{k|rD#-<6iw=BpcTy!8ml`;W$ ziQ}2C;X<|+X6GdCj*Ow#Kk31V9PNes-d8}*DyZbg?-R54_N^z@uAUR7jTMTRNY=lI z$SWPpUSo746x1Y11vcQC!Zm#>q62}U5PN`H?TG4}XfwP7=IgGk^$qF0qo4LQy`Mbm zpTU<<6(#bMB9xKi0!>`fz>L6`9k_DgNSzb$N`3>e)V3y*a{o=>_TABy(iIEr$Ps_V zu+gr%zwvg>XV_Q8xAuk*pNN)dvi+#&#@#t{uENe{4~>LzMS{|9w&;NjP~?dNPGd(C z+27}ME_rT8^(WnZ&1Qtxu*Ru*yK2x+u|Iw6_`iVoAa_g>PTrV>NeL-h@{i?B^_5yZ zrpKj0COwC5Av7zNfdNI6?#rsz$Hdu|uEZaDK)4v=WEGee;Pul+R+RwJ*%KU4rYRJ3 zJ6qJ$LZ7dgbNa4g`A6g!yfoM+3SfDT18?#R=A0NcY&(1uH6$&NI)g_sF99;*RSz-n z`z`UgPjfBVrs{prwe>e(SI2t)&`|*HX<|XkWaP5_@fK){8aVIqP`E#R9C@8H7Q%-} zgNTTYWV9h~SHV!*y2(%OWysDTB2j2X2-2S6>tihzr2gG&2YJSMd0PHv{5}SOXpfn4 zk}?F-Q85SmxF4A}Hf^k{HsZCouZ}5PJW1Z!O?QYlZoxZ!-VYqN6k>cvzOL&4x;Qr- zh|BpTlg_M?W?E&+AfoEE^`d_aJ~NmHui+qNIF;)hqb1*v189Z9BRutAJp95vsoE~G zOES)76sZx}ScB%@6cw@_O{{NOAv6O%(Sk+s`VQ`Zp8r0=uR*CNxBrZHMD{Xle(+`q zt=%cz+~`;>7e=pu#w9LatpXZPz4x6uF#|oGpS7P!7F5;FdVxXecVsFN`uoc-wZ98+ zQWGzg5{qmKw!7$bC_{gJGR$>%3!M{NwFJMeVi@l;E$HxeN$@>;K$8vB3KCmu_&wT; z(;uPu2FNnUh4e0)_r|fG#2nY3BAbCYI`v;EjWPoRc+$itAZ=__-5WiD5vZjx(P{ae z#>siMmm@%r&+!0gEzHZcnUE&*^b^AvFcta`lgp@dG{tl1pFY!Q6OF7^Bfby0QH(}koQl8J z2MpO{b3<7pzP}f}B5!%5$TyRF4L>ITx}hl(%ETeeU5%O-{i*fkr!j+DrHs_W3u_-M zC^f^=uZLqBN_VbqiXtekuZB(fEBw3vR5#5K+K}-Vndp4^u}yYOG6vXn+y~!_paA|? z=L(9t;ZFLbHe^Plq(UUO6L%bRSFTiJPFp=p*IvADCdc}Q`RnYdPJtJ-jTb?F@s;0h z##>TgWz!oEp1wcYO3{IN5~TP1v`}ZnEc6hvP^96Qe)g}I4+SFTO@2nA9uoTeDmhj> z{nk>N?pg0RxQ9#f^yB_Sn*2g6&w2!ZCE=B9>f~=viFdg8UPrjEg{rp$UT>E5j}PWV zDYF;I%IIkoR0xiy9q2C+jZcMAQf=2z4Jx&Wl7tjgKTvbiVLdPmk~~(5OZB2ezyv>uXX=m#g5NvleM+`v zm)}#i$4Xs}`Mr|(_pFQ9>l}OE=>Ea+#XTLk345$dZQZ&^F%9^Y^EtNSYlnX_I(^Xl zq92l0c7m<(V*9d8jb7-TG;|ZrTo(a$49Wav|2*HDC4MZs4`tCKEHw+J7S%R5rL}D5Tjf!aUd9JZj*zvv|fZFub90IozMl6>|hw&S%i8g~(*9HEpmyo#XVLx=0 zJR0!mh$p#D7iLjlADQ*AE64?rjjZG!k$~j#;O3Ch?)zAMqxa3K#peZw&y~WlCHQ{s zumTgGy``5@5*^{>HKive!Ik@b*SgDU%n=?g^kd5A#eJS+cV=m-T&2{J0T`$@kqV<$ zJN$y5413_cWqJZ=mgc|xwMBG`@aX}!4Q%*6cj1iz_lbvvanu#np#v|fj+WeTe^KCO zY<}hW!b(ZP_%eNBd0zMROot?`dDrp%;x}ohp)v)j>^$Z3x2njt483d_dTtJaPbAQ% zLsfCOziu>_&g5AyEJRSJs)bSD6kaV0)U?N}Gsa%l)>!1vtL90TD|#Vak0G zs$jV=sksF9WADvasumSFu5nCkP!C`r;x3-BI_ENa?KbkB2a_slTibBbN`6Y{tK>-$ zuCdU%S-xaa_|=r(S&1{kPye@En^0BV#Wd+kDMFzA&LbTbOJa{&`{5y_3b zEgi}UXeQcJ=FBo|-u~S_aGYgfoUNWT6AJ63X3=vTlkO8ae>zzfbUgepfY7sj z_pE3()&G-8Qa#ZOZ{C2l;=}NT2k+sJH__UCTnvg!V+s+BmY!=yaP;|I|H`MX$x|*2 zWHVM-nFop=(^>a7b6AAF`ZGDKf05G)oE?N%IT7ht-+8A;;+x!yY&${d<6%S$^x|4B zCQ+;eap{36LA#yf6z;S@dZ3npSCfpChEAcJ5qWoPTjs|2nYz$2-`%gcjMR`56>9sBdupHpS*$%QwtsS8Y_S>ZEM_ zO$??lEzyjs9qXxyB}sx!v#inX-1T1scm|sI8gnLJoY9c({_-F-VS`bDEYztLf?PJU z)(U`b_UBbo6MJ(2Fh;x2bAxe6qaqd0^lJ|8)S#$oVYG((g#3#JP`8n==zgegb#5S+ zzlLT7YMTN^Q(t9>6DWa$VBc@&k~YeJqFzo3qL&!GhQU){DO1Xj|0#c49*gI{jiKy$NQB3{mAXxN~H^;=bSE=5W2 z_I4^Ma5E?8K&ikuU=uY1!PLR?^_5Y*agxi@!90Gd^2pI7$C_y{UNO2hHP7~~JWM#! z5~yXtTWQdIC~6zjazobW1AGq(CRzGuD%zz&HA+YDH6@C8jVmwvm8_%^S>^kT{-PcU z+uYa>oz%oXg&Cgj5bI^CX%UX&Mjae@gB6a0db+u$I}1vY7cL zQx6n6nH$-1Dax^b{-b++UZ!HxIU_CO?8e}Mlc^I%RwEg5Q{U^_`T2;!3|oui4{}ab zA5#NEC)8!L*vQNTHehN1UFHL)|=VA z%6jWWq)7SeD{3m%)0tzrE;XBy%jY_w}P&;8?DBxl#SXn1$pw-6StNmf9Nl>E-~w^sSh>Bp{;M#rzBH z=U3h5SG0oO%rjdWQ*~f>M%^?u z^}73qoB!z8f2;40O`~-l_*WwZeoxwLR)A8>*-0&(xmVogL*r^NQXwxJjrzhAt$fm0 zotBrYfb>U^Jswx@8(T)GSw29!u}>`EF26ihoGjGO@3vxpsb-2=(Z|ohT|7@_7Dfdh zB9QR;hchrk5H4Eoe7Q#1@zGxYSzZC%2%+cMD;)}cz^=r&TbBdt>}VoCi(l`3V=QDE z6Fe&VRrUjr@d-d#N!tAp(_J3LC$9T1%@$ttEa4S{q9Xk~n>|blKxWWOHz8k8x4s@f zbE(ajMS1ItK-XWae*|kcX)=Gi?mp?$SK9mT+C7Y^vRXz+F88P-)jAwjhS-VKM0 z(zD6;<;XW}kR%0PB;0)Pj2YS7_JOg7>=K1bmo&`X+T%Ba=#Jz{esnVrVxv6j>D&Ac z3bc=XQHa=^SH-9tygsA?8F}3%3rcX=)(%P`<}qeaR+-u0vh9u`Ic#Rq*s2B!U92-L1POs;q3xx~#ZPtGL<9?^bvx`RUu|7;+r_X$H65mXzeSHW zl@MOH@_$Ec2ve6N9XnpEHG`ECMN7W0Qg65>(N;8aJyJ~@vh4prswvs z_;>M`>veGu`BIe2qt@P4Lvt~XWkPBx^7R9(+vmCKAveKPX68@&2@#pAOIk|~r>y``m3vp?8ToS5&gH5nQB|M07G*)WIU*zfF}uh2fOm@*jK`uM`85>&0&UY# zkD<+*mbpyn31~UMk(C_o<@X5wAHzp5r5Ex(xqePh37GiX`29jz>uwrk z&}AG@rL0a<`pApp_|LEPa3K5jt{BQ~cvB>mf=;BbFe|PxitMYc#KD znas{sx3uLx?b^8*T1HiwQZv$+wROrXIS(;M@!Y%#$Ij-Q-Ty*qoE;VXO!}gJ z>D#xDcD%w5SRiwG_(3%yJo`CvcXTJCc08eW3?3i#THuTOs-1Qu)eN}9H2ielkomEM z-c2YHJ+J;t_^Y1|+wx5`(=d~Afbi1M{t0p0D?PznU^X`ChxO1fhnPjJg*$$IOuXCMXj#cOfgK;XTUt&nY@HN5JjTuPDR8!;i3A@RR-P zfS#?|Ddnh&YdBfbt4SAf$dbxhmW|YwQ%$&^e%q<5N}6BM$>ccQDbO7sM@I7;$>;X# zS;Q{8LH}#{ewNLYYa#ogv2+KRiOhYDkQbo`d0dptGlIFA5ReF4j+mK_{KWDJo=A1o z9&ia5gMLMh7CieP*?bn^>E>i00x{&wqjfsY&ALt{iIhZsJ#9InqvF}XeJkKOZ|E`1 z0`V<1IWE;i9Uz26M;NC#az&mJiTJ*rc&;hUNFc>1S0hVmLQ3QI_t~Q7R!q)nSYnXo z!0#s+ZPl;B+B-&tmX*(2hG$Bq^U?nN+xcW_NdfS_+xq$MuPhf?jNgyWTXWeh*HN^w z%v9QmJ$$g4n-N>oXq`b|O?G+9QtA9r3ZeWAA}>;HIkB9&M8W zqg?pL(nOZCw%2$rz4h_l;AK^+xmzqK5+{yR$besCB;XHoiJfsy7eOt~5CQ0xM>_8r z_s4dTZ_(s-bq5nHL$aF zbXp(6-gNQ7@09pBi1jE~wojz4_0ZAOxqu#PJ-f<;6i&t%W$MpXh8GQ;d!*3y@hAI< zVjL&8TPa>)CO@(^n~1${ajfBU+SOUa+fHo#z&te&)n=mKyWrY=&DgEqT^I1^*t}Rt zcK3pR#rlu!4ITOj>S63MGBis2pY;n}c9#Mj(wW(%7ikDbG4U^nj0xGTv|}n&b+t>o z+Z%HY9%p2abU9pWzHFY+c{7}4;L*|CmyH(T^S)N-ddz)(S{xfLr_DiiC24~bjJ5s$ zT)ncWmw&*;KYI7=v~J0>1!u=x)Oq}A6Gv~Nyx@wcd)N;@wvXOfd-D2t(zIJvXNt%# z5#s2&`WkRnM{#>9P%%EM=+eN))+MT_o#RG{?GiCPH}tqP_x~0Sxt#nN<;{Xs&TtnD zT%E0f00--6T=$~iUp8q5>(n4?&gWec0`$=d;bf2*y-N(7i=`_d83W&CDaeDcj2Uobd{1nE)9tcQng$hK3M@*Bm6xrX*9B`NpXX?})jB$^rx)i zic*J9svku?PC557T#}EQwg{a}RA}^_{%bf0R?K!=JCN4aq1u}Cny=MuS@!7*QSEaJ+lEzlebD`V zET3PuoGuSIr|s%g47LV&$MT_D-5W}grodq-B z-Tw2)XN#lkwu|#R(jAIL85FxQ6p!;H8M|KA?-fu$C@%ChL7Fe-l7R6YC2hG^Z~SaZ z9oWGt_Y?=|LmZ;qn~jC=!pr&pcbgqSc(^FbMd{n|VOD!vt7v1)X@7#lY^AB6y=57D zfR%A8lEBU9r2b4%JW0xFoZ-(K0;;Y(QP0Yn>;~C>!cEwd_$L;v%f?XBeCTq6l}Tg0 zwxNW<_j%6fTQuyf1H!=OcT=W+U~cY%R0ZNc)CzhE%WUOn#|9DBR}D(TQ|0NB61{H| zMd&Zn8SKNUkMKE`>h;yQ+P8}?|I%k!C4Zgon98z-Jq&BT`!hEod0FJZf41WzR3|~9 z$5z#XY)dG-W`kb0)j|Ehg(yYj^2Eja!$&^g2hv16UPlb`pwz@A=Q=jx4(H-rd`Dsa z*hCHYgOIWEIr`>4(hX+_1_w>Dx#jpz>R#WG6-*9kAD$`c5Q63}eft&f6-S6Z+x<4T zm@7CD2DX0VczB7sX0Dq9J~z6r*_jLF;CVYnF~szi(yXu5=$dG8jI7*Bszsl02Mh3( zt&YB0FQ?+zFkkJb`0KrU?Q8B6?tTE__*RYCP+Hd`zKfPz{Eo86K zV;F7Bm9Z%{!Qrkj(vN=9*oQ0|j5_*)jJW>H$@%z1G~hj|^{lbV^>BqP>mAj14k9!1 zkoGc%lqh;Yjw8xMy1>W(p_C|Iv~FXnlN0O1ApK_-hMV>-M(v3G$R(GN>-;cDJ2f%# z#c6f#meM)^Et^MHjQn9$E-cB01hlj^abe2}vK~+SufBS1ttNXwt-s#4t8GfO_UX?p z#(jU$IfaHM7Vi6ogz54w3d5G|3IgE@<77;1*vHJ56M4i~CAY2B+dB^?y?ylZLXsVs zfO7j5n;`9VPyNg)p5_S6j`~^oGi}oXW&>uQXAG<8g;#%IkK>8FWfzv-TPh%>jz8}h><#6SsqjrO z!4TtDbBb#XLjm0v1>7ed--GFePlIRsnCPx3s{~)p9;3ZN|%ENui)Psl#OC&qjX_`ssQ=oE-p!Wg%dE#G8*>I=J zr*hKNM+PdY>)AdV8cS!sUt*y6uOg+MBmh(xtYwce+)jec5PRA^yJeP=Q*Ob#s(V&q zX}9&0KJ6O2&uVO4j{>(B_C0UP^j#Bj$HYV#P z%I1x5oJvp=-N=r0V^b`qz|+yp@c13BW&iy1PT3oy%Nuc)S~^f(Wk})wxmD0%Y~#uq ze#q2)kYsanSX&TE5*?QX-0mFt)PN;7IkR_B&YX#ck*E4t`gRW$m0EQNI4Wtag=IL$ z6-VfHN0HZwzK(`1V7%3aNrT^STRC^Ko+9O^v0(HMdC#6$!UOC@pL>OaK4iUtoQiY7a_tx3b{iI3EO zeP5Q5`Me*$;f%L9TqIuiTX`HJd{5Ev_xBo$(TlzGn8*B0n7f}5q%jON7wYRi(q1~l zO4&+@(LStlMnHovVnsHTW`or1j+KtDHI(9YT9nZZ;jz-Qc7o6>y+;X1 z{TPzM=Zt;n?2$x$@3v-s^pR;{n;}}8X?9aY2RAufv{ECBbAbmIXVAV3^r-EPwDfN} zl9we(1V$e}&3!M&#@^)0?94(u=O<0biRWsNxdch6PXZ?UY5GK683@BPk)3Hz54OI% zX?b;p9qzZ!yZItPdx=O*pRFIz{8wR5jda}%aWs;rMl9W9GXh4|UbkSGGG+T6s~O*m zz6zNnNhGuLOK{F53#@saRTjzYx$)794LlSM?Qd8 zdKbrBBaoo8M1LRU$0(<1kJwj&i`{VdkN%B7m9w}$5pL=BCc-~s_yQUTCH7UwbE}Ia zHk~~;pmoFI_4)<-OZ_ZngvI?({8hhlRHzsIL*47sBOX)(rN%SKrq09hABPTsckR{B zLntX-pO$M)xlefqP+(_VvgQJ(VQ;)Fiz<1?T4p+ufJ@)=9hRVBek#k6+r5JQo>tH+ z1JRBWz%(rPLh<3%WR;aqym*~Uxk-jw35X3^*RHT$2PQ$LHE73MypkUEVpRx4;}Y{h X!lw4y|2_Uwf${mHs$8XvQQ-do;aw2N literal 0 HcmV?d00001 diff --git a/apps/web-ele/src/views/mp/menu/components/menuOptions.ts b/apps/web-ele/src/views/mp/menu/components/menuOptions.ts new file mode 100644 index 000000000..9ba818fc4 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/components/menuOptions.ts @@ -0,0 +1,42 @@ +export default [ + { + value: 'view', + label: '跳转网页', + }, + { + value: 'miniprogram', + label: '跳转小程序', + }, + { + value: 'click', + label: '点击回复', + }, + { + value: 'article_view_limited', + label: '跳转图文消息', + }, + { + value: 'scancode_push', + label: '扫码直接返回结果', + }, + { + value: 'scancode_waitmsg', + label: '扫码回复', + }, + { + value: 'pic_sysphoto', + label: '系统拍照发图', + }, + { + value: 'pic_photo_or_album', + label: '拍照或者相册', + }, + { + value: 'pic_weixin', + label: '微信相册', + }, + { + value: 'location_select', + label: '选择地理位置', + }, +]; diff --git a/apps/web-ele/src/views/mp/menu/components/types.ts b/apps/web-ele/src/views/mp/menu/components/types.ts new file mode 100644 index 000000000..928534ea9 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/components/types.ts @@ -0,0 +1,73 @@ +export interface Replay { + title: string; + description: string; + picUrl: string; + url: string; +} + +export type MenuType = + | '' + | 'article_view_limited' + | 'click' + | 'location_select' + | 'pic_photo_or_album' + | 'pic_sysphoto' + | 'pic_weixin' + | 'scancode_push' + | 'scancode_waitmsg' + | 'view'; + +interface _RawMenu { + // db + id: number; + parentId: number; + accountId: number; + appId: string; + createTime: number; + + // mp-native + name: string; + menuKey: string; + type: MenuType; + url: string; + miniProgramAppId: string; + miniProgramPagePath: string; + articleId: string; + replyMessageType: string; + replyContent: string; + replyMediaId: string; + replyMediaUrl: string; + replyThumbMediaId: string; + replyThumbMediaUrl: string; + replyTitle: string; + replyDescription: string; + replyArticles: Replay; + replyMusicUrl: string; + replyHqMusicUrl: string; +} + +export type RawMenu = Partial<_RawMenu>; + +interface _Reply { + type: string; + accountId: number; + content: string; + mediaId: string; + url: string; + thumbMediaId: string; + thumbMediaUrl: string; + title: string; + description: string; + articles: null | Replay[]; + musicUrl: string; + hqMusicUrl: string; +} + +export type Reply = Partial<_Reply>; + +interface _Menu extends RawMenu { + children: _Menu[]; + reply: Reply; +} + +export type Menu = Partial<_Menu>; diff --git a/apps/web-ele/src/views/mp/menu/data.ts b/apps/web-ele/src/views/mp/menu/data.ts new file mode 100644 index 000000000..2a22837ba --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/data.ts @@ -0,0 +1,9 @@ +/** 菜单未选中标识 */ +export const MENU_NOT_SELECTED = '__MENU_NOT_SELECTED__'; + +/** 菜单级别枚举 */ +export enum Level { + Child = '2', + Parent = '1', + Undefined = '0', +} diff --git a/apps/web-ele/src/views/mp/menu/index.vue b/apps/web-ele/src/views/mp/menu/index.vue new file mode 100644 index 000000000..36425357f --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/index.vue @@ -0,0 +1,411 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/menu/modules/menu-editor.vue b/apps/web-ele/src/views/mp/menu/modules/menu-editor.vue new file mode 100644 index 000000000..45f259a68 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/modules/menu-editor.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/menu/modules/menu-previewer.vue b/apps/web-ele/src/views/mp/menu/modules/menu-previewer.vue new file mode 100644 index 000000000..44ae9fc1d --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/modules/menu-previewer.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/playground/package.json b/playground/package.json index c8651563a..6c3045e62 100644 --- a/playground/package.json +++ b/playground/package.json @@ -54,6 +54,7 @@ "vue-router": "catalog:" }, "devDependencies": { - "@types/json-bigint": "catalog:" + "@types/json-bigint": "catalog:", + "vite": "catalog:" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d74811fa4..484162529 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -669,10 +669,10 @@ importers: version: link:scripts/vsh '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.1(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + version: 6.0.1(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vitejs/plugin-vue-jsx': specifier: 'catalog:' - version: 5.1.1(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + version: 5.1.1(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vue/test-utils': specifier: 'catalog:' version: 2.4.6 @@ -714,10 +714,10 @@ importers: version: 3.6.1(sass@1.93.2)(typescript@5.9.3)(vue-tsc@2.2.10(typescript@5.9.3))(vue@3.5.22(typescript@5.9.3)) vite: specifier: 'catalog:' - version: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + version: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) vitest: specifier: 'catalog:' - version: 3.2.4(@types/node@22.18.11)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + version: 3.2.4(@types/node@22.18.11)(happy-dom@17.6.3)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) vue: specifier: ^3.5.17 version: 3.5.22(typescript@5.9.3) @@ -854,6 +854,9 @@ importers: vue3-signature: specifier: 'catalog:' version: 0.2.4(vue@3.5.22(typescript@5.9.3)) + vuedraggable: + specifier: 'catalog:' + version: 4.1.0(vue@3.5.22(typescript@5.9.3)) apps/web-ele: dependencies: @@ -914,6 +917,9 @@ importers: '@vueuse/integrations': specifier: 'catalog:' version: 13.9.0(async-validator@4.2.5)(axios@1.12.2)(focus-trap@7.6.5)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.22(typescript@5.9.3)) + benz-amr-recorder: + specifier: ^1.1.5 + version: 1.1.5 cropperjs: specifier: 'catalog:' version: 1.6.2 @@ -2113,6 +2119,9 @@ importers: '@types/json-bigint': specifier: 'catalog:' version: 1.0.4 + vite: + specifier: 'catalog:' + version: 7.1.11(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) scripts/turbo-run: dependencies: @@ -5721,6 +5730,12 @@ packages: resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==} hasBin: true + benz-amr-recorder@1.1.5: + resolution: {integrity: sha512-NepctcNTsZHK8NxBb5uKO5p8S+xkbm+vD6GLSkCYdJeEsriexvgumLHpDkanX4QJBcLRMVtg16buWMs+gUPB3g==} + + benz-recorderjs@1.0.5: + resolution: {integrity: sha512-EwedOQo9KLti7HxDi/eZY51PSRbAXnOdEZmLvJ6ro3QQSoF9Y3AXBt57MIllGvVz5vtFYMeikG+GD7qTm3+p9w==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -15108,14 +15123,14 @@ snapshots: dependencies: vite-plugin-pwa: 1.1.0(vite@5.4.21(@types/node@24.8.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0))(workbox-build@7.3.0)(workbox-window@7.3.0) - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) '@rolldown/pluginutils': 1.0.0-beta.44 '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4) - vite: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) transitivePeerDependencies: - supports-color @@ -15137,10 +15152,10 @@ snapshots: vite: 5.4.21(@types/node@24.8.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0) vue: 3.5.22(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) '@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': @@ -15157,13 +15172,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -15782,6 +15797,12 @@ snapshots: baseline-browser-mapping@2.8.18: {} + benz-amr-recorder@1.1.5: + dependencies: + benz-recorderjs: 1.0.5 + + benz-recorderjs@1.0.5: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -21583,13 +21604,13 @@ snapshots: dependencies: vite: 7.1.11(@types/node@24.8.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) - vite-node@3.2.4(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -21758,7 +21779,7 @@ snapshots: sass: 1.93.2 terser: 5.44.0 - vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): + vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): dependencies: esbuild: 0.25.3 fdir: 6.5.0(picomatch@4.0.3) @@ -21769,7 +21790,7 @@ snapshots: optionalDependencies: '@types/node': 22.18.11 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 1.21.7 less: 4.4.2 sass: 1.93.2 terser: 5.44.0 @@ -21851,11 +21872,11 @@ snapshots: - typescript - universal-cookie - vitest@3.2.4(@types/node@22.18.11)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): + vitest@3.2.4(@types/node@22.18.11)(happy-dom@17.6.3)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -21873,8 +21894,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.18.11 @@ -21897,7 +21918,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.11)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4