Merge remote-tracking branch 'yudao/dev' into dev
# Conflicts: # src/views/Login/components/LoginForm.vue # src/views/Login/components/MobileForm.vue # src/views/mp/components/wx-editor/WxEditor.vuepull/125/head
						commit
						ec58321dd8
					
				|  | @ -40,5 +40,15 @@ | |||
|   "i18n-ally.displayLanguage": "zh-CN", | ||||
|   "i18n-ally.enabledFrameworks": ["vue", "react"], | ||||
|   "god.tsconfig": "./tsconfig.json", | ||||
|   "vue-i18n.i18nPaths": "src/locales" | ||||
|   "vue-i18n.i18nPaths": "src/locales", | ||||
|   "explorer.fileNesting.enabled": true, | ||||
|   "explorer.fileNesting.expand": false, | ||||
|   "explorer.fileNesting.patterns": { | ||||
|     "*.ts": "$(capture).test.ts, $(capture).test.tsx", | ||||
|     "*.tsx": "$(capture).test.ts, $(capture).test.tsx", | ||||
|     "*.env": "$(capture).env.*", | ||||
|     "CHANGELOG.md": "CHANGELOG*", | ||||
|     "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,README*,.npmrc,.browserslistrc,vite.config.*,windi.*,tailwind.*,tsconfig.*,postcss*", | ||||
|     ".eslintrc.js": ".eslintignore,.eslintrc-*,.prettierignore,.stylelintignore,.commitlintrc.js,.prettierrc.js,.stylelint*,stylelint*,prettier.*,.editorconfig" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -32,13 +32,12 @@ | |||
|     "@form-create/element-ui": "^3.1.17", | ||||
|     "@iconify/iconify": "^3.1.0", | ||||
|     "@videojs-player/vue": "^1.0.0", | ||||
|     "@vueup/vue-quill": "^1.1.1", | ||||
|     "@vueuse/core": "^9.13.0", | ||||
|     "@wangeditor/editor": "^5.1.23", | ||||
|     "@wangeditor/editor-for-vue": "^5.1.10", | ||||
|     "@zxcvbn-ts/core": "^2.2.1", | ||||
|     "animate.css": "^4.1.1", | ||||
|     "axios": "^1.3.4", | ||||
|     "axios": "^1.3.5", | ||||
|     "benz-amr-recorder": "^1.1.5", | ||||
|     "bpmn-js-token-simulation": "^0.10.0", | ||||
|     "camunda-bpmn-moddle": "^7.0.1", | ||||
|  | @ -48,16 +47,16 @@ | |||
|     "diagram-js": "^11.6.0", | ||||
|     "echarts": "^5.4.1", | ||||
|     "echarts-wordcloud": "^2.1.0", | ||||
|     "element-plus": "2.3.1", | ||||
|     "element-plus": "2.3.3", | ||||
|     "fast-xml-parser": "^4.1.3", | ||||
|     "highlight.js": "^11.7.0", | ||||
|     "intro.js": "^6.0.0", | ||||
|     "intro.js": "^7.0.1", | ||||
|     "jsencrypt": "^3.3.2", | ||||
|     "lodash-es": "^4.17.21", | ||||
|     "min-dash": "^4.0.0", | ||||
|     "mitt": "^3.0.0", | ||||
|     "nprogress": "^0.2.0", | ||||
|     "pinia": "^2.0.33", | ||||
|     "pinia": "^2.0.34", | ||||
|     "qrcode": "^1.5.1", | ||||
|     "qs": "^6.11.1", | ||||
|     "steady-xml": "^0.1.0", | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ const props = defineProps({ | |||
|   editorId: propTypes.string.def('wangeEditor-1'), | ||||
|   height: propTypes.oneOfType([Number, String]).def('500px'), | ||||
|   editorConfig: { | ||||
|     type: Object as PropType<IEditorConfig>, | ||||
|     type: Object as PropType<Partial<IEditorConfig>>, | ||||
|     default: () => undefined | ||||
|   }, | ||||
|   readonly: propTypes.bool.def(false), | ||||
|  | @ -147,6 +147,7 @@ const editorConfig = computed((): IEditorConfig => { | |||
|     props.editorConfig || {} | ||||
|   ) | ||||
| }) | ||||
| 
 | ||||
| const editorStyle = computed(() => { | ||||
|   return { | ||||
|     height: isNumber(props.height) ? `${props.height}px` : props.height | ||||
|  |  | |||
|  | @ -111,7 +111,8 @@ | |||
|               > | ||||
|               <el-divider direction="vertical" /> | ||||
|               <el-button | ||||
|                 type="text" | ||||
|                 type="primary" | ||||
|                 link | ||||
|                 style="color: #ff4d4f" | ||||
|                 @click="removeFieldOptionItem(scope, scope.$index, 'enum')" | ||||
|                 >移除</el-button | ||||
|  | @ -143,7 +144,8 @@ | |||
|             > | ||||
|             <el-divider direction="vertical" /> | ||||
|             <el-button | ||||
|               type="text" | ||||
|               type="primary" | ||||
|               link | ||||
|               style="color: #ff4d4f" | ||||
|               @click="removeFieldOptionItem(scope, scope.$index, 'constraint')" | ||||
|               >移除</el-button | ||||
|  | @ -174,7 +176,8 @@ | |||
|             > | ||||
|             <el-divider direction="vertical" /> | ||||
|             <el-button | ||||
|               type="text" | ||||
|               type="primary" | ||||
|               link | ||||
|               style="color: #ff4d4f" | ||||
|               @click="removeFieldOptionItem(scope, scope.$index, 'property')" | ||||
|               >移除</el-button | ||||
|  |  | |||
|  | @ -29,6 +29,8 @@ type CrudSearchParams = { | |||
|   show?: boolean | ||||
|   // 接口
 | ||||
|   api?: () => Promise<any> | ||||
|   // 搜索字段
 | ||||
|   field?: string | ||||
| } & Omit<FormSchema, 'field'> | ||||
| 
 | ||||
| type CrudTableParams = { | ||||
|  |  | |||
|  | @ -21,16 +21,17 @@ const getCaches = computed((): string[] => { | |||
| <template> | ||||
|   <section | ||||
|     :class="[ | ||||
|       'p-[var(--app-content-padding)] w-[100%] bg-[var(--app-contnet-bg-color)] dark:bg-[var(--el-bg-color)]', | ||||
|       'p-[var(--app-content-padding)] w-[100%] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]', | ||||
|       { | ||||
|         '!min-h-[calc(100%-var(--app-footer-height))]': | ||||
|           fixedHeader && (layout === 'classic' || layout === 'topLeft') && footer, | ||||
|           ((fixedHeader && (layout === 'classic' || layout === 'topLeft')) || layout === 'top') && | ||||
|           footer, | ||||
| 
 | ||||
|         '!min-h-[calc(100%-var(--tags-view-height)-var(--top-tool-height)-var(--app-footer-height))]': | ||||
|           ((!fixedHeader && layout === 'classic') || layout === 'top') && footer, | ||||
|           !fixedHeader && layout === 'classic' && footer, | ||||
| 
 | ||||
|         '!min-h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]': | ||||
|           !fixedHeader && layout === 'topLeft' && footer, | ||||
|           !fixedHeader && (layout === 'topLeft' || layout === 'top') && footer, | ||||
| 
 | ||||
|         '!min-h-[calc(100%-var(--top-tool-height))]': fixedHeader && layout === 'cutMenu' && footer, | ||||
| 
 | ||||
|  |  | |||
|  | @ -177,7 +177,9 @@ export const useRenderLayout = () => { | |||
|             class={[ | ||||
|               `${prefixCls}-content-scrollbar`, | ||||
|               { | ||||
|                 'mt-[var(--tags-view-height)]': fixedHeader.value | ||||
|                 'mt-[var(--tags-view-height)] !pb-[calc(var(--tags-view-height)+var(--app-footer-height))]': | ||||
|                   fixedHeader.value, | ||||
|                 'pb-[var(--app-footer-height)]': !fixedHeader.value | ||||
|               } | ||||
|             ]} | ||||
|           > | ||||
|  |  | |||
|  | @ -37,8 +37,6 @@ import App from './App.vue' | |||
| 
 | ||||
| import './permission' | ||||
| 
 | ||||
| import { isDevMode } from '@/utils/env' | ||||
| 
 | ||||
| import { MyPD } from '@/components/bpmnProcessDesigner/package/index.js' | ||||
| import '@/components/bpmnProcessDesigner/package/theme/index.scss' | ||||
| import 'bpmn-js/dist/assets/diagram-js.css' | ||||
|  | @ -53,11 +51,6 @@ import '@/plugins/tongji' // 百度统计 | |||
| 
 | ||||
| import Logger from '@/utils/Logger' | ||||
| 
 | ||||
| // 本地开发模式 全局引入 element-plus 样式,加快第一次进入速度
 | ||||
| if (isDevMode()) { | ||||
|   import('element-plus/dist/index.css') | ||||
| } | ||||
| 
 | ||||
| // 创建实例
 | ||||
| const setupAll = async () => { | ||||
|   const app = createApp(App) | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| @import './var.css'; | ||||
| @import './vxe.css'; | ||||
| @import 'element-plus/theme-chalk/dark/css-vars.css'; | ||||
| 
 | ||||
| .reset-margin [class*='el-icon'] + span { | ||||
|  |  | |||
|  | @ -2,5 +2,3 @@ | |||
| $namespace: v; | ||||
| // el命名空间 | ||||
| $elNamespace: el; | ||||
| // vxe命名空间 | ||||
| $vxeNamespace: vxe; | ||||
|  |  | |||
|  | @ -1,30 +0,0 @@ | |||
| /*滚动条整体部分*/ | ||||
| .xtable-scrollbar ::-webkit-scrollbar { | ||||
|   width: 10px; | ||||
|   height: 10px; | ||||
| } | ||||
| /*滚动条的轨道*/ | ||||
| .xtable-scrollbar ::-webkit-scrollbar-track { | ||||
|   background-color: #ffffff; | ||||
| } | ||||
| /*滚动条里面的小方块,能向上向下移动*/ | ||||
| .xtable-scrollbar ::-webkit-scrollbar-thumb { | ||||
|   background-color: #bfbfbf; | ||||
|   border-radius: 5px; | ||||
|   border: 1px solid #f1f1f1; | ||||
|   box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
| .xtable-scrollbar ::-webkit-scrollbar-thumb:hover { | ||||
|   background-color: #a8a8a8; | ||||
| } | ||||
| .xtable-scrollbar ::-webkit-scrollbar-thumb:active { | ||||
|   background-color: #787878; | ||||
| } | ||||
| /*边角,即两个滚动条的交汇处*/ | ||||
| .xtable-scrollbar ::-webkit-scrollbar-corner { | ||||
|   background-color: #ffffff; | ||||
| } | ||||
| /*移除 vxe-grid 右侧外框*/ | ||||
| .vxe-button.size--medium.type--button.is--circle { | ||||
|   border: 0; | ||||
| } | ||||
|  | @ -0,0 +1 @@ | |||
| auto-*.d.ts | ||||
|  | @ -1,128 +0,0 @@ | |||
| /* eslint-disable */ | ||||
| /* prettier-ignore */ | ||||
| // @ts-nocheck
 | ||||
| // Generated by unplugin-vue-components
 | ||||
| // Read more: https://github.com/vuejs/core/pull/3399
 | ||||
| import '@vue/runtime-core' | ||||
| 
 | ||||
| export {} | ||||
| 
 | ||||
| declare module '@vue/runtime-core' { | ||||
|   export interface GlobalComponents { | ||||
|     Backtop: typeof import('./../components/Backtop/src/Backtop.vue')['default'] | ||||
|     ConfigGlobal: typeof import('./../components/ConfigGlobal/src/ConfigGlobal.vue')['default'] | ||||
|     ContentDetailWrap: typeof import('./../components/ContentDetailWrap/src/ContentDetailWrap.vue')['default'] | ||||
|     ContentWrap: typeof import('./../components/ContentWrap/src/ContentWrap.vue')['default'] | ||||
|     CopperModal: typeof import('./../components/Cropper/src/CopperModal.vue')['default'] | ||||
|     CountTo: typeof import('./../components/CountTo/src/CountTo.vue')['default'] | ||||
|     Crontab: typeof import('./../components/Crontab/src/Crontab.vue')['default'] | ||||
|     Cropper: typeof import('./../components/Cropper/src/Cropper.vue')['default'] | ||||
|     CropperAvatar: typeof import('./../components/Cropper/src/CropperAvatar.vue')['default'] | ||||
|     Descriptions: typeof import('./../components/Descriptions/src/Descriptions.vue')['default'] | ||||
|     Dialog: typeof import('./../components/Dialog/src/Dialog.vue')['default'] | ||||
|     DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default'] | ||||
|     DocAlert: typeof import('./../components/DocAlert/index.vue')['default'] | ||||
|     Echart: typeof import('./../components/Echart/src/Echart.vue')['default'] | ||||
|     Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] | ||||
|     ElAlert: typeof import('element-plus/es')['ElAlert'] | ||||
|     ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer'] | ||||
|     ElAvatar: typeof import('element-plus/es')['ElAvatar'] | ||||
|     ElBadge: typeof import('element-plus/es')['ElBadge'] | ||||
|     ElButton: typeof import('element-plus/es')['ElButton'] | ||||
|     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] | ||||
|     ElCard: typeof import('element-plus/es')['ElCard'] | ||||
|     ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] | ||||
|     ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] | ||||
|     ElCol: typeof import('element-plus/es')['ElCol'] | ||||
|     ElCollapse: typeof import('element-plus/es')['ElCollapse'] | ||||
|     ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] | ||||
|     ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] | ||||
|     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] | ||||
|     ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] | ||||
|     ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] | ||||
|     ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] | ||||
|     ElDialog: typeof import('element-plus/es')['ElDialog'] | ||||
|     ElDivider: typeof import('element-plus/es')['ElDivider'] | ||||
|     ElDrawer: typeof import('element-plus/es')['ElDrawer'] | ||||
|     ElDropdown: typeof import('element-plus/es')['ElDropdown'] | ||||
|     ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] | ||||
|     ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] | ||||
|     ElementBaseInfo: typeof import('./../components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue')['default'] | ||||
|     ElementForm: typeof import('./../components/bpmnProcessDesigner/package/penal/form/ElementForm.vue')['default'] | ||||
|     ElementListeners: typeof import('./../components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue')['default'] | ||||
|     ElementMultiInstance: typeof import('./../components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue')['default'] | ||||
|     ElementOtherConfig: typeof import('./../components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue')['default'] | ||||
|     ElementProperties: typeof import('./../components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue')['default'] | ||||
|     ElementTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/ElementTask.vue')['default'] | ||||
|     ElForm: typeof import('element-plus/es')['ElForm'] | ||||
|     ElFormItem: typeof import('element-plus/es')['ElFormItem'] | ||||
|     ElIcon: typeof import('element-plus/es')['ElIcon'] | ||||
|     ElImage: typeof import('element-plus/es')['ElImage'] | ||||
|     ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] | ||||
|     ElInput: typeof import('element-plus/es')['ElInput'] | ||||
|     ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] | ||||
|     ElLink: typeof import('element-plus/es')['ElLink'] | ||||
|     ElOption: typeof import('element-plus/es')['ElOption'] | ||||
|     ElPagination: typeof import('element-plus/es')['ElPagination'] | ||||
|     ElPopover: typeof import('element-plus/es')['ElPopover'] | ||||
|     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'] | ||||
|     ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] | ||||
|     ElSpace: typeof import('element-plus/es')['ElSpace'] | ||||
|     ElSwitch: typeof import('element-plus/es')['ElSwitch'] | ||||
|     ElTable: typeof import('element-plus/es')['ElTable'] | ||||
|     ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] | ||||
|     ElTableV2: typeof import('element-plus/es')['ElTableV2'] | ||||
|     ElTabPane: typeof import('element-plus/es')['ElTabPane'] | ||||
|     ElTabs: typeof import('element-plus/es')['ElTabs'] | ||||
|     ElTag: typeof import('element-plus/es')['ElTag'] | ||||
|     ElTimeline: typeof import('element-plus/es')['ElTimeline'] | ||||
|     ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] | ||||
|     ElTooltip: typeof import('element-plus/es')['ElTooltip'] | ||||
|     ElTree: typeof import('element-plus/es')['ElTree'] | ||||
|     ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] | ||||
|     ElUpload: typeof import('element-plus/es')['ElUpload'] | ||||
|     Error: typeof import('./../components/Error/src/Error.vue')['default'] | ||||
|     FlowCondition: typeof import('./../components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue')['default'] | ||||
|     Form: typeof import('./../components/Form/src/Form.vue')['default'] | ||||
|     Highlight: typeof import('./../components/Highlight/src/Highlight.vue')['default'] | ||||
|     Icon: typeof import('./../components/Icon/src/Icon.vue')['default'] | ||||
|     IconSelect: typeof import('./../components/Icon/src/IconSelect.vue')['default'] | ||||
|     IFrame: typeof import('./../components/IFrame/src/IFrame.vue')['default'] | ||||
|     ImageViewer: typeof import('./../components/ImageViewer/src/ImageViewer.vue')['default'] | ||||
|     Infotip: typeof import('./../components/Infotip/src/Infotip.vue')['default'] | ||||
|     InputPassword: typeof import('./../components/InputPassword/src/InputPassword.vue')['default'] | ||||
|     Pagination: typeof import('./../components/Pagination/index.vue')['default'] | ||||
|     ProcessDesigner: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue')['default'] | ||||
|     ProcessPalette: typeof import('./../components/bpmnProcessDesigner/package/palette/ProcessPalette.vue')['default'] | ||||
|     ProcessViewer: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessViewer.vue')['default'] | ||||
|     PropertiesPanel: typeof import('./../components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue')['default'] | ||||
|     Qrcode: typeof import('./../components/Qrcode/src/Qrcode.vue')['default'] | ||||
|     ReceiveTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue')['default'] | ||||
|     RouterLink: typeof import('vue-router')['RouterLink'] | ||||
|     RouterView: typeof import('vue-router')['RouterView'] | ||||
|     ScriptTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue')['default'] | ||||
|     Search: typeof import('./../components/Search/src/Search.vue')['default'] | ||||
|     SignalAndMessage: typeof import('./../components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue')['default'] | ||||
|     Sticky: typeof import('./../components/Sticky/src/Sticky.vue')['default'] | ||||
|     Table: typeof import('./../components/Table/src/Table.vue')['default'] | ||||
|     Tooltip: typeof import('./../components/Tooltip/src/Tooltip.vue')['default'] | ||||
|     UploadFile: typeof import('./../components/UploadFile/src/UploadFile.vue')['default'] | ||||
|     UploadImg: typeof import('./../components/UploadFile/src/UploadImg.vue')['default'] | ||||
|     UploadImgs: typeof import('./../components/UploadFile/src/UploadImgs.vue')['default'] | ||||
|     UserTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue')['default'] | ||||
|     UserTaskListeners: typeof import('./../components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue')['default'] | ||||
|     Verify: typeof import('./../components/Verifition/src/Verify.vue')['default'] | ||||
|     VerifyPoints: typeof import('./../components/Verifition/src/Verify/VerifyPoints.vue')['default'] | ||||
|     VerifySlide: typeof import('./../components/Verifition/src/Verify/VerifySlide.vue')['default'] | ||||
|     XButton: typeof import('./../components/XButton/src/XButton.vue')['default'] | ||||
|     XTextButton: typeof import('./../components/XButton/src/XTextButton.vue')['default'] | ||||
|   } | ||||
|   export interface ComponentCustomProperties { | ||||
|     vLoading: typeof import('element-plus/es')['ElLoadingDirective'] | ||||
|   } | ||||
| } | ||||
|  | @ -1,76 +0,0 @@ | |||
| /* eslint-disable */ | ||||
| /* prettier-ignore */ | ||||
| // @ts-nocheck
 | ||||
