diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index dd8b2435c..217712b36 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -43,9 +43,11 @@ "@vben/styles": "workspace:*", "@vben/types": "workspace:*", "@vben/utils": "workspace:*", + "@videojs-player/vue": "^1.0.0", "@vueuse/core": "catalog:", "@vueuse/integrations": "catalog:", "ant-design-vue": "catalog:", + "benz-amr-recorder": "^1.1.5", "bpmn-js": "catalog:", "bpmn-js-properties-panel": "catalog:", "bpmn-js-token-simulation": "catalog:", @@ -55,9 +57,11 @@ "diagram-js": "catalog:", "fast-xml-parser": "catalog:", "highlight.js": "catalog:", + "lodash": "^4.17.21", "pinia": "catalog:", "steady-xml": "catalog:", "tinymce": "catalog:", + "video.js": "^7.21.5", "vue": "catalog:", "vue-dompurify-html": "catalog:", "vue-router": "catalog:", diff --git a/apps/web-antd/src/api/mp/account/index.ts b/apps/web-antd/src/api/mp/account/index.ts index c5ee331b0..4381926b8 100644 --- a/apps/web-antd/src/api/mp/account/index.ts +++ b/apps/web-antd/src/api/mp/account/index.ts @@ -5,12 +5,12 @@ import { requestClient } from '#/api/request'; export namespace MpAccountApi { /** 公众号账号信息 */ export interface Account { - id?: number; + id: number; name: string; - account: string; - appId: string; - appSecret: string; - token: string; + account?: string; + appId?: string; + appSecret?: string; + token?: string; aesKey?: string; qrCodeUrl?: string; remark?: string; @@ -23,6 +23,10 @@ export namespace MpAccountApi { } } +// 重新导出类型,方便使用 +export type Account = MpAccountApi.Account; +export type AccountSimple = MpAccountApi.AccountSimple; + /** 查询公众号账号列表 */ export function getAccountPage(params: PageParam) { return requestClient.get>( diff --git a/apps/web-antd/src/locales/langs/zh-CN/page.json b/apps/web-antd/src/locales/langs/zh-CN/page.json index eefc4924b..f56160a6a 100644 --- a/apps/web-antd/src/locales/langs/zh-CN/page.json +++ b/apps/web-antd/src/locales/langs/zh-CN/page.json @@ -29,5 +29,11 @@ "tenant": { "placeholder": "请选择租户", "success": "切换租户成功" + }, + "mp": { + "upload": { + "invalidFormat": "上传{0}格式不对!", + "maxSize": "上传{0}大小不能超过{1}M!" + } } } diff --git a/apps/web-antd/src/store/index.ts b/apps/web-antd/src/store/index.ts index 269586ee8..79a9e23d4 100644 --- a/apps/web-antd/src/store/index.ts +++ b/apps/web-antd/src/store/index.ts @@ -1 +1,2 @@ export * from './auth'; +export * from './tagsView'; diff --git a/apps/web-antd/src/store/tagsView.ts b/apps/web-antd/src/store/tagsView.ts new file mode 100644 index 000000000..5067e2016 --- /dev/null +++ b/apps/web-antd/src/store/tagsView.ts @@ -0,0 +1,176 @@ +import type { RouteLocationNormalizedLoaded } from 'vue-router'; + +import { useRouter } from 'vue-router'; + +import { findIndex } from 'lodash'; +import { defineStore } from 'pinia'; + +import { getRawRoute } from '../utils/routerHelper'; + +const router = useRouter(); + +export interface TagsViewState { + visitedViews: RouteLocationNormalizedLoaded[]; + cachedViews: Set; +} + +export const useTagsViewStore = defineStore('tagsView', { + state: (): TagsViewState => ({ + visitedViews: [], + cachedViews: new Set(), + }), + + getters: { + getVisitedViews(): RouteLocationNormalizedLoaded[] { + return this.visitedViews; + }, + getCachedViews(): string[] { + return [...this.cachedViews]; + }, + }, + + 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 suffixList: string[] = []; + this.visitedViews.forEach((v) => { + if ( + v.path === visitedView.path && + v.meta?.title === visitedView.meta?.title + ) { + const rawSuffix = v.meta?.titleSuffix; + const suffixStr = + typeof rawSuffix === 'string' || typeof rawSuffix === 'number' + ? `${rawSuffix}` + : undefined; + suffixList.push(suffixStr ?? '1'); + } + }); + if (suffixList.length > 0) { + let suffix = 1; + while (suffixList.includes(`${suffix}`)) suffix += 1; + visitedView.meta.titleSuffix = suffix === 1 ? undefined : `${suffix}`; + } + } + + this.visitedViews.push(visitedView); + }, + + /** 新增缓存 */ + addCachedView() { + const cacheMap: Set = new Set(); + for (const v of this.visitedViews) { + const item = getRawRoute(v); + if (!item.meta?.noCache) { + const name = item.name as string; + cacheMap.add(name); + } + } + if ( + [...this.cachedViews].sort().toString() === + [...cacheMap].sort().toString() + ) { + return; + } + this.cachedViews = cacheMap; + }, + + /** 删除某个tag和缓存 */ + delView(view: RouteLocationNormalizedLoaded) { + this.delVisitedView(view); + this.delCachedView(); + }, + + /** 删除tag */ + delVisitedView(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) this.visitedViews.splice(index, 1); + }, + + /** 删除缓存 */ + delCachedView() { + const route = router.currentRoute.value; + const index = findIndex( + this.getCachedViews, + (v) => v === route.name, + ); + if (index > -1) { + const name = this.getCachedViews[index] as string; + this.cachedViews.delete(name); + } + }, + + /** 删除全部tag和缓存 */ + delAllViews() { + this.visitedViews = []; + this.cachedViews.clear(); + }, + + /** 删除其他tag和缓存 */ + delOthersViews(view: RouteLocationNormalizedLoaded) { + this.visitedViews = this.visitedViews.filter( + (v) => v?.meta?.affix || v.fullPath === view.fullPath, + ); + this.addCachedView(); + }, + + /** 删除左侧tag */ + delLeftViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) { + this.visitedViews = this.visitedViews.filter( + (v, i) => v?.meta?.affix || v.fullPath === view.fullPath || i > index, + ); + this.addCachedView(); + } + }, + + /** 删除右侧tag */ + delRightViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) { + this.visitedViews = this.visitedViews.filter( + (v, i) => v?.meta?.affix || v.fullPath === view.fullPath || i < index, + ); + this.addCachedView(); + } + }, + + /** 更新tag */ + updateVisitedView(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.fullPath === view.fullPath, + ); + if (index > -1) { + this.visitedViews[index] = { + ...this.visitedViews[index], + ...view, + } as RouteLocationNormalizedLoaded; + } + }, + }, +}); diff --git a/apps/web-antd/src/utils/routerHelper.ts b/apps/web-antd/src/utils/routerHelper.ts index 0f3c52096..6c86773ab 100644 --- a/apps/web-antd/src/utils/routerHelper.ts +++ b/apps/web-antd/src/utils/routerHelper.ts @@ -1,3 +1,5 @@ +import type { RouteLocationNormalizedLoaded } from 'vue-router'; + import { defineAsyncComponent } from 'vue'; const modules = import.meta.glob('../views/**/*.{vue,tsx}'); @@ -14,3 +16,14 @@ export function registerComponent(componentPath: string) { } } } + +export function getRawRoute( + route: RouteLocationNormalizedLoaded, +): RouteLocationNormalizedLoaded { + if (!route) return route; + const { matched, ...others } = route; + return { + ...others, + matched: matched ? matched.map((m) => ({ ...m })) : [], + }; +} diff --git a/apps/web-antd/src/views/mp/components/wx-account-select/index.ts b/apps/web-antd/src/views/mp/components/wx-account-select/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-account-select/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-antd/src/views/mp/components/wx-account-select/main.vue b/apps/web-antd/src/views/mp/components/wx-account-select/main.vue new file mode 100644 index 000000000..db0a3986f --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-account-select/main.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-location/index.ts b/apps/web-antd/src/views/mp/components/wx-location/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-location/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-antd/src/views/mp/components/wx-location/main.vue b/apps/web-antd/src/views/mp/components/wx-location/main.vue new file mode 100644 index 000000000..eac33c164 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-location/main.vue @@ -0,0 +1,53 @@ + + + diff --git a/apps/web-antd/src/views/mp/components/wx-material-select/index.ts b/apps/web-antd/src/views/mp/components/wx-material-select/index.ts new file mode 100644 index 000000000..f4fcea0ba --- /dev/null +++ b/apps/web-antd/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-antd/src/views/mp/components/wx-material-select/main.vue b/apps/web-antd/src/views/mp/components/wx-material-select/main.vue new file mode 100644 index 000000000..a713219fc --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-material-select/main.vue @@ -0,0 +1,368 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-material-select/types.ts b/apps/web-antd/src/views/mp/components/wx-material-select/types.ts new file mode 100644 index 000000000..3d44c5db7 --- /dev/null +++ b/apps/web-antd/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-antd/src/views/mp/components/wx-msg/card.scss b/apps/web-antd/src/views/mp/components/wx-msg/card.scss new file mode 100644 index 000000000..58fde79fd --- /dev/null +++ b/apps/web-antd/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-antd/src/views/mp/components/wx-msg/comment.scss b/apps/web-antd/src/views/mp/components/wx-msg/comment.scss new file mode 100644 index 000000000..219e2e5d4 --- /dev/null +++ b/apps/web-antd/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-antd/src/views/mp/components/wx-msg/components/Msg.vue b/apps/web-antd/src/views/mp/components/wx-msg/components/Msg.vue new file mode 100644 index 000000000..d8bc8d2d4 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-msg/components/Msg.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-msg/components/MsgEvent.vue b/apps/web-antd/src/views/mp/components/wx-msg/components/MsgEvent.vue new file mode 100644 index 000000000..24b039ebf --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-msg/components/MsgEvent.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-antd/src/views/mp/components/wx-msg/components/MsgList.vue b/apps/web-antd/src/views/mp/components/wx-msg/components/MsgList.vue new file mode 100644 index 000000000..a2991ad71 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-msg/components/MsgList.vue @@ -0,0 +1,69 @@ + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-msg/index.ts b/apps/web-antd/src/views/mp/components/wx-msg/index.ts new file mode 100644 index 000000000..048afc95f --- /dev/null +++ b/apps/web-antd/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-antd/src/views/mp/components/wx-msg/main.vue b/apps/web-antd/src/views/mp/components/wx-msg/main.vue new file mode 100644 index 000000000..f7d9b205f --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-msg/main.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-msg/types.ts b/apps/web-antd/src/views/mp/components/wx-msg/types.ts new file mode 100644 index 000000000..9742db434 --- /dev/null +++ b/apps/web-antd/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 { + accountId: number; + avatar: string; + nickname: string; +} diff --git a/apps/web-antd/src/views/mp/components/wx-music/index.ts b/apps/web-antd/src/views/mp/components/wx-music/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-music/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-antd/src/views/mp/components/wx-music/main.vue b/apps/web-antd/src/views/mp/components/wx-music/main.vue new file mode 100644 index 000000000..3f3919d9f --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-music/main.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-news/index.ts b/apps/web-antd/src/views/mp/components/wx-news/index.ts new file mode 100644 index 000000000..374d993fe --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-news/index.ts @@ -0,0 +1 @@ +export { default } from './main.vue'; diff --git a/apps/web-antd/src/views/mp/components/wx-news/main.vue b/apps/web-antd/src/views/mp/components/wx-news/main.vue new file mode 100644 index 000000000..04688a1e8 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-news/main.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-reply/components/TabImage.vue b/apps/web-antd/src/views/mp/components/wx-reply/components/TabImage.vue new file mode 100644 index 000000000..e0ae48988 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-reply/components/TabImage.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-reply/components/TabMusic.vue b/apps/web-antd/src/views/mp/components/wx-reply/components/TabMusic.vue new file mode 100644 index 000000000..397c84594 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-reply/components/TabMusic.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-reply/components/TabNews.vue b/apps/web-antd/src/views/mp/components/wx-reply/components/TabNews.vue new file mode 100644 index 000000000..7baf274d8 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-reply/components/TabNews.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/components/wx-reply/components/TabText.vue b/apps/web-antd/src/views/mp/components/wx-reply/components/TabText.vue new file mode 100644 index 000000000..914d943a6 --- /dev/null +++ b/apps/web-antd/src/views/mp/components/wx-reply/components/TabText.vue @@ -0,0 +1,26 @@ + + +