| // Generated by unplugin-auto-import
 | ||||
| export {} | ||||
| declare global { | ||||
|   const DICT_TYPE: typeof import('@/utils/dict')['DICT_TYPE'] | ||||
|   const EffectScope: typeof import('vue')['EffectScope'] | ||||
|   const computed: typeof import('vue')['computed'] | ||||
|   const createApp: typeof import('vue')['createApp'] | ||||
|   const customRef: typeof import('vue')['customRef'] | ||||
|   const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] | ||||
|   const defineComponent: typeof import('vue')['defineComponent'] | ||||
|   const effectScope: typeof import('vue')['effectScope'] | ||||
|   const getCurrentInstance: typeof import('vue')['getCurrentInstance'] | ||||
|   const getCurrentScope: typeof import('vue')['getCurrentScope'] | ||||
|   const h: typeof import('vue')['h'] | ||||
|   const inject: typeof import('vue')['inject'] | ||||
|   const isProxy: typeof import('vue')['isProxy'] | ||||
|   const isReactive: typeof import('vue')['isReactive'] | ||||
|   const isReadonly: typeof import('vue')['isReadonly'] | ||||
|   const isRef: typeof import('vue')['isRef'] | ||||
|   const markRaw: typeof import('vue')['markRaw'] | ||||
|   const nextTick: typeof import('vue')['nextTick'] | ||||
|   const onActivated: typeof import('vue')['onActivated'] | ||||
|   const onBeforeMount: typeof import('vue')['onBeforeMount'] | ||||
|   const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] | ||||
|   const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] | ||||
|   const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] | ||||
|   const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] | ||||
|   const onDeactivated: typeof import('vue')['onDeactivated'] | ||||
|   const onErrorCaptured: typeof import('vue')['onErrorCaptured'] | ||||
|   const onMounted: typeof import('vue')['onMounted'] | ||||
|   const onRenderTracked: typeof import('vue')['onRenderTracked'] | ||||
|   const onRenderTriggered: typeof import('vue')['onRenderTriggered'] | ||||
|   const onScopeDispose: typeof import('vue')['onScopeDispose'] | ||||
|   const onServerPrefetch: typeof import('vue')['onServerPrefetch'] | ||||
|   const onUnmounted: typeof import('vue')['onUnmounted'] | ||||
|   const onUpdated: typeof import('vue')['onUpdated'] | ||||
|   const provide: typeof import('vue')['provide'] | ||||
|   const reactive: typeof import('vue')['reactive'] | ||||
|   const readonly: typeof import('vue')['readonly'] | ||||
|   const ref: typeof import('vue')['ref'] | ||||
|   const required: typeof import('@/utils/formRules')['required'] | ||||
|   const resolveComponent: typeof import('vue')['resolveComponent'] | ||||
|   const shallowReactive: typeof import('vue')['shallowReactive'] | ||||
|   const shallowReadonly: typeof import('vue')['shallowReadonly'] | ||||
|   const shallowRef: typeof import('vue')['shallowRef'] | ||||
|   const toRaw: typeof import('vue')['toRaw'] | ||||
|   const toRef: typeof import('vue')['toRef'] | ||||
|   const toRefs: typeof import('vue')['toRefs'] | ||||
|   const triggerRef: typeof import('vue')['triggerRef'] | ||||
|   const unref: typeof import('vue')['unref'] | ||||
|   const useAttrs: typeof import('vue')['useAttrs'] | ||||
|   const useCrudSchemas: typeof import('@/hooks/web/useCrudSchemas')['useCrudSchemas'] | ||||
|   const useCssModule: typeof import('vue')['useCssModule'] | ||||
|   const useCssVars: typeof import('vue')['useCssVars'] | ||||
|   const useI18n: typeof import('@/hooks/web/useI18n')['useI18n'] | ||||
|   const useLink: typeof import('vue-router')['useLink'] | ||||
|   const useMessage: typeof import('@/hooks/web/useMessage')['useMessage'] | ||||
|   const useRoute: typeof import('vue-router')['useRoute'] | ||||
|   const useRouter: typeof import('vue-router')['useRouter'] | ||||
|   const useSlots: typeof import('vue')['useSlots'] | ||||
|   const useTable: typeof import('@/hooks/web/useTable')['useTable'] | ||||
|   const useVxeCrudSchemas: typeof import('@/hooks/web/useVxeCrudSchemas')['useVxeCrudSchemas'] | ||||
|   const useXTable: typeof import('@/hooks/web/useXTable')['useXTable'] | ||||
|   const watch: typeof import('vue')['watch'] | ||||
|   const watchEffect: typeof import('vue')['watchEffect'] | ||||
|   const watchPostEffect: typeof import('vue')['watchPostEffect'] | ||||
|   const watchSyncEffect: typeof import('vue')['watchSyncEffect'] | ||||
| } | ||||
| // for type re-export
 | ||||
| declare global { | ||||
|   // @ts-ignore
 | ||||
|   export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' | ||||
| } | ||||
|  | @ -1,8 +0,0 @@ | |||
| export const isDevMode = () => { | ||||
|   const dev = import.meta.env.VITE_DEV | ||||
|   if (dev && dev === 'true') { | ||||
|     return true | ||||
|   } else { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | @ -21,7 +21,8 @@ | |||
|             v-model="loginData.loginForm.tenantName" | ||||
|             :placeholder="t('login.tenantNamePlaceholder')" | ||||
|             :prefix-icon="iconHouse" | ||||
|             type="text" | ||||
|             type="primary" | ||||
|             link | ||||
|           /> | ||||
|         </el-form-item> | ||||
|       </el-col> | ||||
|  |  | |||
|  | @ -22,7 +22,8 @@ | |||
|             v-model="loginData.loginForm.tenantName" | ||||
|             :placeholder="t('login.tenantNamePlaceholder')" | ||||
|             :prefix-icon="iconHouse" | ||||
|             type="text" | ||||
|             type="primary" | ||||
|             link | ||||
|           /> | ||||
|         </el-form-item> | ||||
|       </el-col> | ||||
|  |  | |||
|  | @ -18,7 +18,8 @@ | |||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             v-if="scope.row.formType === 10" | ||||
|             type="text" | ||||
|             type="primary" | ||||
|             link | ||||
|             @click="handleFormDetail(scope.row)" | ||||
|           > | ||||
|             <span>{{ scope.row.formName }}</span> | ||||
|  |  | |||
|  | @ -79,14 +79,16 @@ | |||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             v-if="scope.row.formType === 10" | ||||
|             type="text" | ||||
|             type="primary" | ||||
|             link | ||||
|             @click="handleFormDetail(scope.row)" | ||||
|           > | ||||
|             <span>{{ scope.row.formName }}</span> | ||||
|           </el-button> | ||||
|           <el-button | ||||
|             v-else-if="scope.row.formType === 20" | ||||
|             type="text" | ||||
|             type="primary" | ||||
|             link | ||||
|             @click="handleFormDetail(scope.row)" | ||||
|           > | ||||
|             <span>{{ scope.row.formCustomCreatePath }}</span> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
|   <ContentWrap> | ||||
|     <el-form class="-mb-15px" :model="queryParams" :inline="true" label-width="68px"> | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
|  | @ -140,7 +140,7 @@ | |||
|     </el-table> | ||||
| 
 | ||||
|     <!-- 添加或修改自动回复的对话框 --> | ||||
|     <el-dialog :title="title" v-model="showReplyFormDialog" width="800px" append-to-body> | ||||
|     <el-dialog :title="title" v-model="showReplyFormDialog" width="800px" destroy-on-close> | ||||
|       <el-form ref="formRef" :model="replyForm" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="消息类型" prop="requestMessageType" v-if="msgType === MsgType.Message"> | ||||
|           <el-select v-model="replyForm.requestMessageType" placeholder="请选择"> | ||||
|  | @ -167,7 +167,7 @@ | |||
|           <el-input v-model="replyForm.requestKeyword" placeholder="请输入内容" clearable /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="回复消息"> | ||||
|           <WxReplySelect :objData="objData" v-if="hackResetWxReplySelect" /> | ||||
|           <WxReplySelect :objData="objData" /> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <template #footer> | ||||
|  | @ -183,7 +183,7 @@ import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' | |||
| import WxMusic from '@/views/mp/components/wx-music/main.vue' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import * as MpAutoReplyApi from '@/api/mp/autoReply' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
|  | @ -195,7 +195,7 @@ const message = useMessage() | |||
| const formRef = ref() | ||||
| 
 | ||||
| // 消息类型(Follow: 关注时回复;Message: 消息回复;Keyword: 关键词回复) | ||||
| // 作为tab.name | ||||
| // 作为tab.name,enum的数字不能随意修改,与api参数相关 | ||||
| enum MsgType { | ||||
|   Follow = 1, | ||||
|   Message = 2, | ||||
|  | @ -277,9 +277,6 @@ const rules = { | |||
|   requestMatch: [{ required: true, message: '请求的关键字的匹配不能为空', trigger: 'blur' }] | ||||
| } | ||||
| 
 | ||||
| // 重置 WxReplySelect 组件,解决无法清除的问题 | ||||
| const hackResetWxReplySelect = ref(false) | ||||
| 
 | ||||
| const onAccountChanged = (id?: number) => { | ||||
|   queryParams.accountId = id | ||||
|   getList() | ||||
|  | @ -314,7 +311,6 @@ const handleTabChange = (tabName: TabPaneName) => { | |||
| /** 新增按钮操作 */ | ||||
| const handleAdd = () => { | ||||
|   reset() | ||||
|   resetEditor() | ||||
|   // 打开表单,并设置初始化 | ||||
|   objData.value = { | ||||
|     type: 'text', | ||||
|  | @ -328,7 +324,6 @@ const handleAdd = () => { | |||
| /** 修改按钮操作 */ | ||||
| const handleUpdate = async (row: any) => { | ||||
|   reset() | ||||
|   resetEditor() | ||||
| 
 | ||||
|   const data = await MpAutoReplyApi.getAutoReply(row.id) | ||||
|   // 设置属性 | ||||
|  | @ -386,7 +381,7 @@ const handleSubmit = async () => { | |||
|   } | ||||
| 
 | ||||
|   showReplyFormDialog.value = false | ||||
|   getList() | ||||
|   await getList() | ||||
| } | ||||
| 
 | ||||
| // 表单重置 | ||||
|  | @ -408,14 +403,6 @@ const cancel = () => { | |||
|   reset() | ||||
| } | ||||
| 
 | ||||
| // 表单 Editor 重置 | ||||
| const resetEditor = () => { | ||||
|   hackResetWxReplySelect.value = false // 销毁组件 | ||||
|   nextTick(() => { | ||||
|     hackResetWxReplySelect.value = true // 重建组件 | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| const handleDelete = async (row) => { | ||||
|   await message.confirm('是否确认删除此数据?') | ||||
|   await MpAutoReplyApi.deleteAutoReply(row.id) | ||||
|  |  | |||
|  | @ -1,36 +0,0 @@ | |||
| <template> | ||||
|   <el-select v-model="account.id" placeholder="请选择公众号" class="!w-240px" @change="onChanged"> | ||||
|     <el-option v-for="item in accountList" :key="item.id" :label="item.name" :value="item.id" /> | ||||
|   </el-select> | ||||
| </template> | ||||
| <!-- TODO @芋艿:WxMpSelect 改成 WxAccountSelect,然后挪到现有的 wx-account-select 包下 --> | ||||
| <script lang="ts" setup name="WxMpSelect"> | ||||
| import * as MpAccountApi from '@/api/mp/account' | ||||
| 
 | ||||
| const account: MpAccountApi.AccountVO = reactive({ | ||||
|   id: undefined, | ||||
|   name: '' | ||||
| }) | ||||
| const accountList: Ref<MpAccountApi.AccountVO[]> = ref([]) | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'change', id?: number, name?: string): void | ||||
| }>() | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   handleQuery() | ||||
| }) | ||||
| 
 | ||||
| const handleQuery = async () => { | ||||
|   accountList.value = await MpAccountApi.getSimpleAccountList() | ||||
|   // 默认选中第一个 | ||||
|   if (accountList.value.length > 0) { | ||||
|     account.id = accountList.value[0].id | ||||
|     emit('change', account.id, account.name) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const onChanged = () => { | ||||
|   emit('change', account.id, account.name) | ||||
| } | ||||
| </script> | ||||
|  | @ -1,44 +1,37 @@ | |||
| <template> | ||||
|   <el-form class="-mb-15px" ref="queryFormRef" :inline="true" label-width="68px"> | ||||
|     <el-form-item label="公众号" prop="accountId"> | ||||
|       <!-- TODO 芋艿:需要将 el-form 和 el-select 解耦 --> | ||||
|       <el-select | ||||
|         v-model="accountId" | ||||
|         placeholder="请选择公众号" | ||||
|         class="!w-240px" | ||||
|         @change="accountChanged()" | ||||
|       > | ||||
|         <el-option v-for="item in accountList" :key="item.id" :label="item.name" :value="item.id" /> | ||||
|       </el-select> | ||||
|     </el-form-item> | ||||
|     <el-form-item> | ||||
|       <slot name="actions"></slot> | ||||
|     </el-form-item> | ||||
|   </el-form> | ||||
|   <el-select v-model="account.id" placeholder="请选择公众号" class="!w-240px" @change="onChanged"> | ||||
|     <el-option v-for="item in accountList" :key="item.id" :label="item.name" :value="item.id" /> | ||||
|   </el-select> | ||||
| </template> | ||||
| 
 | ||||
| <script setup name="WxAccountSelect"> | ||||
| <script lang="ts" setup name="WxAccountSelect"> | ||||
| import * as MpAccountApi from '@/api/mp/account' | ||||
| const accountId = ref() | ||||
| const accountList = ref([]) | ||||
| const queryFormRef = ref() | ||||
| 
 | ||||
| const emit = defineEmits(['change']) | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   handleQuery() | ||||
| const account: MpAccountApi.AccountVO = reactive({ | ||||
|   id: undefined, | ||||
|   name: '' | ||||
| }) | ||||
| const accountList: Ref<MpAccountApi.AccountVO[]> = ref([]) | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'change', id?: number, name?: string): void | ||||
| }>() | ||||
| 
 | ||||
| const handleQuery = async () => { | ||||
|   accountList.value = await MpAccountApi.getSimpleAccountList() | ||||
|   // 默认选中第一个 | ||||
|   if (accountList.value.length > 0) { | ||||
|     accountId.value = accountList.value[0].id | ||||
|     emit('change', accountId.value) | ||||
|     account.id = accountList.value[0].id | ||||
|     emit('change', account.id, account.name) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const accountChanged = () => { | ||||
|   emit('change', accountId.value) | ||||
| const onChanged = () => { | ||||
|   emit('change', account.id, account.name) | ||||
| } | ||||
| 
 | ||||
| /** 初始化 */ | ||||
| onMounted(() => { | ||||
|   handleQuery() | ||||
| }) | ||||
| </script> | ||||
|  |  | |||
|  | @ -1,104 +0,0 @@ | |||
| <script name="WxEditor" setup> | ||||
| import { reactive, ref } from 'vue' | ||||
| import { getAccessToken } from '@/utils/auth' | ||||
| import { Editor } from '@/components/Editor' | ||||
| 
 | ||||
| const BASE_URL = import.meta.env.VITE_BASE_URL | ||||
| const actionUrl = BASE_URL + '/admin-api/mp/material/upload-news-image' // 这里写你要上传的图片服务器地址 | ||||
| const headers = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 | ||||
| 
 | ||||
| const message = useMessage() | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   /* 公众号账号编号 */ | ||||
|   accountId: { | ||||
|     type: Number, | ||||
|     required: true | ||||
|   }, | ||||
|   /* 编辑器的内容 */ | ||||
|   value: { | ||||
|     type: String, | ||||
|     default: '' | ||||
|   }, | ||||
|   /* 图片大小 */ | ||||
|   maxSize: { | ||||
|     type: Number, | ||||
|     default: 4000 // kb | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const emit = defineEmits(['input']) | ||||
| 
 | ||||
| const myQuillEditorRef = ref() | ||||
| const content = ref(props.value.replace(/data-src/g, 'src')) | ||||
| const loading = ref(false) // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示 | ||||
| const uploadData = reactive({ | ||||
|   type: 'image', // TODO 芋艿:试试要不要换成 thumb | ||||
|   accountId: props.accountId | ||||
| }) | ||||
| 
 | ||||
| const onEditorChange = (text) => { | ||||
|   //内容改变事件 | ||||
|   emit('input', text) | ||||
| } | ||||
| 
 | ||||
| // 富文本图片上传前 | ||||
| const beforeUpload = () => { | ||||
|   // 显示 loading 动画 | ||||
|   loading.value = true | ||||
| } | ||||
| 
 | ||||
| // 图片上传成功 | ||||
| // 注意!由于微信公众号的图片有访问限制,所以会显示“此图片来自微信公众号,未经允许不可引用” | ||||
| const uploadSuccess = (res) => { | ||||
|   // res为图片服务器返回的数据 | ||||
|   // 获取富文本组件实例 | ||||
|   const quill = myQuillEditorRef.value.quill | ||||
|   // 如果上传成功 | ||||
|   const link = res.data | ||||
|   if (link) { | ||||
|     // 获取光标所在位置 | ||||
|     let length = quill.getSelection().index | ||||
|     // 插入图片  res.info为服务器返回的图片地址 | ||||
|     quill.insertEmbed(length, 'image', link) | ||||
|     // 调整光标到最后 | ||||
|     quill.setSelection(length + 1) | ||||
|   } else { | ||||
|     message.error('图片插入失败') | ||||
|   } | ||||
|   // loading 动画消失 | ||||
|   loading.value = false | ||||
| } | ||||
| 
 | ||||
| // 富文本图片上传失败 | ||||
| const uploadError = () => { | ||||
|   // loading 动画消失 | ||||
|   loading.value = false | ||||
|   message.error('图片插入失败') | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div id="wxEditor"> | ||||
|     <div v-loading="loading" element-loading-text="请稍等,图片上传中"> | ||||
|       <!-- 图片上传组件辅助--> | ||||
|       <el-upload | ||||
|         :action="actionUrl" | ||||
|         :before-upload="beforeUpload" | ||||
|         :data="uploadData" | ||||
|         :headers="headers" | ||||
|         :on-error="uploadError" | ||||
|         :on-success="uploadSuccess" | ||||
|         :show-file-list="false" | ||||
|         class="avatar-uploader" | ||||
|         name="file" | ||||
|       /> | ||||
|       <Editor | ||||
|         ref="quillEditorRef" | ||||
|         :modelValue="content" | ||||
|         editor-id="wxEditor" | ||||
|         @change="(editor_) => onEditorChange(editor_.getText())" | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -1,45 +0,0 @@ | |||
| const toolbarOptions = [ | ||||
|   ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
 | ||||
|   ['blockquote', 'code-block'], // 引用  代码块
 | ||||
|   [{ header: 1 }, { header: 2 }], // 1、2 级标题
 | ||||
|   [{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
 | ||||
|   [{ script: 'sub' }, { script: 'super' }], // 上标/下标
 | ||||
|   [{ indent: '-1' }, { indent: '+1' }], // 缩进
 | ||||
|   // [{'direction': 'rtl'}],                         // 文本方向
 | ||||
|   [{ size: ['small', false, 'large', 'huge'] }], // 字体大小
 | ||||
|   [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
 | ||||
|   [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
 | ||||
|   [{ font: [] }], // 字体种类
 | ||||
|   [{ align: [] }], // 对齐方式
 | ||||
|   ['clean'], // 清除文本格式
 | ||||
|   ['link', 'image', 'video'] // 链接、图片、视频
 | ||||
| ] | ||||
| 
 | ||||
| export default { | ||||
|   theme: 'snow', | ||||
|   placeholder: '请输入文章内容', | ||||
|   modules: { | ||||
|     toolbar: { | ||||
|       container: toolbarOptions, | ||||
|       // container: "#toolbar",
 | ||||
|       handlers: { | ||||
|         image: function (value) { | ||||
|           if (value) { | ||||
|             // 触发input框选择图片文件
 | ||||
|             document.querySelector('.avatar-uploader input').click() | ||||
|           } else { | ||||
|             this.quill.format('image', false) | ||||
|           } | ||||
|         }, | ||||
|         link: function (value) { | ||||
|           if (value) { | ||||
|             const href = prompt('注意!只支持公众号图文链接') | ||||
|             this.quill.format('link', href) | ||||
|           } else { | ||||
|             this.quill.format('link', false) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -231,7 +231,7 @@ | |||
|                     :on-success="handleUploadSuccess" | ||||
|                   > | ||||
|                     <template #trigger> | ||||
|                       <el-button type="text">本地上传</el-button> | ||||
|                       <el-button type="primary" link>本地上传</el-button> | ||||
|                     </template> | ||||
|                     <el-button type="primary" link @click="openMaterial" style="margin-left: 5px" | ||||
|                       >素材库选择 | ||||
|  |  | |||
|  | @ -0,0 +1,75 @@ | |||
| import { IEditorConfig } from '@wangeditor/editor' | ||||
| import { getAccessToken, getTenantId } from '@/utils/auth' | ||||
| 
 | ||||
| const message = useMessage() | ||||
| 
 | ||||
| type InsertFnType = (url: string, alt: string, href: string) => void | ||||
| 
 | ||||
| export const createEditorConfig = ( | ||||
|   server: string, | ||||
|   accountId: number | undefined | ||||
| ): Partial<IEditorConfig> => { | ||||
|   return { | ||||
|     MENU_CONF: { | ||||
|       ['uploadImage']: { | ||||
|         server, | ||||
|         // 单个文件的最大体积限制,默认为 2M
 | ||||
|         maxFileSize: 5 * 1024 * 1024, | ||||
|         // 最多可上传几个文件,默认为 100
 | ||||
|         maxNumberOfFiles: 10, | ||||
|         // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
 | ||||
|         allowedFileTypes: ['image/*'], | ||||
| 
 | ||||
|         // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
 | ||||
|         meta: { | ||||
|           accountId: accountId, | ||||
|           type: 'image' | ||||
|         }, | ||||
|         // 将 meta 拼接到 url 参数中,默认 false
 | ||||
|         metaWithUrl: true, | ||||
| 
 | ||||
|         // 自定义增加 http  header
 | ||||
|         headers: { | ||||
|           Accept: '*', | ||||
|           Authorization: 'Bearer ' + getAccessToken(), | ||||
|           'tenant-id': getTenantId() | ||||
|         }, | ||||
| 
 | ||||
|         // 跨域是否传递 cookie ,默认为 false
 | ||||
|         withCredentials: true, | ||||
| 
 | ||||
|         // 超时时间,默认为 10 秒
 | ||||
|         timeout: 5 * 1000, // 5 秒
 | ||||
| 
 | ||||
|         // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
 | ||||
|         fieldName: 'file', | ||||
| 
 | ||||
|         // 上传之前触发
 | ||||
|         onBeforeUpload(file: File) { | ||||
|           console.log(file) | ||||
|           return file | ||||
|         }, | ||||
|         // 上传进度的回调函数
 | ||||
|         onProgress(progress: number) { | ||||
|           // progress 是 0-100 的数字
 | ||||
|           console.log('progress', progress) | ||||
|         }, | ||||
|         onSuccess(file: File, res: any) { | ||||
|           console.log('onSuccess', file, res) | ||||
|         }, | ||||
|         onFailed(file: File, res: any) { | ||||
|           message.alertError(res.message) | ||||
|           console.log('onFailed', file, res) | ||||
|         }, | ||||
|         onError(file: File, err: any, res: any) { | ||||
|           message.alertError(err.message) | ||||
|           console.error('onError', file, err, res) | ||||
|         }, | ||||
|         // 自定义插入图片
 | ||||
|         customInsert(res: any, insertFn: InsertFnType) { | ||||
|           insertFn(res.data.url, 'image', res.data.url) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -11,7 +11,7 @@ | |||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item> | ||||
|         <el-button type="primary" plain @click="handleAdd" v-hasPermi="['mp:draft:create']"> | ||||
|  | @ -71,168 +71,182 @@ | |||
|     <el-dialog | ||||
|       :title="operateMaterial === 'add' ? '新建图文' : '修改图文'" | ||||
|       width="80%" | ||||
|       top="20px" | ||||
|       v-model="dialogNewsVisible" | ||||
|       :before-close="dialogNewsClose" | ||||
|       :close-on-click-modal="false" | ||||
|       destroy-on-close | ||||
|     > | ||||
|       <div class="left"> | ||||
|         <div class="select-item"> | ||||
|           <div v-for="(news, index) in articlesAdd" :key="news.id"> | ||||
|             <div | ||||
|               class="news-main father" | ||||
|               v-if="index === 0" | ||||
|               :class="{ activeAddNews: isActiveAddNews === index }" | ||||
|               @click="activeNews(index)" | ||||
|             > | ||||
|               <div class="news-content"> | ||||
|                 <img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" /> | ||||
|                 <div class="news-content-title">{{ news.title }}</div> | ||||
|               </div> | ||||
|               <div class="child" v-if="articlesAdd.length > 1"> | ||||
|                 <el-button size="small" @click="downNews(index)" | ||||
|                   ><Icon icon="ep:sort-down" />下移</el-button | ||||
|                 > | ||||
|                 <el-button v-if="operateMaterial === 'add'" size="small" @click="minusNews(index)" | ||||
|                   ><Icon icon="ep:delete" />删除 | ||||
|                 </el-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div | ||||
|               class="news-main-item father" | ||||
|               v-if="index > 0" | ||||
|               :class="{ activeAddNews: isActiveAddNews === index }" | ||||
|               @click="activeNews(index)" | ||||
|             > | ||||
|               <div class="news-content-item"> | ||||
|                 <div class="news-content-item-title">{{ news.title }}</div> | ||||
|                 <div class="news-content-item-img"> | ||||
|                   <img | ||||
|                     class="material-img" | ||||
|                     v-if="news.thumbUrl" | ||||
|                     :src="news.thumbUrl" | ||||
|                     height="100%" | ||||
|                   /> | ||||
|       <el-container> | ||||
|         <el-aside width="40%"> | ||||
|           <div class="select-item"> | ||||
|             <div v-for="(news, index) in articlesAdd" :key="news.id"> | ||||
|               <div | ||||
|                 class="news-main father" | ||||
|                 v-if="index === 0" | ||||
|                 :class="{ activeAddNews: isActiveAddNews === index }" | ||||
|                 @click="activeNews(index)" | ||||
|               > | ||||
|                 <div class="news-content"> | ||||
|                   <img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" /> | ||||
|                   <div class="news-content-title">{{ news.title }}</div> | ||||
|                 </div> | ||||
|                 <div class="child" v-if="articlesAdd.length > 1"> | ||||
|                   <el-button size="small" @click="downNews(index)" | ||||
|                     ><Icon icon="ep:sort-down" />下移</el-button | ||||
|                   > | ||||
|                   <el-button v-if="operateMaterial === 'add'" size="small" @click="minusNews(index)" | ||||
|                     ><Icon icon="ep:delete" />删除 | ||||
|                   </el-button> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="child"> | ||||
|                 <el-button | ||||
|                   v-if="articlesAdd.length > index + 1" | ||||
|                   size="small" | ||||
|                   @click="downNews(index)" | ||||
|                   ><Icon icon="ep:sort-down" />下移 | ||||
|                 </el-button> | ||||
|                 <el-button size="small" @click="upNews(index)" | ||||
|                   ><Icon icon="ep:sort-up" />上移</el-button | ||||
|                 > | ||||
|                 <el-button | ||||
|                   v-if="operateMaterial === 'add'" | ||||
|                   type="danger" | ||||
|                   size="small" | ||||
|                   @click="minusNews(index)" | ||||
|                   ><Icon icon="ep:delete" />删除 | ||||
|                 </el-button> | ||||
|               <div | ||||
|                 class="news-main-item father" | ||||
|                 v-if="index > 0" | ||||
|                 :class="{ activeAddNews: isActiveAddNews === index }" | ||||
|                 @click="activeNews(index)" | ||||
|               > | ||||
|                 <div class="news-content-item"> | ||||
|                   <div class="news-content-item-title">{{ news.title }}</div> | ||||
|                   <div class="news-content-item-img"> | ||||
|                     <img | ||||
|                       class="material-img" | ||||
|                       v-if="news.thumbUrl" | ||||
|                       :src="news.thumbUrl" | ||||
|                       width="100%" | ||||
|                     /> | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 <div class="child"> | ||||
|                   <el-button | ||||
|                     v-if="articlesAdd.length > index + 1" | ||||
|                     size="small" | ||||
|                     @click="downNews(index)" | ||||
|                     ><Icon icon="ep:sort-down" />下移 | ||||
|                   </el-button> | ||||
|                   <el-button size="small" @click="upNews(index)" | ||||
|                     ><Icon icon="ep:sort-up" />上移</el-button | ||||
|                   > | ||||
|                   <el-button | ||||
|                     v-if="operateMaterial === 'add'" | ||||
|                     type="danger" | ||||
|                     size="small" | ||||
|                     @click="minusNews(index)" | ||||
|                     ><Icon icon="ep:delete" />删除 | ||||
|                   </el-button> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <el-row justify="center" class="ope-row"> | ||||
|             <el-button | ||||
|               type="primary" | ||||
|               circle | ||||
|               @click="plusNews" | ||||
|               v-if="articlesAdd.length < 8 && operateMaterial === 'add'" | ||||
|             > | ||||
|               <Icon icon="ep:plus" /> | ||||
|             </el-button> | ||||
|           </el-row> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0"> | ||||
|         <br /> | ||||
|         <br /> | ||||
|         <br /> | ||||
|         <br /> | ||||
|         <!-- 标题、作者、原文地址 --> | ||||
|         <el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" /> | ||||
|         <el-input | ||||
|           v-model="articlesAdd[isActiveAddNews].author" | ||||
|           placeholder="请输入作者" | ||||
|           style="margin-top: 5px" | ||||
|         /> | ||||
|         <el-input | ||||
|           v-model="articlesAdd[isActiveAddNews].contentSourceUrl" | ||||
|           placeholder="请输入原文地址" | ||||
|           style="margin-top: 5px" | ||||
|         /> | ||||
|         <!-- 封面和摘要 --> | ||||
|         <div class="input-tt">封面和摘要:</div> | ||||
|         <div> | ||||
|           <div class="thumb-div"> | ||||
|             <img | ||||
|               class="material-img" | ||||
|               v-if="articlesAdd[isActiveAddNews].thumbUrl" | ||||
|               :src="articlesAdd[isActiveAddNews].thumbUrl" | ||||
|               :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'" | ||||
|             /> | ||||
|             <Icon | ||||
|               v-else | ||||
|               icon="ep:plus" | ||||
|               class="avatar-uploader-icon" | ||||
|               :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'" | ||||
|             /> | ||||
|             <div class="thumb-but"> | ||||
|               <el-upload | ||||
|                 :action="uploadUrl" | ||||
|                 :headers="headers" | ||||
|                 multiple | ||||
|                 :limit="1" | ||||
|                 :file-list="fileList" | ||||
|                 :data="uploadData" | ||||
|                 :before-upload="beforeThumbImageUpload" | ||||
|                 :on-success="handleUploadSuccess" | ||||
|             <el-row justify="center" class="ope-row"> | ||||
|               <el-button | ||||
|                 type="primary" | ||||
|                 circle | ||||
|                 @click="plusNews" | ||||
|                 v-if="articlesAdd.length < 8 && operateMaterial === 'add'" | ||||
|               > | ||||
|                 <template #trigger> | ||||
|                   <el-button size="small" type="primary">本地上传</el-button> | ||||
|                 </template> | ||||
|                 <el-button | ||||
|                   size="small" | ||||
|                   type="primary" | ||||
|                   @click="openMaterial" | ||||
|                   style="margin-left: 5px" | ||||
|                   >素材库选择</el-button | ||||
|                 > | ||||
|                 <template #tip> | ||||
|                   <div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div> | ||||
|                 </template> | ||||
|               </el-upload> | ||||
|             </div> | ||||
|             <el-dialog title="选择图片" v-model="dialogImageVisible" width="80%" append-to-body> | ||||
|               <WxMaterialSelect | ||||
|                 ref="materialSelectRef" | ||||
|                 :objData="{ type: 'image', accountId: queryParams.accountId }" | ||||
|                 @select-material="selectMaterial" | ||||
|               /> | ||||
|             </el-dialog> | ||||
|                 <Icon icon="ep:plus" /> | ||||
|               </el-button> | ||||
|             </el-row> | ||||
|           </div> | ||||
|           <el-input | ||||
|             :rows="8" | ||||
|             type="textarea" | ||||
|             v-model="articlesAdd[isActiveAddNews].digest" | ||||
|             placeholder="请输入摘要" | ||||
|             class="digest" | ||||
|             maxlength="120" | ||||
|             style="float: right" | ||||
|           /> | ||||
|         </div> | ||||
|         <!--富文本编辑器组件--> | ||||
|         <el-row> | ||||
|           <WxEditor | ||||
|             v-model="articlesAdd[isActiveAddNews].content" | ||||
|             :account-id="uploadData.accountId" | ||||
|             v-if="hackResetEditor" | ||||
|           /> | ||||
|         </el-row> | ||||
|       </div> | ||||
|         </el-aside> | ||||
|         <el-main> | ||||
|           <div class="" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0"> | ||||
|             <!-- 标题、作者、原文地址 --> | ||||
|             <el-row :gutter="20"> | ||||
|               <el-input | ||||
|                 v-model="articlesAdd[isActiveAddNews].title" | ||||
|                 placeholder="请输入标题(必填)" | ||||
|               /> | ||||
|               <el-input | ||||
|                 v-model="articlesAdd[isActiveAddNews].author" | ||||
|                 placeholder="请输入作者" | ||||
|                 style="margin-top: 5px" | ||||
|               /> | ||||
|               <el-input | ||||
|                 v-model="articlesAdd[isActiveAddNews].contentSourceUrl" | ||||
|                 placeholder="请输入原文地址" | ||||
|                 style="margin-top: 5px" | ||||
|               /> | ||||
|             </el-row> | ||||
|             <!-- 封面和摘要 --> | ||||
|             <el-row :gutter="20"> | ||||
|               <el-col :span="12"> | ||||
|                 <p>封面:</p> | ||||
|                 <div class="thumb-div"> | ||||
|                   <el-image | ||||
|                     v-if="articlesAdd[isActiveAddNews].thumbUrl" | ||||
|                     style="width: 300px; max-height: 300px" | ||||
|                     :src="articlesAdd[isActiveAddNews].thumbUrl" | ||||
|                     fit="contain" | ||||
|                   /> | ||||
|                   <Icon | ||||
|                     v-else | ||||
|                     icon="ep:plus" | ||||
|                     class="avatar-uploader-icon" | ||||
|                     :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'" | ||||
|                   /> | ||||
|                   <div class="thumb-but"> | ||||
|                     <el-upload | ||||
|                       :action="uploadUrl" | ||||
|                       :headers="headers" | ||||
|                       multiple | ||||
|                       :limit="1" | ||||
|                       :file-list="fileList" | ||||
|                       :data="uploadData" | ||||
|                       :before-upload="beforeThumbImageUpload" | ||||
|                       :on-success="handleUploadSuccess" | ||||
|                     > | ||||
|                       <template #trigger> | ||||
|                         <el-button size="small" type="primary">本地上传</el-button> | ||||
|                       </template> | ||||
|                       <el-button | ||||
|                         size="small" | ||||
|                         type="primary" | ||||
|                         @click="openMaterial" | ||||
|                         style="margin-left: 5px" | ||||
|                         >素材库选择</el-button | ||||
|                       > | ||||
|                       <template #tip> | ||||
|                         <div class="el-upload__tip" | ||||
|                           >支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div | ||||
|                         > | ||||
|                       </template> | ||||
|                     </el-upload> | ||||
|                   </div> | ||||
|                   <el-dialog | ||||
|                     title="选择图片" | ||||
|                     v-model="dialogImageVisible" | ||||
|                     width="80%" | ||||
|                     append-to-body | ||||
|                   > | ||||
|                     <WxMaterialSelect | ||||
|                       ref="materialSelectRef" | ||||
|                       :objData="{ type: 'image', accountId: queryParams.accountId }" | ||||
|                       @select-material="selectMaterial" | ||||
|                     /> | ||||
|                   </el-dialog> | ||||
|                 </div> | ||||
|               </el-col> | ||||
|               <el-col :span="12"> | ||||
|                 <p>摘要:</p> | ||||
|                 <el-input | ||||
|                   :rows="8" | ||||
|                   type="textarea" | ||||
|                   v-model="articlesAdd[isActiveAddNews].digest" | ||||
|                   placeholder="请输入摘要" | ||||
|                   class="digest" | ||||
|                   maxlength="120" | ||||
|                 /> | ||||
|               </el-col> | ||||
|             </el-row> | ||||
|             <!--富文本编辑器组件--> | ||||
|             <el-row> | ||||
|               <Editor | ||||
|                 v-model="articlesAdd[isActiveAddNews].content" | ||||
|                 :editor-config="editorConfig" | ||||
|               /> | ||||
|             </el-row> | ||||
|           </div> | ||||
|         </el-main> | ||||
|       </el-container> | ||||
|       <template #footer> | ||||
|         <el-button @click="dialogNewsVisible = false">取 消</el-button> | ||||
|         <el-button type="primary" @click="submitForm">提 交</el-button> | ||||
|  | @ -242,16 +256,18 @@ | |||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts" name="MpDraft"> | ||||
| import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue' | ||||
| import { Editor } from '@/components/Editor' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import { getAccessToken } from '@/utils/auth' | ||||
| import * as MpDraftApi from '@/api/mp/draft' | ||||
| import * as MpFreePublishApi from '@/api/mp/freePublish' | ||||
| import type { UploadFiles, UploadProps, UploadRawFile } from 'element-plus' | ||||
| // 可以用改本地数据模拟,避免API调用超限 | ||||
| // import drafts from './mock' | ||||
| import { createEditorConfig } from './editor-config' | ||||
| // import drafts from './mock' // 可以用改本地数据模拟,避免API调用超限 | ||||
| import { IEditorConfig } from '@wangeditor/editor' | ||||
| 
 | ||||
| const message = useMessage() // 消息 | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
|  | @ -285,14 +301,25 @@ const uploadData: UploadData = reactive({ | |||
| }) | ||||
| 
 | ||||
| // ========== 草稿新建 or 修改 ========== | ||||
| interface Article { | ||||
|   title: string | ||||
|   thumbMediaId: string | ||||
|   author: string | ||||
|   digest: string | ||||
|   showCoverPic: string | ||||
|   content: string | ||||
|   contentSourceUrl: string | ||||
|   needOpenComment: string | ||||
|   onlyFansCanComment: string | ||||
|   thumbUrl: string | ||||
| } | ||||
| const dialogNewsVisible = ref(false) | ||||
| const addMaterialLoading = ref(false) // 添加草稿的 loading 标识 | ||||
| const articlesAdd = ref<any[]>([]) | ||||
| const articlesAdd = ref<Article[]>([]) | ||||
| const isActiveAddNews = ref(0) | ||||
| const dialogImageVisible = ref(false) | ||||
| const operateMaterial = ref<'add' | 'edit'>('add') | ||||
| const articlesMediaId = ref('') | ||||
| const hackResetEditor = ref(false) | ||||
| 
 | ||||
| /** 侦听公众号变化 **/ | ||||
| const onAccountChanged = (id?: number) => { | ||||
|  | @ -305,8 +332,11 @@ const onAccountChanged = (id?: number) => { | |||
| const setAccountId = (id?: number) => { | ||||
|   queryParams.accountId = id | ||||
|   uploadData.accountId = id | ||||
|   editorConfig.value = createEditorConfig(uploadUrl, queryParams.accountId) | ||||
| } | ||||
| 
 | ||||
| const editorConfig = ref<Partial<IEditorConfig>>({}) | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|  | @ -329,7 +359,6 @@ const getList = async () => { | |||
| // ======================== 新增/修改草稿 ======================== | ||||
| /** 新增按钮操作 */ | ||||
| const handleAdd = () => { | ||||
|   resetEditor() | ||||
|   reset() | ||||
|   // 打开表单,并设置初始化 | ||||
|   operateMaterial.value = 'add' | ||||
|  | @ -338,7 +367,6 @@ const handleAdd = () => { | |||
| 
 | ||||
| /** 更新按钮操作 */ | ||||
| const handleUpdate = (item: any) => { | ||||
|   resetEditor() | ||||
|   reset() | ||||
|   articlesMediaId.value = item.mediaId | ||||
|   articlesAdd.value = JSON.parse(JSON.stringify(item.content.newsItem)) | ||||
|  | @ -370,7 +398,6 @@ const dialogNewsClose = async (onDone: () => {}) => { | |||
|   try { | ||||
|     await message.confirm('修改内容可能还未保存,确定关闭吗?') | ||||
|     reset() | ||||
|     resetEditor() | ||||
|     onDone() | ||||
|   } catch {} | ||||
| } | ||||
|  | @ -381,14 +408,6 @@ const reset = () => { | |||
|   articlesAdd.value = [buildEmptyArticle()] | ||||
| } | ||||
| 
 | ||||
| // 表单 Editor 重置 | ||||
| const resetEditor = () => { | ||||
|   hackResetEditor.value = false // 销毁组件 | ||||
|   nextTick(() => { | ||||
|     hackResetEditor.value = true // 重建组件 | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| // 将图文向下移动 | ||||
| const downNews = (index: number) => { | ||||
|   let temp = articlesAdd.value[index] | ||||
|  | @ -398,8 +417,8 @@ const downNews = (index: number) => { | |||
| } | ||||
| 
 | ||||
| // 将图文向上移动 | ||||
| const upNews = (index) => { | ||||
|   let temp = articlesAdd.value[index] | ||||
| const upNews = (index: number) => { | ||||
|   const temp = articlesAdd.value[index] | ||||
|   articlesAdd.value[index] = articlesAdd.value[index - 1] | ||||
|   articlesAdd.value[index - 1] = temp | ||||
|   isActiveAddNews.value = index - 1 | ||||
|  | @ -407,7 +426,6 @@ const upNews = (index) => { | |||
| 
 | ||||
| // 选中指定 index 的图文 | ||||
| const activeNews = (index: number) => { | ||||
|   resetEditor() | ||||
|   isActiveAddNews.value = index | ||||
| } | ||||
| 
 | ||||
|  | @ -429,7 +447,7 @@ const plusNews = () => { | |||
| } | ||||
| 
 | ||||
| // 创建空的 article | ||||
| const buildEmptyArticle = () => { | ||||
| const buildEmptyArticle = (): Article => { | ||||
|   return { | ||||
|     title: '', | ||||
|     thumbMediaId: '', | ||||
|  | @ -455,8 +473,8 @@ const beforeThumbImageUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawF | |||
|     addMaterialLoading.value = false | ||||
|     return false | ||||
|   } | ||||
|   const isLt = rawFile.size / 1024 / 1024 < 2 | ||||
|   if (!isLt) { | ||||
| 
 | ||||
|   if (rawFile.size / 1024 / 1024 > 2) { | ||||
|     message.error('上传图片大小不能超过 2M!') | ||||
|     addMaterialLoading.value = false | ||||
|     return false | ||||
|  | @ -465,10 +483,10 @@ const beforeThumbImageUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawF | |||
|   return true | ||||
| } | ||||
| 
 | ||||
| const handleUploadSuccess: UploadProps['onSuccess'] = (response: any) => { | ||||
| const handleUploadSuccess: UploadProps['onSuccess'] = (res: any) => { | ||||
|   addMaterialLoading.value = false | ||||
|   if (response.code !== 0) { | ||||
|     message.error('上传出错:' + response.msg) | ||||
|   if (res.code !== 0) { | ||||
|     message.error('上传出错:' + res.msg) | ||||
|     return false | ||||
|   } | ||||
| 
 | ||||
|  | @ -476,8 +494,8 @@ const handleUploadSuccess: UploadProps['onSuccess'] = (response: any) => { | |||
|   fileList.value = [] | ||||
| 
 | ||||
|   // 设置草稿的封面字段 | ||||
|   articlesAdd.value[isActiveAddNews.value].thumbMediaId = response.data.mediaId | ||||
|   articlesAdd.value[isActiveAddNews.value].thumbUrl = response.data.url | ||||
|   articlesAdd.value[isActiveAddNews.value].thumbMediaId = res.data.mediaId | ||||
|   articlesAdd.value[isActiveAddNews.value].thumbUrl = res.data.url | ||||
| } | ||||
| 
 | ||||
| // 选择 or 上传完素材,设置回草稿 | ||||
|  | @ -502,7 +520,7 @@ const handlePublish = async (item: any) => { | |||
|     await message.confirm(content) | ||||
|     await MpFreePublishApi.submitFreePublish(accountId, mediaId) | ||||
|     message.notifySuccess('发布成功') | ||||
|     getList() | ||||
|     await getList() | ||||
|   } catch {} | ||||
| } | ||||
| 
 | ||||
|  | @ -514,7 +532,7 @@ const handleDelete = async (item: any) => { | |||
|     await message.confirm('此操作将永久删除该草稿, 是否继续?') | ||||
|     await MpDraftApi.deleteDraft(accountId, mediaId) | ||||
|     message.notifySuccess('删除成功') | ||||
|     getList() | ||||
|     await getList() | ||||
|   } catch {} | ||||
| } | ||||
| </script> | ||||
|  | @ -535,6 +553,13 @@ const handleDelete = async (item: any) => { | |||
|   padding-top: 5px; | ||||
| } | ||||
| 
 | ||||
| .el-row { | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| .el-row:last-child { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| 
 | ||||
| .item-name { | ||||
|   font-size: 12px; | ||||
|   overflow: hidden; | ||||
|  | @ -548,35 +573,33 @@ const handleDelete = async (item: any) => { | |||
| } | ||||
| 
 | ||||
| /*新增图文*/ | ||||
| .left { | ||||
|   display: inline-block; | ||||
|   width: 35%; | ||||
|   vertical-align: top; | ||||
|   margin-top: 200px; | ||||
| } | ||||
| // .left { | ||||
| //   display: inline-block; | ||||
| //   vertical-align: top; | ||||
| //   margin-top: 200px; | ||||
| // } | ||||
| 
 | ||||
| .right { | ||||
|   display: inline-block; | ||||
|   width: 60%; | ||||
|   margin-top: -40px; | ||||
| } | ||||
| // .right { | ||||
| //   display: inline-block; | ||||
| //   width: 100%; | ||||
| // } | ||||
| 
 | ||||
| .avatar-uploader { | ||||
|   width: 20%; | ||||
|   display: inline-block; | ||||
| } | ||||
| // .avatar-uploader { | ||||
| //   width: 20%; | ||||
| //   display: inline-block; | ||||
| // } | ||||
| 
 | ||||
| .avatar-uploader .el-upload { | ||||
|   border-radius: 6px; | ||||
|   cursor: pointer; | ||||
|   position: relative; | ||||
|   overflow: hidden; | ||||
|   text-align: unset !important; | ||||
| } | ||||
| // .avatar-uploader .el-upload { | ||||
| //   border-radius: 6px; | ||||
| //   cursor: pointer; | ||||
| //   position: relative; | ||||
| //   overflow: hidden; | ||||
| //   text-align: unset !important; | ||||
| // } | ||||
| 
 | ||||
| .avatar-uploader .el-upload:hover { | ||||
|   border-color: #165dff; | ||||
| } | ||||
| // .avatar-uploader .el-upload:hover { | ||||
| //   border-color: #165dff; | ||||
| // } | ||||
| 
 | ||||
| .avatar-uploader-icon { | ||||
|   border: 1px solid #d9d9d9; | ||||
|  | @ -599,7 +622,7 @@ const handleDelete = async (item: any) => { | |||
| } | ||||
| 
 | ||||
| .digest { | ||||
|   width: 60%; | ||||
|   width: 100%; | ||||
|   display: inline-block; | ||||
|   vertical-align: top; | ||||
| } | ||||
|  | @ -620,28 +643,16 @@ const handleDelete = async (item: any) => { | |||
|   border: 1px solid #eaeaea; | ||||
| } | ||||
| 
 | ||||
| p { | ||||
|   line-height: 30px; | ||||
| } | ||||
| 
 | ||||
| @media (min-width: 992px) and (max-width: 1300px) { | ||||
|   .waterfall { | ||||
|     column-count: 3; | ||||
|   } | ||||
| 
 | ||||
|   p { | ||||
|     color: red; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media (min-width: 768px) and (max-width: 991px) { | ||||
|   .waterfall { | ||||
|     column-count: 2; | ||||
|   } | ||||
| 
 | ||||
|   p { | ||||
|     color: orange; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 767px) { | ||||
|  | @ -707,10 +718,6 @@ p { | |||
|   background-color: #acadae; | ||||
| } | ||||
| 
 | ||||
| .input-tt { | ||||
|   padding: 5px; | ||||
| } | ||||
| 
 | ||||
| .activeAddNews { | ||||
|   border: 5px solid #2bb673; | ||||
| } | ||||
|  | @ -747,7 +754,7 @@ p { | |||
| 
 | ||||
| .thumb-div { | ||||
|   display: inline-block; | ||||
|   width: 30%; | ||||
|   width: 100%; | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
|  | @ -51,8 +51,7 @@ | |||
| <script lang="ts" setup name="MpFreePublish"> | ||||
| import * as FreePublishApi from '@/api/mp/freePublish' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| 
 | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
|  | @ -102,7 +101,6 @@ const handleDelete = async (item: any) => { | |||
|   } catch {} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .ope-row { | ||||
|   margin-top: 5px; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|   <ContentWrap> | ||||
|     <el-form class="-mb-15px" :inline="true" label-width="68px"> | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
|  | @ -248,7 +248,7 @@ | |||
| <script lang="ts" setup name="MpMaterial"> | ||||
| import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' | ||||
| import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import * as MpMaterialApi from '@/api/mp/material' | ||||
| import * as authUtil from '@/utils/auth' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|   <ContentWrap> | ||||
|     <el-form class="-mb-15px" ref="queryFormRef" :inline="true" label-width="68px"> | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
|  | @ -192,7 +192,7 @@ | |||
| import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import * as MpMenuApi from '@/api/mp/menu' | ||||
| import { handleTree } from '@/utils/tree' | ||||
| import menuOptions from './menuOptions' | ||||
|  |  | |||
|  | @ -0,0 +1,145 @@ | |||
| <template> | ||||
|   <div> | ||||
|     <el-table v-loading="props.loading" :data="props.list"> | ||||
|       <el-table-column | ||||
|         label="发送时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         width="180" | ||||
|         :formatter="dateFormatter" | ||||
|       /> | ||||
|       <el-table-column label="消息类型" align="center" prop="type" width="80" /> | ||||
|       <el-table-column label="发送方" align="center" prop="sendFrom" width="80"> | ||||
|         <template #default="scope"> | ||||
|           <el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag> | ||||
|           <el-tag v-else type="info">公众号</el-tag> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="用户标识" align="center" prop="openid" width="300" /> | ||||
|       <el-table-column label="内容" prop="content"> | ||||
|         <template #default="scope"> | ||||
|           <!-- 【事件】区域 --> | ||||
|           <div v-if="scope.row.type === MsgType.Event && scope.row.event === 'subscribe'"> | ||||
|             <el-tag type="success">关注</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'unsubscribe'"> | ||||
|             <el-tag type="danger">取消关注</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'CLICK'"> | ||||
|             <el-tag>点击菜单</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'VIEW'"> | ||||
|             <el-tag>点击菜单链接</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_waitmsg'" | ||||
|           > | ||||
|             <el-tag>扫码结果</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_push'"> | ||||
|             <el-tag>扫码结果</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_sysphoto'"> | ||||
|             <el-tag>系统拍照发图</el-tag> | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_photo_or_album'" | ||||
|           > | ||||
|             <el-tag>拍照或者相册</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_weixin'"> | ||||
|             <el-tag>微信相册</el-tag> | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'location_select'" | ||||
|           > | ||||
|             <el-tag>选择地理位置</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event"> | ||||
|             <el-tag type="danger">未知事件类型</el-tag> | ||||
|           </div> | ||||
|           <!-- 【消息】区域 --> | ||||
|           <div v-else-if="scope.row.type === MsgType.Text">{{ scope.row.content }}</div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Voice"> | ||||
|             <wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Image"> | ||||
|             <a target="_blank" :href="scope.row.mediaUrl"> | ||||
|               <img :src="scope.row.mediaUrl" style="width: 100px" /> | ||||
|             </a> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Video || scope.row.type === 'shortvideo'"> | ||||
|             <wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Link"> | ||||
|             <el-tag>链接</el-tag> | ||||
|             : | ||||
|             <a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Location"> | ||||
|             <WxLocation | ||||
|               :label="scope.row.label" | ||||
|               :location-y="scope.row.locationY" | ||||
|               :location-x="scope.row.locationX" | ||||
|             /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Music"> | ||||
|             <WxMusic | ||||
|               :title="scope.row.title" | ||||
|               :description="scope.row.description" | ||||
|               :thumb-media-url="scope.row.thumbMediaUrl" | ||||
|               :music-url="scope.row.musicUrl" | ||||
|               :hq-music-url="scope.row.hqMusicUrl" | ||||
|             /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.News"> | ||||
|             <WxNews :articles="scope.row.articles" /> | ||||
|           </div> | ||||
|           <div v-else> | ||||
|             <el-tag type="danger">未知消息类型</el-tag> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="emit('send', scope.row.userId)" | ||||
|             v-hasPermi="['mp:message:send']" | ||||
|           > | ||||
|             消息 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 分页组件 --> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' | ||||
| import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' | ||||
| import WxLocation from '@/views/mp/components/wx-location/main.vue' | ||||
| import WxMusic from '@/views/mp/components/wx-music/main.vue' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import { MsgType } from '@/views/mp/components/wx-msg/types' | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   list: { | ||||
|     type: Array, | ||||
|     required: true | ||||
|   }, | ||||
|   loading: { | ||||
|     type: Boolean, | ||||
|     required: true | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const emit = defineEmits<{ (e: 'send', v: number) }>() | ||||
| </script> | ||||
|  | @ -9,7 +9,7 @@ | |||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="消息类型" prop="type"> | ||||
|         <el-select v-model="queryParams.type" placeholder="请选择消息类型" class="!w-240px"> | ||||
|  | @ -58,124 +58,7 @@ | |||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap> | ||||
|     <el-table v-loading="loading" :data="list"> | ||||
|       <el-table-column | ||||
|         label="发送时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         width="180" | ||||
|         :formatter="dateFormatter" | ||||
|       /> | ||||
|       <el-table-column label="消息类型" align="center" prop="type" width="80" /> | ||||
|       <el-table-column label="发送方" align="center" prop="sendFrom" width="80"> | ||||
|         <template #default="scope"> | ||||
|           <el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag> | ||||
|           <el-tag v-else type="info">公众号</el-tag> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="用户标识" align="center" prop="openid" width="300" /> | ||||
|       <el-table-column label="内容" prop="content"> | ||||
|         <template #default="scope"> | ||||
|           <!-- 【事件】区域 --> | ||||
|           <div v-if="scope.row.type === MsgType.Event && scope.row.event === 'subscribe'"> | ||||
|             <el-tag type="success">关注</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'unsubscribe'"> | ||||
|             <el-tag type="danger">取消关注</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'CLICK'"> | ||||
|             <el-tag>点击菜单</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'VIEW'"> | ||||
|             <el-tag>点击菜单链接</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_waitmsg'" | ||||
|           > | ||||
|             <el-tag>扫码结果</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_push'"> | ||||
|             <el-tag>扫码结果</el-tag> | ||||
|             【{{ scope.row.eventKey }}】 | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_sysphoto'"> | ||||
|             <el-tag>系统拍照发图</el-tag> | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_photo_or_album'" | ||||
|           > | ||||
|             <el-tag>拍照或者相册</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_weixin'"> | ||||
|             <el-tag>微信相册</el-tag> | ||||
|           </div> | ||||
|           <div | ||||
|             v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'location_select'" | ||||
|           > | ||||
|             <el-tag>选择地理位置</el-tag> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Event"> | ||||
|             <el-tag type="danger">未知事件类型</el-tag> | ||||
|           </div> | ||||
|           <!-- 【消息】区域 --> | ||||
|           <div v-else-if="scope.row.type === MsgType.Text">{{ scope.row.content }}</div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Voice"> | ||||
|             <wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Image"> | ||||
|             <a target="_blank" :href="scope.row.mediaUrl"> | ||||
|               <img :src="scope.row.mediaUrl" style="width: 100px" /> | ||||
|             </a> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Video || scope.row.type === 'shortvideo'"> | ||||
|             <wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Link"> | ||||
|             <el-tag>链接</el-tag> | ||||
|             : | ||||
|             <a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Location"> | ||||
|             <WxLocation | ||||
|               :label="scope.row.label" | ||||
|               :location-y="scope.row.locationY" | ||||
|               :location-x="scope.row.locationX" | ||||
|             /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.Music"> | ||||
|             <WxMusic | ||||
|               :title="scope.row.title" | ||||
|               :description="scope.row.description" | ||||
|               :thumb-media-url="scope.row.thumbMediaUrl" | ||||
|               :music-url="scope.row.musicUrl" | ||||
|               :hq-music-url="scope.row.hqMusicUrl" | ||||
|             /> | ||||
|           </div> | ||||
|           <div v-else-if="scope.row.type === MsgType.News"> | ||||
|             <WxNews :articles="scope.row.articles" /> | ||||
|           </div> | ||||
|           <div v-else> | ||||
|             <el-tag type="danger">未知消息类型</el-tag> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="handleSend(scope.row)" | ||||
|             v-hasPermi="['mp:message:send']" | ||||
|           > | ||||
|             消息 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 分页组件 --> | ||||
|     <MessageTable :list="list" :loading="loading" @send="handleSend" /> | ||||
|     <Pagination | ||||
|       v-show="total > 0" | ||||
|       :total="total" | ||||
|  | @ -183,37 +66,31 @@ | |||
|       v-model:limit="queryParams.pageSize" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
| 
 | ||||
|     <!-- 发送消息的弹窗 --> | ||||
|     <el-dialog | ||||
|       title="粉丝消息列表" | ||||
|       v-model="showMessageBox" | ||||
|       @click="showMessageBox = true" | ||||
|       width="50%" | ||||
|     > | ||||
|       <template #footer> | ||||
|         <WxMsg :user-id="userId" v-if="showMessageBox" /> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 发送消息的弹窗 --> | ||||
|   <el-dialog | ||||
|     title="粉丝消息列表" | ||||
|     v-model="messageBox.show" | ||||
|     @click="messageBox.show = true" | ||||
|     width="50%" | ||||
|     destroy-on-close | ||||
|   > | ||||
|     <WxMsg :user-id="messageBox.userId" /> | ||||
|   </el-dialog> | ||||
| </template> | ||||
| <script setup lang="ts" name="MpMessage"> | ||||
| import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' | ||||
| import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' | ||||
| import WxMsg from '@/views/mp/components/wx-msg/main.vue' | ||||
| import WxLocation from '@/views/mp/components/wx-location/main.vue' | ||||
| import WxMusic from '@/views/mp/components/wx-music/main.vue' | ||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import * as MpMessageApi from '@/api/mp/message' | ||||
| import WxMsg from '@/views/mp/components/wx-msg/main.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import MessageTable from './MessageTable.vue' | ||||
| import { DICT_TYPE, getStrDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import { MsgType } from '@/views/mp/components/wx-msg/types' | ||||
| import type { FormInstance } from 'element-plus' | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref<any[]>([]) // 列表的数据 | ||||
| const loading = ref(false) | ||||
| const total = ref(0) // 数据的总页数 | ||||
| const list = ref<any[]>([]) // 当前页的列表数据 | ||||
| 
 | ||||
| // 搜索参数 | ||||
| interface QueryParams { | ||||
|  | @ -233,8 +110,12 @@ const queryParams: QueryParams = reactive({ | |||
|   createTime: [] | ||||
| }) | ||||
| const queryFormRef = ref<FormInstance | null>(null) // 搜索的表单 | ||||
| const showMessageBox = ref(false) // 是否显示弹出层 | ||||
| const userId = ref(0) // 操作的用户编号 | ||||
| 
 | ||||
| // 消息对话框 | ||||
| const messageBox = reactive({ | ||||
|   show: false, | ||||
|   userId: 0 | ||||
| }) | ||||
| 
 | ||||
| /** 侦听accountId */ | ||||
| const onAccountChanged = (id?: number) => { | ||||
|  | @ -243,6 +124,11 @@ const onAccountChanged = (id?: number) => { | |||
| } | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.pageNo = 1 | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| const getList = async () => { | ||||
|   try { | ||||
|     loading.value = true | ||||
|  | @ -254,14 +140,9 @@ const getList = async () => { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.pageNo = 1 | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = async () => { | ||||
|   // 暂存 accountId,并在 reset 后恢复 | ||||
|   const accountId = queryParams.accountId | ||||
|   queryFormRef.value?.resetFields() | ||||
|   queryParams.accountId = accountId | ||||
|  | @ -269,8 +150,8 @@ const resetQuery = async () => { | |||
| } | ||||
| 
 | ||||
| /** 打开消息发送窗口 */ | ||||
| const handleSend = async (row: any) => { | ||||
|   userId.value = row.userId | ||||
|   showMessageBox.value = true | ||||
| const handleSend = async (userId: number) => { | ||||
|   messageBox.userId = userId | ||||
|   messageBox.show = true | ||||
| } | ||||
| </script> | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item> | ||||
|         <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['mp:tag:create']"> | ||||
|  | @ -74,7 +74,7 @@ | |||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as MpTagApi from '@/api/mp/tag' | ||||
| import TagForm from './TagForm.vue' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="公众号" prop="accountId"> | ||||
|         <WxMpSelect @change="onAccountChanged" /> | ||||
|         <WxAccountSelect @change="onAccountChanged" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户标识" prop="openid"> | ||||
|         <el-input | ||||
|  | @ -97,7 +97,7 @@ | |||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as MpUserApi from '@/api/mp/user' | ||||
| import * as MpTagApi from '@/api/mp/tag' | ||||
| import WxMpSelect from '@/views/mp/components/WxMpSelect.vue' | ||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||
| import type { FormInstance } from 'element-plus' | ||||
| import UserForm from './UserForm.vue' | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,7 +59,8 @@ const formData = ref({ | |||
| }) | ||||
| const formRules = reactive({ | ||||
|   name: [{ required: true, message: '字典名称不能为空', trigger: 'blur' }], | ||||
|   type: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }] | ||||
|   type: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '状态不能为空', trigger: 'change' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,7 +83,8 @@ const formData = ref({ | |||
| const formRules = reactive({ | ||||
|   label: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }], | ||||
|   value: [{ required: true, message: '数据键值不能为空', trigger: 'blur' }], | ||||
|   sort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }] | ||||
|   sort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '状态不能为空', trigger: 'change' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
|  |  | |||
|  | @ -105,7 +105,9 @@ const formRules = reactive({ | |||
|   status: [{ required: true, message: '租户状态不能为空', trigger: 'blur' }], | ||||
|   accountCount: [{ required: true, message: '账号额度不能为空', trigger: 'blur' }], | ||||
|   expireTime: [{ required: true, message: '过期时间不能为空', trigger: 'blur' }], | ||||
|   domain: [{ required: true, message: '绑定域名不能为空', trigger: 'blur' }] | ||||
|   domain: [{ required: true, message: '绑定域名不能为空', trigger: 'blur' }], | ||||
|   username: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }], | ||||
|   password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| const packageList = ref([]) // 租户套餐 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 puhui999
						puhui999