mirror of
https://gitee.com/log4j/pig-ui.git
synced 2024-12-23 05:40:20 +08:00
Merge remote-tracking branch 'origin/lei_dev' into hui_dev
# Conflicts: # src/views/admin/menu/form.vue
This commit is contained in:
commit
dcbddc1fdb
4486
package-lock.json
generated
4486
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
3585
pnpm-lock.yaml
Normal file
3585
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
63
src/api/mp/wx-account-fans.ts
Normal file
63
src/api/mp/wx-account-fans.ts
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function fetchList(query) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function sync(appId) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans/sync/' + appId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObjs(id) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account-fans',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
66
src/api/mp/wx-account-tag.ts
Normal file
66
src/api/mp/wx-account-tag.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function getPage(query) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function delObjs(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag',
|
||||
method: 'delete',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function sync(appId) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag/sync/' + appId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function list(appId) {
|
||||
return request({
|
||||
url: '/admin/wx-account-tag/list/',
|
||||
method: 'get',
|
||||
params: { 'wxAccountAppid': appId }
|
||||
})
|
||||
}
|
85
src/api/mp/wx-account.ts
Normal file
85
src/api/mp/wx-account.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function fetchList(query) {
|
||||
return request({
|
||||
url: '/admin/wx-account/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-account/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function generateQr(appid) {
|
||||
return request({
|
||||
url: '/admin/wx-account/qr/' + appid,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function clearQuota(appid) {
|
||||
return request({
|
||||
url: '/admin/wx-account/clear-quota/' + appid,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObjs(id) {
|
||||
return request({
|
||||
url: '/admin/wx-account/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-account',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchAccountList() {
|
||||
return request({
|
||||
url: '/admin/wx-account/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchStatistics(q) {
|
||||
return request({
|
||||
url: '/admin/wx-account/statistics',
|
||||
method: 'get',
|
||||
params: q
|
||||
})
|
||||
}
|
39
src/api/mp/wx-auto-reply.ts
Normal file
39
src/api/mp/wx-auto-reply.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function getPage(query) {
|
||||
return request({
|
||||
url: '/admin/wx-auto-reply/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-auto-reply',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-auto-reply/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-auto-reply/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-auto-reply',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
79
src/api/mp/wx-fans-msg.ts
Normal file
79
src/api/mp/wx-fans-msg.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function fetchList(query) {
|
||||
return request({
|
||||
url: '/admin/wx-fans-msg/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-fans-msg',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wxfansmsg/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObjs(id) {
|
||||
return request({
|
||||
url: '/admin/wxfansmsg/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wxfansmsg',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchResList(query) {
|
||||
return request({
|
||||
url: '/admin/wx-fans-msg/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addResObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-fans-msg',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function delResObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-fans-msg/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
74
src/api/mp/wx-material.ts
Normal file
74
src/api/mp/wx-material.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function getPage(query) {
|
||||
return request({
|
||||
url: '/admin/wx-material/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-material/materialNews',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function materialNewsUpdate(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-material/materialNews',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-material/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObj(query) {
|
||||
return request({
|
||||
url: '/admin/wx-material',
|
||||
method: 'delete',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj) {
|
||||
return request({
|
||||
url: '/admin/wx-material',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getMaterialOther(query) {
|
||||
return request({
|
||||
url: '/admin/wx-material/materialOther',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
export function getMaterialVideo(query) {
|
||||
return request({
|
||||
url: '/admin/wx-material/materialVideo',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getTempMaterialOther(query) {
|
||||
return request({
|
||||
url: '/admin/wx-material/tempMaterialOther',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
40
src/api/mp/wx-menu.ts
Normal file
40
src/api/mp/wx-menu.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function getObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-menu/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function saveObj(appId, data) {
|
||||
return request({
|
||||
url: '/admin/wx-menu/' + appId,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function publishObj(id) {
|
||||
return request({
|
||||
url: '/admin/wx-menu/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
129
src/components/NameAvatar/base.scss
Normal file
129
src/components/NameAvatar/base.scss
Normal file
@ -0,0 +1,129 @@
|
||||
$d-type: (
|
||||
flex: flex,
|
||||
block: block,
|
||||
none: none,
|
||||
);
|
||||
|
||||
$flex-jc: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
between: space-between,
|
||||
around: space-around,
|
||||
);
|
||||
|
||||
$flex-ai: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
stretch: stretch,
|
||||
);
|
||||
|
||||
//spacing
|
||||
$spacing-types: (
|
||||
m: margin,
|
||||
p: padding,
|
||||
);
|
||||
|
||||
$spacing-directions: (
|
||||
t: top,
|
||||
r: right,
|
||||
b: bottom,
|
||||
l: left,
|
||||
);
|
||||
|
||||
$spacing-base-size: 5px;
|
||||
|
||||
$spacing-sizes: (
|
||||
0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
5: 5,
|
||||
);
|
||||
|
||||
@each $key, $value in $d-type {
|
||||
.d-#{$key} {
|
||||
display: $value;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.text-ellipsis {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-100 {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-grow-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@each $dir in(top, bottom, right, left) {
|
||||
.border-#{$dir} {
|
||||
border-#{$dir}: 1px solid;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $value in $flex-jc {
|
||||
.jc-#{$key} {
|
||||
justify-content: $value;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $value in $flex-ai {
|
||||
.ai-#{$key} {
|
||||
align-items: $value;
|
||||
}
|
||||
}
|
||||
//text
|
||||
@each $var in (left, center, right) {
|
||||
.text-#{$var} {
|
||||
text-align: $var !important;
|
||||
}
|
||||
}
|
||||
|
||||
@each $typeKey, $type in $spacing-types {
|
||||
@each $sizeKey, $size in $spacing-sizes {
|
||||
.#{$typeKey}-#{$sizeKey} {
|
||||
#{$type}: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
|
||||
@each $sizeKey, $size in $spacing-sizes {
|
||||
.#{$typeKey}x-#{$sizeKey} {
|
||||
#{$type}-left: $size * $spacing-base-size;
|
||||
#{$type}-right: $size * $spacing-base-size;
|
||||
}
|
||||
|
||||
.#{$typeKey}y-#{$sizeKey} {
|
||||
#{$type}-top: $size * $spacing-base-size;
|
||||
#{$type}-bottom: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
|
||||
@each $directionKey, $direction in $spacing-directions {
|
||||
@each $sizeKey, $size in $spacing-sizes {
|
||||
.#{$typeKey}#{$directionKey}-#{$sizeKey} {
|
||||
#{$type}-#{$direction}: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
106
src/components/NameAvatar/index.vue
Normal file
106
src/components/NameAvatar/index.vue
Normal file
@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div :class="mode=='square'?'chatface':'brround avatar cover-image'" :style="transform"
|
||||
style="overflow:hidden;width:40px;height:40px;">
|
||||
<img v-if="faceUrl&&!num" :src="faceUrl" class="w-100 h-100"/>
|
||||
<div v-else :style="styles" class="w-100 h-100 d-flex ai-center jc-center">{{ text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "nameAvatar",
|
||||
props: {
|
||||
scale: {
|
||||
type: String,
|
||||
default: "1"
|
||||
},
|
||||
num: [Number, String],
|
||||
name: String,
|
||||
mode: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
fontColor: {
|
||||
type: String,
|
||||
default: "#fff"
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: "#3696F2"
|
||||
},
|
||||
faceUrl: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
watch: {},
|
||||
computed: {
|
||||
// eslint-disable-next-line vue/return-in-computed-property
|
||||
text() {
|
||||
if (this.num !== undefined) {
|
||||
return `+${this.num}`;
|
||||
} else {
|
||||
if (this.name) {
|
||||
return this.name.slice(-2);
|
||||
}
|
||||
}
|
||||
},
|
||||
transform() {
|
||||
let style = {};
|
||||
if (this.scale) {
|
||||
style["transform"] = `scale(${this.scale}, ${this.scale})`;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
styles() {
|
||||
let style = {};
|
||||
if (this.size) {
|
||||
style["font-size"] = "12px";
|
||||
}
|
||||
if (this.fontColor) {
|
||||
style.color = this.fontColor;
|
||||
}
|
||||
if (this.backgroundColor) {
|
||||
style["background"] = this.backgroundColor;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./base.scss";
|
||||
|
||||
.avatar {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: bottom;
|
||||
font-size: 8px;
|
||||
user-select: none;
|
||||
z-index: 10;
|
||||
|
||||
&:hover {
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.brround {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.chatface {
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2,7 +2,7 @@
|
||||
<div class="head-container">
|
||||
<el-input v-model="searchName" :placeholder="placeholder" clearable style="margin-bottom: 20px"
|
||||
@change="getDeptTree" />
|
||||
<el-tree :data="state.List" :props="props" :expand-on-click-node="false" ref="deptTreeRef"
|
||||
<el-tree :data="state.List" :props="props.props" :expand-on-click-node="false" ref="deptTreeRef"
|
||||
:loading="state.localLoading" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
|
||||
</div>
|
||||
</template>
|
||||
@ -13,7 +13,7 @@ import { onMounted, reactive, ref, unref } from "vue";
|
||||
|
||||
const emit = defineEmits(['search', 'nodeClick'])
|
||||
|
||||
const { placeholder, props, query, loading } = defineProps({
|
||||
const props = defineProps({
|
||||
props: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@ -40,7 +40,7 @@ const { placeholder, props, query, loading } = defineProps({
|
||||
|
||||
const state = reactive({
|
||||
List: [],
|
||||
localLoading: loading
|
||||
localLoading: props.loading
|
||||
})
|
||||
|
||||
|
||||
@ -51,12 +51,15 @@ const handleNodeClick = (item: any) => {
|
||||
}
|
||||
|
||||
const getDeptTree = () => {
|
||||
if (query instanceof Function) {
|
||||
if (props.query instanceof Function) {
|
||||
state.localLoading = true
|
||||
const result = query(unref(searchName))
|
||||
const result = props.query(unref(searchName))
|
||||
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
|
||||
result.then((r: any) => {
|
||||
state.List = r.data
|
||||
if(r.data.length > 0){
|
||||
handleNodeClick(r.data[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-remove="handleRemove"
|
||||
:data="data"
|
||||
:auto-upload="autoUpload"
|
||||
:on-success="handleUploadSuccess" class="upload-file-uploader" drag multiple>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">
|
||||
@ -33,8 +35,10 @@
|
||||
:file-list="fileList"
|
||||
:headers="headers"
|
||||
:limit="limit"
|
||||
:auto-upload="autoUpload"
|
||||
:on-error="handleUploadError"
|
||||
:on-remove="handleRemove"
|
||||
:data="data"
|
||||
:on-success="handleUploadSuccess" class="upload-file-uploader" multiple>
|
||||
<el-button type="primary" link>点击上传</el-button>
|
||||
</el-upload>
|
||||
@ -76,6 +80,13 @@ const props = defineProps({
|
||||
validator:(value: string) => {
|
||||
return ['default','simple'].includes(value)
|
||||
}
|
||||
},
|
||||
data: {
|
||||
type: Object
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
@ -185,4 +196,13 @@ watch(() => props.modelValue, val => {
|
||||
}, { deep: true, immediate: true });
|
||||
|
||||
|
||||
const submit = () => {
|
||||
fileUpload.value.submit()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
@ -74,7 +74,6 @@
|
||||
<div class="vue3-cron-div">
|
||||
<el-button
|
||||
class="language"
|
||||
type="text"
|
||||
@click="state.language = state.language === 'en' ? 'cn' : 'en'"
|
||||
>{{ state.language === 'en' ? 'cn' : 'en' }}</el-button
|
||||
>
|
||||
@ -556,7 +555,7 @@
|
||||
<span>
|
||||
cron预览:
|
||||
</span>
|
||||
<el-tag type="primary">
|
||||
<el-tag type="primary">
|
||||
{{ state.cron }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
@ -87,7 +87,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
setup(props,context){
|
||||
setup(props){
|
||||
const {mode,captchaType} = toRefs(props)
|
||||
const { proxy } = getCurrentInstance();
|
||||
let secretKey = ref(''), //后端返回的ase加密秘钥
|
||||
|
@ -101,8 +101,8 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const {mode, captchaType, vSpace, imgSize, barSize, type, blockSize, explain} = toRefs(props)
|
||||
setup(props) {
|
||||
const {mode, captchaType, type, blockSize, explain} = toRefs(props)
|
||||
const { proxy } = getCurrentInstance();
|
||||
let secretKey = ref(''), //后端返回的ase加密秘钥
|
||||
passFlag = ref(''), //是否通过的标识
|
||||
@ -198,10 +198,11 @@ export default {
|
||||
//鼠标按下
|
||||
function start(e) {
|
||||
e = e || window.event
|
||||
let x = null
|
||||
if (!e.touches) { //兼容PC端
|
||||
var x = e.clientX;
|
||||
x = e.clientX;
|
||||
} else { //兼容移动端
|
||||
var x = e.touches[0].pageX;
|
||||
x = e.touches[0].pageX;
|
||||
}
|
||||
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left);
|
||||
startMoveTime.value = +new Date(); //开始滑动的时间
|
||||
|
94
src/components/wechart/fileUpload/index.vue
Normal file
94
src/components/wechart/fileUpload/index.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<el-upload
|
||||
ref="fileUpload"
|
||||
:action="actionUrl"
|
||||
:headers="headers"
|
||||
multiple
|
||||
:limit="1"
|
||||
:on-success="handleUploadSuccess"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeThumbImageUpload"
|
||||
:auto-upload="autoUpload"
|
||||
:data="uploadData">
|
||||
<template #tip>
|
||||
<div class="el-upload__tip" v-if="props.type.length > 0">支持{{props.type.join("/")}}格式,大小不超过2M</div>
|
||||
</template>
|
||||
<el-button type="primary">本地上传</el-button>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-file-upload">
|
||||
|
||||
import {Local, Session} from "/@/utils/storage";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const actionUrl = ref("/admin/wx-material/materialFileUpload")
|
||||
|
||||
const fileUpload = ref()
|
||||
|
||||
const headers = computed(() => {
|
||||
const tenantId = Local.get("tenantId") ? Local.get("tenantId") : 1
|
||||
return {
|
||||
'Authorization': "Bearer " + Session.get("token"),
|
||||
'TENANT-ID': tenantId
|
||||
};
|
||||
})
|
||||
// 定义刷新表格emit
|
||||
const emit = defineEmits(['success']);
|
||||
const fileList = ref([])
|
||||
|
||||
const props = defineProps({
|
||||
uploadData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
appId: '',
|
||||
mediaType: 'image',
|
||||
title: '',
|
||||
introduction: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
type: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const beforeThumbImageUpload = (file: any) => {
|
||||
let isType = true
|
||||
if(props.type?.length > 0){
|
||||
isType = props.type?.includes(file.type)
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 2
|
||||
if (!isType) {
|
||||
useMessage().error("上传文件格式不对!")
|
||||
}
|
||||
if (!isLt) {
|
||||
useMessage().error("上传文件大小不能超过2M!!")
|
||||
}
|
||||
return isType && isLt
|
||||
}
|
||||
|
||||
const handleUploadSuccess = (response, file, fileList) => {
|
||||
fileList.value = []
|
||||
emit("success",response, file, fileList)
|
||||
}
|
||||
const submit = () => {
|
||||
fileUpload.value.submit()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
215
src/components/wechart/wx-material-select/main.vue
Normal file
215
src/components/wechart/wx-material-select/main.vue
Normal file
@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<el-dialog title="选择图文" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<div v-if="objData.type === 'image'">
|
||||
<div class="waterfall" v-loading="state.loading">
|
||||
<div class="waterfall-item" v-for="item in state.dataList" :key="item.mediaId">
|
||||
<img class="material-img" :src="item.url" />
|
||||
<p class="item-name">{{ item.name }}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="success" @click="selectMaterial(item)"
|
||||
>选择
|
||||
<el-icon class="el-icon--right"></el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<pagination v-bind="state.pagination"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"/>
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'voice'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="state.loading" :data="state.dataList">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="语音" align="center" prop="url">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="上传时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
fixed="right"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<el-button
|
||||
icon="el-icon-circle-plus"
|
||||
@click="selectMaterial(scope.row)"
|
||||
>选择</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"/>
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'video'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="state.loading" :data="state.dataList">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="标题" align="center" prop="title" />
|
||||
<el-table-column label="介绍" align="center" prop="introduction" />
|
||||
<el-table-column label="视频" align="center" prop="url">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="上传时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
fixed="right"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<el-button
|
||||
icon="el-icon-circle-plus"
|
||||
@click="selectMaterial(scope.row)"
|
||||
>选择</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"/>
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'news'">
|
||||
<div class="waterfall" v-loading="state.loading">
|
||||
<template v-for="item in state.dataList">
|
||||
<div v-if="item.content && item.content.newsItem" class="waterfall-item" :key="item.id">
|
||||
<wx-news :obj-data="item.content.newsItem"></wx-news>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="success" @click="selectMaterial(item)">
|
||||
选择<el-icon class="el-icon--right"/>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<pagination v-bind="state.pagination"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-material-select">
|
||||
|
||||
import {defineEmits} from "vue";
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {getPage} from "/@/api/mp/wx-material";
|
||||
const WxNews = defineAsyncComponent(() => import('../wx-news/index.vue'))
|
||||
|
||||
const emit = defineEmits(["selectMaterial"])
|
||||
|
||||
const objData = reactive({
|
||||
repType: '',
|
||||
accountId: '',
|
||||
type: ''
|
||||
})
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
type: "",
|
||||
appId: ""
|
||||
},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false,
|
||||
props: {
|
||||
item: 'items',
|
||||
totalCount: 'totalCount'
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle
|
||||
} = useTable(state)
|
||||
|
||||
|
||||
const selectMaterial = (item : any) => {
|
||||
emit('selectMaterial', item,objData.accountId)
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const openDialog = (data: any) => {
|
||||
state.queryForm.type = data.type
|
||||
state.queryForm.appId = data.accountId
|
||||
objData.type = data.type
|
||||
visible.value = true
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap:10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
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) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
/*瀑布流样式*/
|
||||
</style>
|
101
src/components/wechart/wx-msg/card.scss
Normal file
101
src/components/wechart/wx-msg/card.scss
Normal file
@ -0,0 +1,101 @@
|
||||
.avue-card{
|
||||
&__item{
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #e8e8e8;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0,0,0,.65);
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5;
|
||||
list-style: none;
|
||||
font-feature-settings: "tnum";
|
||||
cursor: pointer;
|
||||
height:200px;
|
||||
&:hover{
|
||||
border-color: rgba(0,0,0,.09);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,.09);
|
||||
}
|
||||
&--add{
|
||||
border:1px dashed #000;
|
||||
width: 100%;
|
||||
color: rgba(0,0,0,.45);
|
||||
background-color: #fff;
|
||||
border-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
i{
|
||||
margin-right: 10px;
|
||||
}
|
||||
&:hover{
|
||||
color: #40a9ff;
|
||||
background-color: #fff;
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__body{
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
}
|
||||
&__detail{
|
||||
flex:1
|
||||
}
|
||||
&__avatar{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 48px;
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
&__title{
|
||||
color: rgba(0,0,0,.85);
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
&:hover{
|
||||
color:#1890ff;
|
||||
}
|
||||
}
|
||||
&__info{
|
||||
color: rgba(0,0,0,.45);
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
height: 64px;
|
||||
}
|
||||
&__menu{
|
||||
display: flex;
|
||||
justify-content:space-around;
|
||||
height: 50px;
|
||||
background: #f7f9fa;
|
||||
color: rgba(0,0,0,.45);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
&:hover{
|
||||
color:#1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** joolun 额外加的 */
|
||||
.avue-comment__main {
|
||||
flex: unset!important;
|
||||
border-radius: 5px!important;
|
||||
margin: 0 8px!important;
|
||||
}
|
||||
.avue-comment__header {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
.avue-comment__body {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
88
src/components/wechart/wx-msg/comment.scss
Normal file
88
src/components/wechart/wx-msg/comment.scss
Normal file
@ -0,0 +1,88 @@
|
||||
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
|
||||
.avue-comment{
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
&--reverse{
|
||||
flex-direction:row-reverse;
|
||||
.avue-comment__main{
|
||||
&:before,&:after{
|
||||
left: auto;
|
||||
right: -8px;
|
||||
border-width: 8px 0 8px 8px;
|
||||
}
|
||||
&:before{
|
||||
border-left-color: #dedede;
|
||||
}
|
||||
&:after{
|
||||
border-left-color: #f8f8f8;
|
||||
margin-right: 1px;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__avatar{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&__header{
|
||||
padding: 5px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
&__author{
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
&__main{
|
||||
flex:1;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 2px;
|
||||
&:before,&:after{
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
right: 100%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
display: block;
|
||||
content: " ";
|
||||
border-color: transparent;
|
||||
border-style: solid solid outset;
|
||||
border-width: 8px 8px 8px 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
&:before {
|
||||
border-right-color: #dedede;
|
||||
z-index: 1;
|
||||
}
|
||||
&:after{
|
||||
border-right-color: #f8f8f8;
|
||||
margin-left: 1px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
&__body{
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
font-family: Segoe UI,Lucida Grande,Helvetica,Arial,Microsoft YaHei,FreeSans,Arimo,Droid Sans,wenquanyi micro hei,Hiragino Sans GB,Hiragino Sans GB W3,FontAwesome,sans-serif;color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
blockquote{
|
||||
margin:0;
|
||||
font-family: Georgia,Times New Roman,Times,Kai,Kaiti SC,KaiTi,BiauKai,FontAwesome,serif;
|
||||
padding: 1px 0 1px 15px;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
}
|
223
src/components/wechart/wx-msg/index.vue
Normal file
223
src/components/wechart/wx-msg/index.vue
Normal file
@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<el-dialog title="用户消息" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<div v-loading="mainLoading" class="msg-main">
|
||||
<div id="msg-div" class="msg-div">
|
||||
<div v-if="!tableLoading">
|
||||
<div v-if="loadMore" class="el-table__empty-block" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span>
|
||||
</div>
|
||||
<div v-if="!loadMore" class="el-table__empty-block"><span class="el-table__empty-text">没有更多了</span></div>
|
||||
</div>
|
||||
<div v-for="item in tableData" :key="item.id" class="execution">
|
||||
<div class="avue-comment" :class="item.type === '2' ? 'avue-comment--reverse' : ''">
|
||||
<div class="avatar-div">
|
||||
<name-avatar v-if="item.type === '1'" scale="2" :name="item.nickName" />
|
||||
<name-avatar v-if="item.type !== '1'" scale="2" :face-url="item.appLogo" />
|
||||
</div>
|
||||
<div class="avue-comment__main">
|
||||
<div class="avue-comment__header">
|
||||
<div class="avue-comment__create_time">{{ item.createTime }}</div>
|
||||
</div>
|
||||
<div class="avue-comment__body" :style="item.type === '2' ? 'background: #6BED72;' : ''">
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'subscribe'">
|
||||
<el-tag type="success" size="mini">关注</el-tag>
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'unsubscribe'">
|
||||
<el-tag type="danger" size="mini">取消关注</el-tag>
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'CLICK'">
|
||||
<el-tag size="mini">点击菜单</el-tag>
|
||||
:【{{ item.repName }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'VIEW'">
|
||||
<el-tag size="mini">点击菜单链接</el-tag>
|
||||
:【{{ item.repUrl }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'scancode_waitmsg'">
|
||||
<el-tag size="mini">扫码结果:</el-tag>
|
||||
:【{{ item.repContent }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'text'">{{ item.repContent }}</div>
|
||||
<div v-if="item.repType === 'image'">
|
||||
<a target="_blank" :href="item.repUrl"><img :src="item.repUrl" style="width: 100px"></a>
|
||||
</div>
|
||||
<div v-if="item.repType === 'voice'">
|
||||
{{item}}
|
||||
<!-- <WxVoicePlayer :obj-data="item"></WxVoicePlayer>-->
|
||||
</div>
|
||||
<div v-if="item.repType === 'video'" style="text-align: center">
|
||||
{{item}}
|
||||
<!-- <WxVideoPlayer :obj-data="item"></WxVideoPlayer>-->
|
||||
</div>
|
||||
<div v-if="item.repType === 'shortvideo'" style="text-align: center">
|
||||
{{item}}
|
||||
<!-- <WxVideoPlayer :obj-data="item"></WxVideoPlayer>-->
|
||||
</div>
|
||||
<div v-if="item.repType === 'location'">
|
||||
<el-link
|
||||
type="primary"
|
||||
target="_blank"
|
||||
:href="'https://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&pointx='+item.repLocationY+'&pointy='+item.repLocationX+'&name='+item.repContent+'&ref=joolun'">
|
||||
<img
|
||||
:src="'https://apis.map.qq.com/ws/staticmap/v2/?zoom=10&markers=color:blue|label:A|'+item.repLocationX+','+item.repLocationY+'&key=PFFBZ-RBM3V-IEEPP-UH6KE-6QUQE-C4BVJ&size=250*180'">
|
||||
<p /><i class="el-icon-map-location"></i>{{ item.repContent }}
|
||||
</el-link>
|
||||
</div>
|
||||
<div v-if="item.repType === 'link'" class="avue-card__detail">
|
||||
<el-link type="success" :underline="false" target="_blank" :href="item.repUrl">
|
||||
<div class="avue-card__title"><i class="el-icon-link"></i>{{ item.repName }}</div>
|
||||
</el-link>
|
||||
<div class="avue-card__info" style="height: unset">{{ item.repDesc }}</div>
|
||||
</div>
|
||||
<div v-if="item.repType === 'news'" style="width: 300px">
|
||||
<wx-news :obj-data="JSON.parse(item.content).articles"></wx-news>
|
||||
</div>
|
||||
<div v-if="item.repType === 'music'">
|
||||
<el-link type="success" :underline="false" target="_blank" :href="item.repUrl">
|
||||
<div class="avue-card__body" style="padding:10px;background-color: #fff;border-radius: 5px">
|
||||
<div class="avue-card__avatar"><img :src="item.repThumbUrl" alt=""></div>
|
||||
<div class="avue-card__detail">
|
||||
<div class="avue-card__title" style="margin-bottom:unset">{{ item.repName }}</div>
|
||||
<div class="avue-card__info" style="height: unset">{{ item.repDesc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-loading="sendLoading" class="msg-send">
|
||||
<wx-reply :objData="objData"></wx-reply>
|
||||
<el-button type="success" class="send-but" @click="sendMsg">发送(S)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-msg">
|
||||
import { fetchList,addObj } from '/@/api/mp/wx-fans-msg'
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import("../wx-reply/index.vue"))
|
||||
const WxNews = defineAsyncComponent(() => import("../wx-news/index.vue"))
|
||||
|
||||
const NameAvatar = defineAsyncComponent(() => import("/@/components/NameAvatar/index.vue"))
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
// 各种loading
|
||||
const mainLoading = ref(false)
|
||||
const sendLoading = ref(false)
|
||||
|
||||
const loadMore = ref(false)
|
||||
|
||||
const objData = ref({
|
||||
repType: 'text',
|
||||
appId: ''
|
||||
}) as any
|
||||
|
||||
const page = reactive({
|
||||
total: 0, // 总页数
|
||||
currentPage: 1, // 当前页数
|
||||
pageSize: 10, // 每页显示多少条
|
||||
ascs: [], //升序字段
|
||||
descs: 'create_time'//降序字段
|
||||
})
|
||||
|
||||
const wxData = reactive({
|
||||
appId: '',
|
||||
wxUserId: ''
|
||||
})
|
||||
|
||||
const sendMsg = () => {
|
||||
if (objData.value) {
|
||||
if (objData.value.repType === 'news') {
|
||||
if(objData.value.content.newsItem.length > 1){
|
||||
useMessage().error("图文消息条数限制在1条以内,已默认发送第一条")
|
||||
}
|
||||
objData.value.content.newsItem = [objData.value.content.newsItem[0]]
|
||||
objData.value.content = JSON.stringify(objData.value.content)
|
||||
}
|
||||
sendLoading.value = true
|
||||
addObj(Object.assign({
|
||||
wxUserId: wxData.wxUserId,
|
||||
appId: wxData.appId
|
||||
}, objData.value)).then(() => {
|
||||
tableData.value = []
|
||||
getData()
|
||||
}).finally(() => {
|
||||
sendLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const tableData = ref([] as any)
|
||||
|
||||
const tableLoading = ref(false)
|
||||
|
||||
const openDialog = (data: any) => {
|
||||
wxData.wxUserId = data.wxUserId
|
||||
wxData.appId = data.appId
|
||||
objData.value.appId = data.appId
|
||||
getData()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const getData = () => {
|
||||
tableLoading.value = true
|
||||
fetchList({
|
||||
...page,
|
||||
...wxData
|
||||
}).then(res=> {
|
||||
const data = res.data.records.reverse()
|
||||
tableData.value = [...data, ...tableData.value]
|
||||
page.total = res.data.total
|
||||
tableLoading.value = false
|
||||
if (data.length < page.pageSize || data.length === 0) {
|
||||
loadMore.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const loadingMore = () => {
|
||||
page.currentPage = page.currentPage + 1
|
||||
getData()
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './comment.scss';
|
||||
@import './card.scss';
|
||||
|
||||
|
||||
.msg-main {
|
||||
margin-top: -30px;
|
||||
padding: 10px;
|
||||
}
|
||||
.msg-div {
|
||||
height: 50vh;
|
||||
overflow: auto;
|
||||
background-color: #eaeaea;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.msg-send {
|
||||
padding: 10px;
|
||||
}
|
||||
.avatar-div {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
}
|
||||
.send-but {
|
||||
float: right;
|
||||
margin-top: 8px!important;
|
||||
}
|
||||
</style>
|
92
src/components/wechart/wx-news/index.vue
Normal file
92
src/components/wechart/wx-news/index.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="news-home">
|
||||
<div v-for="(news, index) in props.objData" :key="index" class="news-div">
|
||||
<a v-if="index===0" target="_blank" :href="news.url">
|
||||
<div class="news-main">
|
||||
<div class="news-content">
|
||||
<img class="material-img" :src="news.thumbUrl" width="280px" height="120px" />
|
||||
<div class="news-content-title">
|
||||
<span>{{ news.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a v-if="index>0" target="_blank" :href="news.url">
|
||||
<div class="news-main-item">
|
||||
<div class="news-content-item">
|
||||
<div class="news-content-item-title">{{ news.title }}</div>
|
||||
<div class="news-content-item-img">
|
||||
<img class="material-img" :src="news.thumbUrl" height="100%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-news">
|
||||
|
||||
const props = defineProps({
|
||||
objData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-home{
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-main{
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-content{
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.news-content-title{
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
white-space: normal;
|
||||
box-sizing: unset!important
|
||||
}
|
||||
.news-main-item{
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
.news-content-item{
|
||||
position: relative;
|
||||
}
|
||||
.news-content-item-title{
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
width: 70%;
|
||||
margin-left: 1%;
|
||||
white-space: normal
|
||||
}
|
||||
.news-content-item-img{
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae;
|
||||
margin-right: 1%;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
282
src/components/wechart/wx-reply/index.vue
Normal file
282
src/components/wechart/wx-reply/index.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<el-tabs v-model="objData.repType" type="border-card" @tab-click="handleClick" style="width: 100%;">
|
||||
<el-tab-pane name="text" label="text">
|
||||
<template #label><i class="el-icon-document"></i> 文本</template>
|
||||
<el-input
|
||||
v-model="objData.repContent"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入内容">
|
||||
</el-input>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="image" label="image">
|
||||
<template #label><i class="el-icon-picture"></i> 图片</template>
|
||||
<el-row>
|
||||
<div v-if="objData.repUrl" class="select-item">
|
||||
<img class="material-img" :src="objData.repUrl">
|
||||
<p v-if="objData.repName" class="item-name">{{ objData.repName }}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.repUrl" style="width: 100%;">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial({type: 'image',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12" class="col-add">
|
||||
<wx-file-upload :data="uploadData" @success="handelImage"></wx-file-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="voice" label="voice">
|
||||
<template #label><i class="el-icon-phone"></i> 语音</template>
|
||||
<el-row>
|
||||
<div v-if="objData.repName" class="select-item">
|
||||
<p class="item-name">{{ objData.repName }}</p>
|
||||
<div class="item-infos">
|
||||
<!-- <WxVoicePlayer :obj-data="Object.assign(tempPlayerObj,{repMediaId: objData.media_id, repName: objData.repName})"></WxVoicePlayer>-->
|
||||
</div>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.repName" style="width: 100%;">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial({type: 'voice',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12" class="col-add">
|
||||
<wx-file-upload :data="uploadData"></wx-file-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="video" label="video">
|
||||
<template #label><i class="el-icon-share"></i> 视频</template>
|
||||
<el-row style="text-align: center">
|
||||
<el-input v-model="objData.repName" placeholder="请输入标题"></el-input>
|
||||
<el-input v-model="objData.repDesc" placeholder="请输入描述"></el-input>
|
||||
<div style="text-align: center;">
|
||||
<a v-if="objData.repUrl" target="_blank" :href="objData.repUrl"><i
|
||||
class="icon-bofang"> 播放视频</i></a>
|
||||
</div>
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<div style="text-align: center">
|
||||
<el-button type="success" @click="openMaterial({type: 'video',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="news" label="news">
|
||||
<template #label><i class="el-icon-news"></i> 图文</template>
|
||||
<el-row>
|
||||
<div v-if="objData.content" class="select-item">
|
||||
<wx-news :obj-data="objData.content.newsItem"></wx-news>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.content" style="width: 100%;">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="24" class="col-select2">
|
||||
<el-button type="success" @click="openMaterial({type: 'news',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<wx-material-select ref="dialogNewsRef" @selectMaterial="selectMaterial"></wx-material-select>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-reply">
|
||||
import {getMaterialVideo} from "/@/api/mp/wx-material";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const WxMaterialSelect = defineAsyncComponent(() => import("/@/components/wechart/wx-material-select/main.vue"))
|
||||
|
||||
const WxFileUpload = defineAsyncComponent(() => import("/@/components/wechart/fileUpload/index.vue"))
|
||||
|
||||
const WxNews = defineAsyncComponent(() => import("/@/components/wechart/wx-news/index.vue"))
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
objData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
repType: '',
|
||||
repContent: '',
|
||||
repName: '',
|
||||
repDesc: '',
|
||||
repUrl: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const uploadData = reactive({
|
||||
mediaType: props.objData.repType,
|
||||
title: '',
|
||||
introduction: '',
|
||||
appId: props.objData.appId
|
||||
})
|
||||
|
||||
const handleClick = (tab) => {
|
||||
uploadData.mediaType = tab.paneName
|
||||
uploadData.appId = props.objData.appId
|
||||
|
||||
}
|
||||
|
||||
const deleteObj = () => {
|
||||
props.objData.repName = ''
|
||||
props.objData.repUrl = ''
|
||||
props.objData.content = ''
|
||||
}
|
||||
|
||||
const openMaterial = (data: any) => {
|
||||
dialogNewsRef.value.openDialog(data)
|
||||
}
|
||||
|
||||
const dialogNewsRef = ref()
|
||||
|
||||
const selectMaterial = (item, appId) => {
|
||||
let tempObjItem = {
|
||||
repType: '',
|
||||
repMediaId: '',
|
||||
media_id: '',
|
||||
content: ''
|
||||
} as any
|
||||
tempObjItem.repType = props.objData.repType
|
||||
tempObjItem.repMediaId = item.mediaId
|
||||
tempObjItem.media_id = item.mediaId
|
||||
tempObjItem.content = item.content
|
||||
|
||||
props.objData.repMediaId = item.mediaId
|
||||
props.objData.media_id = item.mediaId
|
||||
props.objData.content = item.content
|
||||
if (props.objData.repType === 'music') {
|
||||
tempObjItem.repThumbMediaId = item.mediaId
|
||||
tempObjItem.repThumbUrl = item.url
|
||||
props.objData.repThumbMediaId = item.mediaId
|
||||
props.objData.repThumbUrl = item.url
|
||||
} else {
|
||||
tempObjItem.repName = item.name
|
||||
tempObjItem.repUrl = item.url
|
||||
props.objData.repName = item.name
|
||||
props.objData.repUrl = item.url
|
||||
}
|
||||
if (props.objData.repType === 'video') {
|
||||
getMaterialVideo({
|
||||
mediaId: item.mediaId,
|
||||
appId: appId
|
||||
}).then(response => {
|
||||
const data = response.data.data
|
||||
tempObjItem.repDesc = data.description
|
||||
tempObjItem.repUrl = data.downUrl
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handelImage = (response) => {
|
||||
if (response.code === 0) {
|
||||
const item = response.data
|
||||
selectMaterial(item,props.objData.appId)
|
||||
} else {
|
||||
useMessage().error("上传错误" + response.msg)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.select-item {
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.select-item2 {
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
line-height: unset !important;
|
||||
}
|
||||
|
||||
.col-select {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
}
|
||||
|
||||
.col-select2 {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.col-add {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
float: right
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px !important;
|
||||
height: 100px !important;
|
||||
line-height: 100px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item-infos {
|
||||
width: 30%;
|
||||
margin: auto
|
||||
}
|
||||
</style>
|
@ -62,6 +62,7 @@ export function wavesDirective(app: App) {
|
||||
*/
|
||||
export function dragDirective(app: App) {
|
||||
app.directive('drag', {
|
||||
// @ts-ignore
|
||||
mounted(el, binding) {
|
||||
if (!binding.value) return false;
|
||||
|
||||
|
@ -26,6 +26,8 @@ export interface BasicTableProps {
|
||||
descs?: string[]
|
||||
|
||||
ascs?: string[]
|
||||
|
||||
props?: any
|
||||
}
|
||||
|
||||
export interface Pagination {
|
||||
@ -60,7 +62,11 @@ export function useTable(options?: BasicTableProps) {
|
||||
loading: false,
|
||||
selectObjs: [],
|
||||
descs: [],
|
||||
ascs: []
|
||||
ascs: [],
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total'
|
||||
}
|
||||
}
|
||||
|
||||
const mergeDefaultOptions = (options: any, props: any): BasicTableProps => {
|
||||
@ -86,10 +92,10 @@ export function useTable(options?: BasicTableProps) {
|
||||
descs: state.descs,
|
||||
ascs: state.ascs
|
||||
}).then((res: any) => {
|
||||
state.dataList = state.isPage ? res.data.records : res.data
|
||||
state.pagination!.total = state.isPage ? res.data.total : 0
|
||||
state.dataList = state.isPage ? res.data[state.props.item] : res.data
|
||||
state.pagination!.total = state.isPage ? res.data[state.props.totalCount] : 0
|
||||
}).catch((err: any) => {
|
||||
ElMessage.error(err.data.msg)
|
||||
ElMessage.error(err.msg || err.data.msg)
|
||||
}).finally(() => {
|
||||
state.loading = false;
|
||||
})
|
||||
|
@ -2,8 +2,8 @@ import {createApp} from 'vue'
|
||||
import pinia from '/@/stores/index'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import {directive} from '/@/directive/index'
|
||||
import {i18n} from '/@/i18n/index'
|
||||
import {directive} from '/@/directive'
|
||||
import {i18n} from '/@/i18n'
|
||||
import other from '/@/utils/other'
|
||||
|
||||
import ElementPlus from 'element-plus'
|
||||
|
@ -1,6 +1,5 @@
|
||||
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
|
||||
import errorCode from './errorCode'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Session, Local } from '/@/utils/storage';
|
||||
import qs from 'qs';
|
||||
|
||||
@ -20,6 +19,7 @@ const service: AxiosInstance = axios.create({
|
||||
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
// get查询参数序列化
|
||||
if (config.method === 'get') {
|
||||
// @ts-ignore
|
||||
config.paramsSerializer = (params: any) => {
|
||||
return qs.stringify(params, { arrayFormat: 'repeat' })
|
||||
}
|
||||
@ -51,7 +51,6 @@ service.interceptors.response.use((res: any) => {
|
||||
return res.data;
|
||||
}, error => {
|
||||
const status = Number(error.response.status) || 200
|
||||
const message = error.response.data.msg || errorCode[status] || errorCode['default']
|
||||
if (status === 424) {
|
||||
ElMessageBox.confirm('令牌状态已过期,请点击重新登录', '系统提示', {
|
||||
confirmButtonText: '重新登录',
|
||||
|
@ -1,226 +1,215 @@
|
||||
<template>
|
||||
<el-dialog :close-on-click-modal="false" :title="state.ruleForm.menuId ? $t('common.editBtn') : $t('common.addBtn')"
|
||||
draggable v-model="visible">
|
||||
<el-form :model="state.ruleForm" :rules="dataRules" label-width="90px" ref="menuDialogFormRef"
|
||||
v-loading="loading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.menuType')" prop="menType">
|
||||
<el-radio-group v-model="state.ruleForm.menuType">
|
||||
<el-radio-button label="0">左菜单</el-radio-button>
|
||||
<el-radio-button label="1">按钮</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.parentId')" prop="parentId">
|
||||
<el-tree-select :data="state.parentData" :placeholder="$t('sysmenu.inputParentIdTip')"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
:render-after-expand="false" check-strictly
|
||||
class="w100" clearable
|
||||
v-model="state.ruleForm.parentId">
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.name')" prop="name">
|
||||
<el-input :placeholder="$t('sysmenu.inputNameTip')" clearable
|
||||
v-model="state.ruleForm.name"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.path')" prop="path">
|
||||
<el-input :placeholder="$t('sysmenu.inputPathTip')" v-model="state.ruleForm.path"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '1'">
|
||||
<el-form-item :label="$t('sysmenu.permission')" prop="permission">
|
||||
<el-input :placeholder="$t('sysmenu.inputPermissionTip')" maxlength="50"
|
||||
v-model="state.ruleForm.permission"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.sortOrder')" prop="sortOrder">
|
||||
<el-input-number :min="0" controls-position="right" v-model="state.ruleForm.sortOrder"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('sysmenu.inputIconTip')" v-model="state.ruleForm.icon"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0' && showembedded">
|
||||
<el-form-item :label="$t('sysmenu.embedded')" prop="embedded">
|
||||
<el-radio-group v-model="state.ruleForm.embedded">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.keepAlive')" prop="keepAlive">
|
||||
<el-radio-group v-model="state.ruleForm.keepAlive">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.visible')" prop="visible">
|
||||
<el-radio-group v-model="state.ruleForm.visible">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-dialog :title="state.ruleForm.menuId ? $t('common.editBtn') : $t('common.addBtn')" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="menuDialogFormRef" :model="state.ruleForm" :rules="dataRules" label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.menuType')" prop="menType">
|
||||
<el-radio-group v-model="state.ruleForm.menuType">
|
||||
<el-radio-button label="0">左菜单</el-radio-button>
|
||||
<el-radio-button label="1">按钮</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.parentId')" prop="parentId">
|
||||
<el-tree-select v-model="state.ruleForm.parentId" :data="state.parentData" :render-after-expand="false"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }" class="w100" clearable check-strictly
|
||||
:placeholder="$t('sysmenu.inputParentIdTip')">
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.name')" prop="name">
|
||||
<el-input v-model="state.ruleForm.name" clearable :placeholder="$t('sysmenu.inputNameTip')"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.path')" prop="path">
|
||||
<el-input v-model="state.ruleForm.path" :placeholder="$t('sysmenu.inputPathTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '1'">
|
||||
<el-form-item :label="$t('sysmenu.permission')" prop="permission">
|
||||
<el-input v-model="state.ruleForm.permission" maxlength="50"
|
||||
:placeholder="$t('sysmenu.inputPermissionTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.sortOrder')" prop="sortOrder">
|
||||
<el-input-number v-model="state.ruleForm.sortOrder" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysmenu.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('sysmenu.inputIconTip')" v-model="state.ruleForm.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0' && showembedded">
|
||||
<el-form-item :label="$t('sysmenu.embedded')" prop="embedded">
|
||||
<el-radio-group v-model="state.ruleForm.embedded">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.keepAlive')" prop="keepAlive">
|
||||
<el-radio-group v-model="state.ruleForm.keepAlive">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-form-item :label="$t('sysmenu.visible')" prop="visible">
|
||||
<el-radio-group v-model="state.ruleForm.visible">
|
||||
<el-radio-button label="0">否</el-radio-button>
|
||||
<el-radio-button label="1">是</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button @click="onSubmit" type="primary">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemMenuDialog" setup>
|
||||
import {addObj, info, pageList, update} from "/@/api/admin/menu";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {rule} from "/@/utils/validate";
|
||||
<script setup lang="ts" name="systemMenuDialog">
|
||||
import { info, pageList, update, addObj } from "/@/api/admin/menu";
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义变量内容
|
||||
const menuDialogFormRef = ref();
|
||||
// 定义需要的数据
|
||||
const state = reactive({
|
||||
ruleForm: {
|
||||
id: '',
|
||||
menuId: '',
|
||||
name: '',
|
||||
permission: '',
|
||||
parentId: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
sortOrder: 0,
|
||||
menuType: '0',
|
||||
keepAlive: '0',
|
||||
visible: '1',
|
||||
embedded: '0',
|
||||
},
|
||||
parentData: [] as any[], // 上级菜单数据
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义变量内容
|
||||
const menuDialogFormRef = ref();
|
||||
// 定义需要的数据
|
||||
const state = reactive({
|
||||
ruleForm: {
|
||||
menuId: '',
|
||||
name: '',
|
||||
permission: '',
|
||||
parentId: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
sortOrder: 0,
|
||||
menuType: '0',
|
||||
keepAlive: '0',
|
||||
visible: '1',
|
||||
embedded: '0',
|
||||
},
|
||||
parentData: [] as any[], // 上级菜单数据
|
||||
});
|
||||
|
||||
// 从后端获取菜单信息
|
||||
const getMenuData = () => {
|
||||
state.parentData = []
|
||||
pageList({
|
||||
type: '0'
|
||||
}).then(res => {
|
||||
let menu = {
|
||||
createBy: "",
|
||||
createTime: "",
|
||||
delFlag: "",
|
||||
icon: "",
|
||||
keepAlive: "",
|
||||
menuId: "",
|
||||
menuType: "",
|
||||
parentId: "",
|
||||
path: "",
|
||||
embedded: '0',
|
||||
sortOrder: 0,
|
||||
updateBy: "",
|
||||
updateTime: "",
|
||||
visible: "1",
|
||||
id: '-1', name: '根菜单', children: []
|
||||
};
|
||||
menu.children = res.data;
|
||||
state.parentData.push(menu)
|
||||
})
|
||||
};
|
||||
|
||||
const showembedded = ref(false)
|
||||
|
||||
watch(() => state.ruleForm.path, (val) => {
|
||||
if (val.startsWith('http')) {
|
||||
showembedded.value = true
|
||||
} else {
|
||||
showembedded.value = false
|
||||
state.ruleForm.embedded = '0'
|
||||
}
|
||||
})
|
||||
|
||||
const dataRules = reactive({
|
||||
menType: [{ required: true, message: "菜单类型不能为空", trigger: "blur" }],
|
||||
parentId: [{ required: true, message: "上级菜单不能为空", trigger: "blur" }],
|
||||
name: [{ required: true, message: "菜单不能为空", trigger: "blur" }],
|
||||
path: [{ required: true, message: "路径不能为空", trigger: "blur" }],
|
||||
permission: [{ required: true, message: "权限代码不能为空", trigger: "blur" }],
|
||||
sortOrder: [{ required: true, message: "排序不能为空", trigger: "blur" }]
|
||||
})
|
||||
// 打开弹窗
|
||||
const openDialog = (type: string, row?: any) => {
|
||||
if (row?.id && type === 'edit') {
|
||||
state.ruleForm.menuId = row.id
|
||||
// 模拟数据,实际请走接口
|
||||
loading.value = true
|
||||
info(row.id).then(res => {
|
||||
Object.assign(state.ruleForm, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
// 清空表单,此项需加表单验证才能使用
|
||||
nextTick(() => {
|
||||
menuDialogFormRef?.value?.resetFields();
|
||||
state.ruleForm.parentId = row?.id || '-1'
|
||||
});
|
||||
|
||||
// 从后端获取菜单信息
|
||||
const getMenuData = () => {
|
||||
state.parentData = []
|
||||
pageList({
|
||||
type: '0'
|
||||
}).then(res => {
|
||||
let menu = {
|
||||
createBy: "",
|
||||
createTime: "",
|
||||
delFlag: "",
|
||||
icon: "",
|
||||
keepAlive: "",
|
||||
menuId: "",
|
||||
menuType: "",
|
||||
parentId: "",
|
||||
path: "",
|
||||
embedded: '0',
|
||||
sortOrder: 0,
|
||||
updateBy: "",
|
||||
updateTime: "",
|
||||
visible: "1",
|
||||
id: '-1', name: '根菜单', children: []
|
||||
};
|
||||
menu.children = res.data;
|
||||
state.parentData.push(menu)
|
||||
})
|
||||
};
|
||||
}
|
||||
visible.value = true;
|
||||
getMenuData();
|
||||
};
|
||||
|
||||
|
||||
const showembedded = ref(false)
|
||||
|
||||
|
||||
watch(() => state.ruleForm.path, (val) => {
|
||||
if (val.startsWith('http')) {
|
||||
showembedded.value = true
|
||||
} else {
|
||||
showembedded.value = false
|
||||
state.ruleForm.embedded = '0'
|
||||
}
|
||||
// 保存数据
|
||||
const onSubmit = () => {
|
||||
// 保存 调用刷新
|
||||
if (state.ruleForm.menuId) {
|
||||
loading.value = true
|
||||
update(state.ruleForm).then(() => {
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
}).catch(err => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
const dataRules = reactive({
|
||||
menType: [{required: true, message: "菜单类型不能为空", trigger: "blur"}],
|
||||
parentId: [{required: true, message: "上级菜单不能为空", trigger: "blur"}],
|
||||
name: [{required: true, message: "菜单名称不能为空", trigger: "blur"}],
|
||||
path: [{required: true, message: "路由路径不能为空", trigger: "blur"}
|
||||
, {validator: rule.noChinese, trigger: 'blur'}],
|
||||
permission: [{required: true, message: "排序不能为空", trigger: "blur"}],
|
||||
visible: [{required: true, message: "显示不能为空", trigger: "blur"}],
|
||||
sortOrder: [{required: true, message: "排序不能为空", trigger: "blur"}],
|
||||
keepAlive: [{required: true, message: "缓冲不能为空", trigger: "blur"}]
|
||||
} else {
|
||||
loading.value = true
|
||||
addObj(state.ruleForm).then(() => {
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
}).catch(err => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
// 打开弹窗
|
||||
const openDialog = (type: string, row?: any) => {
|
||||
if (row?.id && type === 'edit') {
|
||||
state.ruleForm.id = row.id
|
||||
// 模拟数据,实际请走接口
|
||||
loading.value = true
|
||||
info(row.id).then(res => {
|
||||
Object.assign(state.ruleForm, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
// 清空表单,此项需加表单验证才能使用
|
||||
nextTick(() => {
|
||||
menuDialogFormRef?.value?.resetFields();
|
||||
state.ruleForm.parentId = row?.id || '-1'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
visible.value = true;
|
||||
getMenuData();
|
||||
};
|
||||
};
|
||||
|
||||
// 保存数据
|
||||
const onSubmit = () => {
|
||||
// 保存 调用刷新
|
||||
if (state.ruleForm.id) {
|
||||
loading.value = true
|
||||
update(state.ruleForm).then(() => {
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
}).catch(err => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
loading.value = true
|
||||
addObj(state.ruleForm).then(() => {
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
}).catch(err => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// 暴露变量 只有暴漏出来的变量 父组件才能使用
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
// 暴露变量 只有暴漏出来的变量 父组件才能使用
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
@ -204,4 +204,9 @@ const handleDelete = (ids: string[]) => {
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
state.dataList
|
||||
})
|
||||
</script>
|
||||
|
@ -153,7 +153,6 @@ const handleClick = (tab: TabsPaneContext) => {
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['refreshDataList'])
|
||||
const visible = ref(false)
|
||||
|
||||
const sortable = ref() as any
|
||||
|
@ -24,7 +24,6 @@
|
||||
<script setup lang="ts" name="preview">
|
||||
import { useGeneratorPreviewApi } from '/@/api/gen/table';
|
||||
import { handleTree } from '/@/utils/other';
|
||||
import {validatePhone} from "/@/api/admin/user";
|
||||
|
||||
const visible = ref(false)
|
||||
// ======== 显示页面 ========
|
||||
|
@ -230,7 +230,7 @@ const initPieChart = () => {
|
||||
state.global.homeChartTwo = markRaw(echarts.init(homePieRef.value, state.charts.theme));
|
||||
var getname = ['房屋及结构物', '专用设备', '通用设备', '文物和陈列品', '图书、档案'];
|
||||
var getvalue = [34.2, 38.87, 17.88, 9.05, 2.05];
|
||||
var data = [];
|
||||
var data = [] as any;
|
||||
for (var i = 0; i < getname.length; i++) {
|
||||
data.push({ name: getname[i], value: getvalue[i] });
|
||||
}
|
||||
|
150
src/views/mp/wx-account-fans/form.vue
Normal file
150
src/views/mp/wx-account-fans/form.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :close-on-click-modal="false"
|
||||
:title="form.id ? $t('common.editBtn') : $t('common.addBtn')" draggable>
|
||||
<el-form ref="dataFormRef" v-loading="loading" :model="form" :rules="dataRules" label-width="90px">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.wxAccountName')" prop="wxAccountName">
|
||||
<el-input v-model="form.wxAccountName" disabled/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.wxAccountAppid')" prop="wxAccountAppid">
|
||||
<el-input v-model="form.wxAccountAppid" disabled/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.openid')" prop="openid">
|
||||
<el-input v-model="form.openid" disabled/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.nickname')" prop="nickname">
|
||||
<el-input v-model="form.nickname" disabled/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.remark')" prop="remark">
|
||||
<el-input v-model="form.remark" :placeholder="t('fans.inputremarkTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('fans.tagIds')" prop="tagIds">
|
||||
<el-select v-model="form.tagIds" :placeholder="t('fans.inputTagTip')" class="w100" clearable multiple>
|
||||
<el-option v-for="item in tagOption" :key="item.tagId" :label="item.tag" :value="item.tagId"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="wx-fans" setup>
|
||||
|
||||
import {addObj, getObj, putObj} from "/@/api/mp/wx-account-fans";
|
||||
import {list} from '/@/api/mp/wx-account-tag'
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {useI18n} from "vue-i18n"
|
||||
|
||||
const {t} = useI18n();
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const wxAccountAppid = ref()
|
||||
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: ''
|
||||
});
|
||||
|
||||
const dataRules = ref([])
|
||||
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any, accountId: string) => {
|
||||
visible.value = true
|
||||
form.id = row.id
|
||||
wxAccountAppid.value = accountId
|
||||
|
||||
// 重置表单数据
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields()
|
||||
}
|
||||
|
||||
if (form.id) {
|
||||
getFansData()
|
||||
}
|
||||
getTagList()
|
||||
};
|
||||
|
||||
|
||||
const getFansData = () => {
|
||||
loading.value = true
|
||||
getObj(form.id).then(res => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 提交
|
||||
const onSubmit = () => {
|
||||
dataFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
loading.value = true
|
||||
if (form.id) {
|
||||
putObj(form).then(() => {
|
||||
useMessage().success(t('common.editSuccessText'))
|
||||
visible.value = false // 关闭弹窗
|
||||
emit('refresh')
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
addObj(form).then(() => {
|
||||
useMessage().success(t('common.addSuccessText'))
|
||||
visible.value = false // 关闭弹窗
|
||||
emit('refresh')
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const tagOption = ref([])
|
||||
const getTagList = () => {
|
||||
list(wxAccountAppid.value).then(res => {
|
||||
tagOption.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
22
src/views/mp/wx-account-fans/i18n/en.ts
Normal file
22
src/views/mp/wx-account-fans/i18n/en.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
fans: {
|
||||
index: 'index',
|
||||
importwxAccountFansTip: 'import WxAccountFans',
|
||||
id: 'id',
|
||||
openid: 'openid',
|
||||
subscribeStatus: 'subscribeStatus',
|
||||
subscribeTime: 'subscribeTime',
|
||||
nickname: 'nickname',
|
||||
gender: 'gender',
|
||||
language: 'language',
|
||||
country: 'country',
|
||||
province: 'province',
|
||||
city: 'city',
|
||||
tagIds: 'tagIds',
|
||||
headimgUrl: 'headimgUrl',
|
||||
remark: 'remark',
|
||||
wxAccountId: 'wxAccountId',
|
||||
wxAccountName: 'wxAccountName',
|
||||
wxAccountAppid: 'wxAccountAppid'
|
||||
}
|
||||
}
|
24
src/views/mp/wx-account-fans/i18n/zh-cn.ts
Normal file
24
src/views/mp/wx-account-fans/i18n/zh-cn.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
fans: {
|
||||
index: '序号',
|
||||
importwxAccountFansTip: '导入微信公众号粉丝表',
|
||||
id: '主键',
|
||||
openid: '用户标识',
|
||||
subscribeStatus: '订阅状态',
|
||||
subscribeTime: '订阅时间',
|
||||
nickname: '昵称',
|
||||
gender: '性别',
|
||||
language: '语言',
|
||||
country: '国家',
|
||||
province: '省份',
|
||||
city: '城市',
|
||||
tagIds: '分组',
|
||||
headimgUrl: ' headimgUrl',
|
||||
remark: '备注',
|
||||
wxAccountId: '微信公众号ID',
|
||||
wxAccountName: '微信公众号',
|
||||
wxAccountAppid: '公众号appid',
|
||||
inputremarkTip: '请输入备注',
|
||||
inputTagTip: '请选择分组'
|
||||
}
|
||||
}
|
181
src/views/mp/wx-account-fans/index.vue
Normal file
181
src/views/mp/wx-account-fans/index.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-card class="layout-padding-auto">
|
||||
<el-row v-show="showSearch" class="mb8">
|
||||
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
|
||||
<el-form-item :label="$t('fans.nickname')" prop="nickname">
|
||||
<el-input v-model="state.queryForm.nickname"
|
||||
style="max-width: 180px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('fans.wxAccountName')" prop="wxAccountAppid">
|
||||
<el-select v-model="state.queryForm.wxAccountAppid" :placeholder="$t('fans.wxAccountName')" clearable class="w100">
|
||||
<el-option v-for="item in accountList" :key="item.appid" :label="item.name" :value="item.appid"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="ml2">
|
||||
<el-button formDialogRef icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button formDialogRef icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
<el-button formDialogRef icon="Sort" @click="asyncFans" v-auth="'mp_wxaccountfans_sync'">同步</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button v-auth="'mp_fans_export'" class="ml10" formDialogRef icon="Download" type="primary"
|
||||
@click="exportExcel">
|
||||
{{ $t('common.exportBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" style="float: right;margin-right: 20px"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%"
|
||||
@selection-change="handleSelectionChange" @sort-change="sortChangeHandle">
|
||||
<el-table-column align="center" type="selection" width="60"/>
|
||||
<el-table-column :label="t('fans.index')" type="index" width="80"/>
|
||||
<el-table-column :label="t('fans.id')" prop="id" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.openid')" prop="openid" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.subscribeStatus')" prop="subscribeStatus" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="subscribe" :value="scope.row.subscribeStatus"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('fans.subscribeTime')" prop="subscribeTime" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.nickname')" prop="nickname" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.language')" prop="language" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.tagIds')" prop="tagIds" show-overflow-tooltip width="200">
|
||||
<template #default="scope">
|
||||
<span v-for="(tag, index) in scope.row.tagList" :key="index">
|
||||
<el-tag>{{ tag.tag }} </el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('fans.remark')" prop="remark" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('fans.wxAccountName')" prop="wxAccountName" show-overflow-tooltip/>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary"
|
||||
@click="formDialogRef.openDialog(scope.row,state.queryForm.wxAccountAppid)">{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<el-button text type="primary" @click="handleDelete([scope.row.id])">{{
|
||||
$t('common.delBtn')
|
||||
}}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList"></form-dialog>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxAccountFans" setup>
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {delObjs, fetchList, sync} from "/@/api/mp/wx-account-fans";
|
||||
import { fetchAccountList } from '/@/api/mp/wx-account'
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {useDict} from "/@/hooks/dict";
|
||||
|
||||
const FormDialog = defineAsyncComponent(() => import("./form.vue"))
|
||||
|
||||
const { subscribe } = useDict('subscribe')
|
||||
|
||||
// 引入组件
|
||||
const {t} = useI18n()
|
||||
// 定义查询字典
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList,
|
||||
createdIsNeed: false
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile
|
||||
} = useTable(state)
|
||||
|
||||
const accountList = ref([])
|
||||
|
||||
const getAccountList = () => {
|
||||
fetchAccountList().then(res => {
|
||||
accountList.value = res.data
|
||||
if(accountList.value.length > 0){
|
||||
state.queryForm.wxAccountAppid = accountList.value[0].appid
|
||||
getDataList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => state.queryForm.wxAccountAppid,() => {
|
||||
getDataList()
|
||||
})
|
||||
|
||||
const asyncFans = () => {
|
||||
if(state.queryForm.wxAccountAppid){
|
||||
sync(state.queryForm.wxAccountAppid).then(() => {
|
||||
useMessage().success("已开始从微信同步粉丝信息,建议等待后查询")
|
||||
getDataList()
|
||||
})
|
||||
}else{
|
||||
useMessage().error("请选择公众号")
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAccountList()
|
||||
})
|
||||
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/mp/fans/export', state.queryForm, 'fans.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: any) => {
|
||||
objs.forEach((val: any) => {
|
||||
selectObjs.value.push(val.id)
|
||||
});
|
||||
multiple.value = !objs.length
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox().confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids).then(() => {
|
||||
getDataList(false);
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
})
|
||||
})
|
||||
};
|
||||
</script>
|
80
src/views/mp/wx-account-tag/form.vue
Normal file
80
src/views/mp/wx-account-tag/form.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :close-on-click-modal="false"
|
||||
:title="form.id ? $t('common.editBtn') : $t('common.addBtn')" draggable>
|
||||
<el-form ref="dataFormRef" v-loading="loading" :model="form" :rules="dataRules" formDialogRef label-width="90px">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('wxAccountTag.tag')" prop="tag">
|
||||
<el-input v-model="form.tag" :placeholder="t('wxAccountTag.inputTagTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="WxAccountTagDialog" setup>
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {addObj} from '/@/api/mp/wx-account-tag'
|
||||
import {useI18n} from "vue-i18n"
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义字典
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
wxAccountAppid: '',
|
||||
tag: '',
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
form.wxAccountAppid = id
|
||||
|
||||
// 重置表单数据
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields()
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = () => {
|
||||
dataFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
loading.value = true
|
||||
addObj(form).then(() => {
|
||||
useMessage().success(t('common.addSuccessText'))
|
||||
visible.value = false // 关闭弹窗
|
||||
emit('refresh')
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
18
src/views/mp/wx-account-tag/i18n/en.ts
Normal file
18
src/views/mp/wx-account-tag/i18n/en.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
wxAccountTag: {
|
||||
index: 'index',
|
||||
importwxAccountTagTip: 'import WxAccountTag',
|
||||
id: 'id',
|
||||
tag: 'tag',
|
||||
wxAccountId: 'wxAccountId',
|
||||
wxAccountName: 'wxAccountName',
|
||||
wxAccountAppid: 'wxAccountAppid',
|
||||
tagId: 'tagId',
|
||||
inputIdTip: 'input id',
|
||||
inputTagTip: 'input tag',
|
||||
inputWxAccountIdTip: 'input wxAccountId',
|
||||
inputWxAccountNameTip: 'input wxAccountName',
|
||||
inputWxAccountAppidTip: 'input wxAccountAppid',
|
||||
inputTagIdTip: 'input tagId',
|
||||
}
|
||||
}
|
18
src/views/mp/wx-account-tag/i18n/zh-cn.ts
Normal file
18
src/views/mp/wx-account-tag/i18n/zh-cn.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
wxAccountTag: {
|
||||
index: '序号',
|
||||
importwxAccountTagTip: '导入标签管理',
|
||||
id: '主键',
|
||||
tag: '标签',
|
||||
wxAccountId: '微信账号ID',
|
||||
wxAccountName: '微信账号名称',
|
||||
wxAccountAppid: 'appID',
|
||||
tagId: '标签ID',
|
||||
inputIdTip: '请输入主键',
|
||||
inputTagTip: '请输入标签',
|
||||
inputWxAccountIdTip: '请输入微信账号ID',
|
||||
inputWxAccountNameTip: '请输入微信账号名称',
|
||||
inputWxAccountAppidTip: '请输入appID',
|
||||
inputTagIdTip: '请输入标签ID',
|
||||
}
|
||||
}
|
155
src/views/mp/wx-account-tag/index.vue
Normal file
155
src/views/mp/wx-account-tag/index.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-card class="layout-padding-auto">
|
||||
<el-row v-show="showSearch" class="mb8">
|
||||
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
|
||||
<el-form-item :label="$t('wxAccountTag.tag')" prop="tag">
|
||||
<el-input v-model="state.queryForm.tag" :placeholder="t('wxAccountTag.inputTagTip')"
|
||||
style="max-width: 180px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('wxAccountTag.wxAccountAppid')" prop="wxAccountAppid">
|
||||
<el-select v-model="state.queryForm.wxAccountAppid" :placeholder="t('wxAccountTag.inputWxAccountAppidTip')" clearable class="w100">
|
||||
<el-option v-for="item in accountList" :key="item.appid" :label="item.name" :value="item.appid"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="ml2">
|
||||
<el-button formDialogRef icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button formDialogRef icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button v-auth="'mp_wx_account_tag_add'" class="ml10" formDialogRef icon="folder-add" type="primary"
|
||||
@click="formDialogRef.openDialog(state.queryForm.wxAccountAppid)">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'mp_wx_account_tag_export'" class="ml10" formDialogRef icon="Download" type="primary"
|
||||
@click="exportExcel">
|
||||
{{ $t('common.exportBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'mp_wx_account_tag_del'" :disabled="multiple" class="ml10" formDialogRef icon="Delete"
|
||||
type="primary" @click="handleDelete(selectObjs)">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" style="float: right;margin-right: 20px"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%"
|
||||
@selection-change="handleSelectionChange" @sort-change="sortChangeHandle">
|
||||
<el-table-column align="center" type="selection" width="60"/>
|
||||
<el-table-column :label="t('wxAccountTag.index')" type="index" width="80"/>
|
||||
<el-table-column :label="t('wxAccountTag.tag')" prop="tag" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxAccountTag.wxAccountId')" prop="wxAccountId" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxAccountTag.wxAccountName')" prop="wxAccountName" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxAccountTag.wxAccountAppid')" prop="wxAccountAppid" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxAccountTag.tagId')" prop="tagId" show-overflow-tooltip/>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" @click="formDialogRef.openDialog(scope.row.id)">{{ $t('common.editBtn') }}</el-button>
|
||||
<el-button text type="primary" @click="handleDelete([scope.row.id])">{{$t('common.delBtn') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-card>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxAccountTag" setup>
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {delObjs, getPage} from "/@/api/mp/wx-account-tag";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {fetchAccountList} from "/@/api/mp/wx-account";
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const {t} = useI18n()
|
||||
// 定义查询字典
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false
|
||||
})
|
||||
|
||||
const accountList = ref([])
|
||||
|
||||
const getAccountList = () => {
|
||||
fetchAccountList().then(res => {
|
||||
accountList.value = res.data
|
||||
if(accountList.value.length > 0){
|
||||
state.queryForm.wxAccountAppid = accountList.value[0].appid
|
||||
getDataList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAccountList()
|
||||
})
|
||||
|
||||
watch(() => state.queryForm.wxAccountAppid,() => {
|
||||
getDataList()
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/mp/wxAccountTag/export', state.queryForm, 'wxAccountTag.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: any) => {
|
||||
objs.forEach((val: any) => {
|
||||
selectObjs.value.push(val.id)
|
||||
});
|
||||
multiple.value = !objs.length
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox().confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids).then(() => {
|
||||
getDataList(false);
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
})
|
||||
})
|
||||
};
|
||||
</script>
|
161
src/views/mp/wx-account/form.vue
Normal file
161
src/views/mp/wx-account/form.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" :title="form.id ? $t('common.editBtn') : $t('common.addBtn')" size="50%">
|
||||
<el-form ref="dataFormRef" v-loading="loading" :model="form" :rules="dataRules" formDialogRef label-width="90px">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.name')" prop="name">
|
||||
<el-input v-model="form.name" :placeholder="t('account.inputNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.account')" prop="account">
|
||||
<el-input v-model="form.account" :placeholder="t('account.inputAccountTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.appid')" prop="appid">
|
||||
<el-input v-model="form.appid" :placeholder="t('account.inputAppidTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.appsecret')" prop="appsecret">
|
||||
<el-input v-model="form.appsecret" :placeholder="t('account.inputAppsecretTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.url')" prop="url">
|
||||
<el-input v-model="form.url" :placeholder="t('account.inputUrlTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.token')" prop="token">
|
||||
<el-input v-model="form.token" :placeholder="t('account.inputTokenTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('account.aeskey')" prop="aeskey">
|
||||
<el-input v-model="form.aeskey" :placeholder="t('account.inputAeskeyTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="WxAccountDialog" setup>
|
||||
// 定义子组件向父组件传值/事件
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {addObj, getObj, putObj} from '/@/api/mp/wx-account'
|
||||
import {useI18n} from "vue-i18n"
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义字典
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
account: '',
|
||||
appid: '',
|
||||
appsecret: '',
|
||||
url: '',
|
||||
token: '',
|
||||
aeskey: ''
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
name: [{required: true, message: '名称不能为空', trigger: 'blur'}],
|
||||
account: [{required: true, message: '微信号不能为空', trigger: 'blur'}],
|
||||
appid: [{required: true, message: 'appid不能为空', trigger: 'blur'}],
|
||||
appsecret: [{required: true, message: '密钥不能为空', trigger: 'blur'}],
|
||||
url: [{required: true, message: 'url不能为空', trigger: 'blur'}],
|
||||
token: [{required: true, message: 'token不能为空', trigger: 'blur'}],
|
||||
aeskey: [{required: true, message: '加密密钥不能为空', trigger: 'blur'}]
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
form.id = ''
|
||||
|
||||
// 重置表单数据
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields()
|
||||
}
|
||||
|
||||
// 获取wxAccount信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getwxAccountData(id)
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = () => {
|
||||
dataFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
// 更新
|
||||
if (form.id) {
|
||||
loading.value = true
|
||||
putObj(form).then(() => {
|
||||
useMessage().success(t('common.editSuccessText'))
|
||||
visible.value = false // 关闭弹窗
|
||||
emit('refresh')
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
loading.value = true
|
||||
addObj(form).then(() => {
|
||||
useMessage().success(t('common.addSuccessText'))
|
||||
visible.value = false // 关闭弹窗
|
||||
emit('refresh')
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化表单数据
|
||||
const getwxAccountData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
getObj(id).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
24
src/views/mp/wx-account/i18n/en.ts
Normal file
24
src/views/mp/wx-account/i18n/en.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
account: {
|
||||
index: 'index',
|
||||
importwxAccountTip: 'import WxAccount',
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
account: 'account',
|
||||
appid: 'appid',
|
||||
appsecret: 'appsecret',
|
||||
url: 'url',
|
||||
token: 'token',
|
||||
aeskey: 'aeskey',
|
||||
qrUrl: 'qrUrl',
|
||||
inputIdTip: 'input id',
|
||||
inputNameTip: 'input name',
|
||||
inputAccountTip: 'input account',
|
||||
inputAppidTip: 'input appid',
|
||||
inputAppsecretTip: 'input appsecret',
|
||||
inputUrlTip: 'input url',
|
||||
inputTokenTip: 'input token',
|
||||
inputAeskeyTip: 'input aeskey',
|
||||
inputQrUrlTip: 'input qrUrl',
|
||||
}
|
||||
}
|
24
src/views/mp/wx-account/i18n/zh-cn.ts
Normal file
24
src/views/mp/wx-account/i18n/zh-cn.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export default {
|
||||
account: {
|
||||
index: '序号',
|
||||
importwxAccountTip: '导入公众号账户表',
|
||||
id: '主键',
|
||||
name: '名称',
|
||||
account: '微信号',
|
||||
appid: 'appid',
|
||||
appsecret: '密钥',
|
||||
url: ' url',
|
||||
token: 'token',
|
||||
aeskey: '加密密钥',
|
||||
qrUrl: '图片',
|
||||
inputIdTip: '请输入主键',
|
||||
inputNameTip: '请输入名称',
|
||||
inputAccountTip: '请输入微信号',
|
||||
inputAppidTip: '请输入appid',
|
||||
inputAppsecretTip: '请输入密钥',
|
||||
inputUrlTip: '请输入 url',
|
||||
inputTokenTip: '请输入token',
|
||||
inputAeskeyTip: '请输入加密密钥',
|
||||
inputQrUrlTip: '请输入图片',
|
||||
}
|
||||
}
|
172
src/views/mp/wx-account/index.vue
Normal file
172
src/views/mp/wx-account/index.vue
Normal file
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-card class="layout-padding-auto">
|
||||
<el-row v-show="showSearch" class="mb8">
|
||||
<el-form ref="queryRef" :inline="true" :model="state.queryForm">
|
||||
<el-form-item :label="$t('account.name')" prop="name">
|
||||
<el-input v-model="state.queryForm.name" :placeholder="t('account.inputNameTip')"
|
||||
style="max-width: 180px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('account.account')" prop="account">
|
||||
<el-input v-model="state.queryForm.account" :placeholder="t('account.inputAccountTip')"
|
||||
style="max-width: 180px"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="ml2">
|
||||
<el-button formDialogRef icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button formDialogRef icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button v-auth="'mp_wxaccount_add'" class="ml10" formDialogRef icon="folder-add" type="primary"
|
||||
@click="formDialogRef.openDialog()">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'mp_wxaccount_export'" class="ml10" formDialogRef icon="Download" type="primary"
|
||||
@click="exportExcel">
|
||||
{{ $t('common.exportBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'mp_wxaccount_del'" :disabled="multiple" class="ml10" formDialogRef icon="Delete"
|
||||
type="primary" @click="handleDelete(selectObjs)">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" style="float: right;margin-right: 20px"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%"
|
||||
@selection-change="handleSelectionChange" @sort-change="sortChangeHandle">
|
||||
<el-table-column align="center" type="selection" width="60"/>
|
||||
<el-table-column :label="t('account.index')" type="index" width="80"/>
|
||||
<el-table-column :label="t('account.name')" prop="name" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.account')" prop="account" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.appid')" prop="appid" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.appsecret')" prop="appsecret" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.url')" prop="url" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.token')" prop="token" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.aeskey')" prop="aeskey" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('account.qrUrl')" prop="qrUrl" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<a target="_blank" :href="scope.row.qrUrl"><img :src="scope.row.qrUrl" style="width: 100px"></a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.action')" width="200">
|
||||
<template #default="scope">
|
||||
<el-button v-auth="'mp_wxaccount_edit'" text type="primary" @click="formDialogRef.openDialog(scope.row.id)">{{ $t('common.editBtn') }}</el-button>
|
||||
<el-button v-auth="'mp_wxaccount_del'" text type="primary" @click="handleDelete([scope.row.id])">{{$t('common.delBtn') }}</el-button>
|
||||
<el-button text type="primary" @click="access(scope.row,scope.index)">接入</el-button>
|
||||
<el-button text type="primary" @click="generate(scope.row,scope.index)">二维码</el-button>
|
||||
<el-button text type="primary" @click="quota(scope.row,scope.index)">quota</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-card>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)"/>
|
||||
|
||||
<el-dialog v-model="dialogFormVisible" title="接入">
|
||||
<el-input v-model="wxurl" readonly>
|
||||
<template #append>
|
||||
<el-button @click="copyText(wxurl)">复制链接</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxAccount" setup>
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {clearQuota, delObjs, fetchList, generateQr} from "/@/api/mp/wx-account";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import commonFunction from '/@/utils/commonFunction';
|
||||
|
||||
const { copyText } = commonFunction();
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const {t} = useI18n()
|
||||
// 定义查询字典
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/pigx/account/export', state.queryForm, 'account.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: any) => {
|
||||
objs.forEach((val: any) => {
|
||||
selectObjs.value.push(val.id)
|
||||
});
|
||||
multiple.value = !objs.length
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox().confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids).then(() => {
|
||||
getDataList(false);
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
const dialogFormVisible = ref(false)
|
||||
const wxurl = ref("")
|
||||
const access = (row: any) => {
|
||||
dialogFormVisible.value = true
|
||||
wxurl.value = row.url + '/mp/' + row.appid + '/portal'
|
||||
}
|
||||
|
||||
const generate = (row: any) => {
|
||||
generateQr(row.appid).then(() => {
|
||||
useMessage().success("获取成功")
|
||||
getDataList()
|
||||
})
|
||||
}
|
||||
|
||||
const quota = (row) => {
|
||||
clearQuota(row.appid).then(() => {
|
||||
useMessage().success("清空api的调用quota成功")
|
||||
})
|
||||
}
|
||||
</script>
|
347
src/views/mp/wx-auto-reply/index.vue
Normal file
347
src/views/mp/wx-auto-reply/index.vue
Normal file
@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4" :xs="24">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<query-tree :query="deptData.queryList"
|
||||
@node-click="handleNodeClick"/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :md="20">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<el-tabs v-model="type" @tab-click="handleClick">
|
||||
<el-tab-pane name="1" label="1">
|
||||
<template #label>关注时回复</template>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button class="ml10" icon="folder-add" type="primary" @click="handleAdd">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%" @sort-change="sortChangeHandle">
|
||||
<el-table-column label="序号" type="index" width="80"/>
|
||||
<el-table-column label="回复消息类型" prop="repType" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="dicDataRepType" :value="scope.row.repType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" prop="action" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(scope.row)">编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
icon="el-icon-delete"
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="2" label="2">
|
||||
<template #label>消息回复</template>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button class="ml10" icon="folder-add" type="primary" @click="handleAdd">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%" @sort-change="sortChangeHandle">
|
||||
<el-table-column label="序号" type="index" width="80"/>
|
||||
<el-table-column label="请求消息类型" prop="reqType" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="dicDataReqType" :value="scope.row.reqType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回复消息类型" prop="repType" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="dicDataRepType" :value="scope.row.repType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" prop="action" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="el-icon-edit"
|
||||
link
|
||||
@click="handleEdit(scope.row)">编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-delete"
|
||||
link
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="3" label="3">
|
||||
<template #label>关键词回复</template>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button class="ml10" icon="folder-add" type="primary" @click="handleAdd">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%" @sort-change="sortChangeHandle">
|
||||
<el-table-column label="序号" type="index" width="80"/>
|
||||
<el-table-column label="关键词" prop="reqKey" show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column label="匹配类型" prop="repMate" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="dicRepMate" :value="scope.row.repMate"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="匹配类型" prop="repMate" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="dicDataRepType" :value="scope.row.repType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" prop="action" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="el-icon-edit"
|
||||
link
|
||||
@click="handleEdit(scope.row)">编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-delete"
|
||||
link
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog :title="handleType === 'add' ? '新增回复消息' : '修改回复消息'" v-model="dialog1Visible" width="50%">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item v-if="type === '2'" label="请求消息类型">
|
||||
<el-select v-model="objData.reqType" placeholder="请选择">
|
||||
<template v-for="item in dicDataReqType">
|
||||
<el-option
|
||||
v-if="item.value !== 'event'"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled">
|
||||
</el-option>
|
||||
</template>
|
||||
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="type === '3'" label="匹配类型">
|
||||
<el-select v-model="objData.repMate" placeholder="请选择" style="width: 100px">
|
||||
<el-option
|
||||
v-for="item in dicRepMate"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="type === '3'" label="关键词">
|
||||
<el-input v-model="objData.reqKey" placeholder="请输入内容" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="回复消息">
|
||||
<WxReply v-if="hackResetWxReplySelect" :obj-data="objData"></WxReply>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer >
|
||||
<el-button @click="dialog1Visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-auto-reply">
|
||||
|
||||
import {fetchAccountList} from "/@/api/mp/wx-account";
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {getPage,delObj,addObj,putObj} from '/@/api/mp/wx-auto-reply'
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'))
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import("/@/components/wechart/wx-reply/index.vue"))
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (node: any) => {
|
||||
accountId.value = node.appid
|
||||
state.queryForm.appId = accountId.value
|
||||
getDataList()
|
||||
}
|
||||
|
||||
const dicDataRepType = ref([{
|
||||
label: '文本',
|
||||
value: 'text'
|
||||
}, {
|
||||
label: '图片',
|
||||
value: 'image'
|
||||
}, {
|
||||
label: '语音',
|
||||
value: 'voice'
|
||||
}, {
|
||||
label: '视频',
|
||||
value: 'video'
|
||||
}, {
|
||||
label: '图文',
|
||||
value: 'news'
|
||||
}])
|
||||
|
||||
|
||||
const dicDataReqType = ref(
|
||||
[{
|
||||
value: 'text',
|
||||
label: '文本'
|
||||
}, {
|
||||
value: 'image',
|
||||
label: '图片'
|
||||
}, {
|
||||
value: 'voice',
|
||||
label: '语音'
|
||||
}, {
|
||||
value: 'video',
|
||||
label: '视频'
|
||||
}, {
|
||||
value: 'shortvideo',
|
||||
label: '小视频'
|
||||
}, {
|
||||
value: 'location',
|
||||
label: '地理位置'
|
||||
}, {
|
||||
value: 'link',
|
||||
label: '链接消息'
|
||||
}, {
|
||||
value: 'event',
|
||||
label: '事件推送'
|
||||
}]
|
||||
)
|
||||
const dicRepMate = ref([
|
||||
{
|
||||
value: '1',
|
||||
label: '全匹配'
|
||||
}, {
|
||||
value: '2',
|
||||
label: '半匹配'
|
||||
}
|
||||
])
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: () => {
|
||||
return fetchAccountList()
|
||||
}
|
||||
})
|
||||
|
||||
const accountId = ref()
|
||||
|
||||
const type = ref("1")
|
||||
|
||||
const handleClick = (e: any) => {
|
||||
type.value = e.paneName
|
||||
state.queryForm.type = type.value
|
||||
state.queryForm.appId = accountId.value
|
||||
getDataList()
|
||||
|
||||
}
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
type: "",
|
||||
appId: ""
|
||||
},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false
|
||||
})
|
||||
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
} = useTable(state)
|
||||
|
||||
const dialog1Visible = ref(false)
|
||||
|
||||
const handleType = ref('add')
|
||||
|
||||
const hackResetWxReplySelect = ref(true)
|
||||
|
||||
const objData = ref() as any
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
hackResetWxReplySelect.value = false
|
||||
nextTick(() => {
|
||||
hackResetWxReplySelect.value = true
|
||||
})
|
||||
handleType.value = 'edit'
|
||||
dialog1Visible.value = true
|
||||
|
||||
if (row.content && typeof row.content === 'string') {
|
||||
row.content = JSON.parse(row.content)
|
||||
}
|
||||
objData.value = Object.assign({}, row)
|
||||
}
|
||||
|
||||
const handleDel = (row) => {
|
||||
useMessageBox().confirm("是否确认删除此数据?").then(() => {
|
||||
delObj(row.id).then(() =>{
|
||||
useMessage().success("删除成功")
|
||||
getDataList()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (objData.repType === 'news') {
|
||||
objData.content = JSON.stringify(objData.content)
|
||||
}
|
||||
if (handleType.value === 'add') {
|
||||
addObj(Object.assign({
|
||||
type: type.value,
|
||||
appId: accountId.value
|
||||
}, objData.value)).then(() => {
|
||||
useMessage().success("添加成功")
|
||||
getDataList()
|
||||
dialog1Visible.value = false
|
||||
})
|
||||
}
|
||||
if (handleType.value === 'edit') {
|
||||
putObj(objData.value).then(() => {
|
||||
useMessage().success("修改成功")
|
||||
getDataList()
|
||||
dialog1Visible.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
hackResetWxReplySelect.value = false//销毁组件
|
||||
nextTick(() => {
|
||||
hackResetWxReplySelect.value = true//重建组件
|
||||
})
|
||||
handleType.value = 'add'
|
||||
dialog1Visible.value = true
|
||||
objData.value = {
|
||||
repType: 'text',
|
||||
appId: accountId.value
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
64
src/views/mp/wx-fans-msg/i18n/en.ts
Normal file
64
src/views/mp/wx-fans-msg/i18n/en.ts
Normal file
@ -0,0 +1,64 @@
|
||||
export default {
|
||||
wxFansMsg: {
|
||||
index: 'index',
|
||||
importwxMsgTip: 'import WxMsg',
|
||||
id: 'id',
|
||||
appName: 'appName',
|
||||
appLogo: 'appLogo',
|
||||
wxUserId: 'wxUserId',
|
||||
nickName: 'nickName',
|
||||
headimgUrl: 'headimgUrl',
|
||||
type: 'type',
|
||||
repType: 'repType',
|
||||
repEvent: 'repEvent',
|
||||
repContent: 'repContent',
|
||||
repMediaId: 'repMediaId',
|
||||
repName: 'repName',
|
||||
repDesc: 'repDesc',
|
||||
repUrl: 'repUrl',
|
||||
repHqUrl: 'repHqUrl',
|
||||
content: 'content',
|
||||
repThumbMediaId: 'repThumbMediaId',
|
||||
repThumbUrl: 'repThumbUrl',
|
||||
repLocationX: 'repLocationX',
|
||||
repLocationY: 'repLocationY',
|
||||
repScale: 'repScale',
|
||||
readFlag: 'readFlag',
|
||||
appId: 'appId',
|
||||
openId: 'openId',
|
||||
remark: 'remark',
|
||||
delFlag: 'delFlag',
|
||||
createTime: 'createTime',
|
||||
updateTime: 'updateTime',
|
||||
tenantId: 'tenantId',
|
||||
inputIdTip: 'input id',
|
||||
inputAppNameTip: 'input appName',
|
||||
inputAppLogoTip: 'input appLogo',
|
||||
inputWxUserIdTip: 'input wxUserId',
|
||||
inputNickNameTip: 'input nickName',
|
||||
inputHeadimgUrlTip: 'input headimgUrl',
|
||||
inputTypeTip: 'input type',
|
||||
inputRepTypeTip: 'input repType',
|
||||
inputRepEventTip: 'input repEvent',
|
||||
inputRepContentTip: 'input repContent',
|
||||
inputRepMediaIdTip: 'input repMediaId',
|
||||
inputRepNameTip: 'input repName',
|
||||
inputRepDescTip: 'input repDesc',
|
||||
inputRepUrlTip: 'input repUrl',
|
||||
inputRepHqUrlTip: 'input repHqUrl',
|
||||
inputContentTip: 'input content',
|
||||
inputRepThumbMediaIdTip: 'input repThumbMediaId',
|
||||
inputRepThumbUrlTip: 'input repThumbUrl',
|
||||
inputRepLocationXTip: 'input repLocationX',
|
||||
inputRepLocationYTip: 'input repLocationY',
|
||||
inputRepScaleTip: 'input repScale',
|
||||
inputReadFlagTip: 'input readFlag',
|
||||
inputAppIdTip: 'input appId',
|
||||
inputOpenIdTip: 'input openId',
|
||||
inputRemarkTip: 'input remark',
|
||||
inputDelFlagTip: 'input delFlag',
|
||||
inputCreateTimeTip: 'input createTime',
|
||||
inputUpdateTimeTip: 'input updateTime',
|
||||
inputTenantIdTip: 'input tenantId',
|
||||
}
|
||||
}
|
64
src/views/mp/wx-fans-msg/i18n/zh-cn.ts
Normal file
64
src/views/mp/wx-fans-msg/i18n/zh-cn.ts
Normal file
@ -0,0 +1,64 @@
|
||||
export default {
|
||||
wxFansMsg: {
|
||||
index: '序号',
|
||||
importwxMsgTip: '导入微信消息',
|
||||
id: '主键',
|
||||
appName: '公众号名称',
|
||||
appLogo: '公众号logo',
|
||||
wxUserId: '微信用户ID',
|
||||
nickName: '微信用户昵称',
|
||||
headimgUrl: '微信用户头像',
|
||||
type: '消息分类',
|
||||
repType: '消息类型',
|
||||
repEvent: '事件类型',
|
||||
repContent: '内容',
|
||||
repMediaId: '回复类型',
|
||||
repName: '回复的素材名、视频和音乐的标题',
|
||||
repDesc: '视频和音乐的描述',
|
||||
repUrl: '链接',
|
||||
repHqUrl: '高质量链接',
|
||||
content: '图文消息的内容',
|
||||
repThumbMediaId: '缩略图的媒体id',
|
||||
repThumbUrl: '缩略图url',
|
||||
repLocationX: '地理位置维度',
|
||||
repLocationY: '地理位置经度',
|
||||
repScale: '地图缩放大小',
|
||||
readFlag: '已读标记',
|
||||
appId: '公众号ID',
|
||||
openId: '微信唯一标识',
|
||||
remark: '备注',
|
||||
delFlag: '逻辑删除标记(0:显示;1:隐藏)',
|
||||
createTime: '创建时间',
|
||||
updateTime: '更新时间',
|
||||
tenantId: '租户ID',
|
||||
inputIdTip: '请输入主键',
|
||||
inputAppNameTip: '请输入公众号名称',
|
||||
inputAppLogoTip: '请输入公众号logo',
|
||||
inputWxUserIdTip: '请输入微信用户ID',
|
||||
inputNickNameTip: '请输入微信用户昵称',
|
||||
inputHeadimgUrlTip: '请输入微信用户头像',
|
||||
inputTypeTip: '请输入消息分类',
|
||||
inputRepTypeTip: '请输入消息类型',
|
||||
inputRepEventTip: '请输入事件类型',
|
||||
inputRepContentTip: '请输入回复类型文本保存文字、地理位置信息',
|
||||
inputRepMediaIdTip: '请输入回复类型',
|
||||
inputRepNameTip: '请输入回复的素材名、视频和音乐的标题',
|
||||
inputRepDescTip: '请输入视频和音乐的描述',
|
||||
inputRepUrlTip: '请输入链接',
|
||||
inputRepHqUrlTip: '请输入高质量链接',
|
||||
inputContentTip: '请输入图文消息的内容',
|
||||
inputRepThumbMediaIdTip: '请输入缩略图的媒体id',
|
||||
inputRepThumbUrlTip: '请输入缩略图url',
|
||||
inputRepLocationXTip: '请输入地理位置维度',
|
||||
inputRepLocationYTip: '请输入地理位置经度',
|
||||
inputRepScaleTip: '请输入地图缩放大小',
|
||||
inputReadFlagTip: '请输入已读标记(1:是;0:否)',
|
||||
inputAppIdTip: '请输入公众号ID',
|
||||
inputOpenIdTip: '请输入微信唯一标识',
|
||||
inputRemarkTip: '请输入备注',
|
||||
inputDelFlagTip: '请输入逻辑删除标记(0:显示;1:隐藏)',
|
||||
inputCreateTimeTip: '请输入创建时间',
|
||||
inputUpdateTimeTip: '请输入更新时间',
|
||||
inputTenantIdTip: '请输入租户ID',
|
||||
}
|
||||
}
|
193
src/views/mp/wx-fans-msg/index.vue
Normal file
193
src/views/mp/wx-fans-msg/index.vue
Normal file
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-card class="layout-padding-auto">
|
||||
<el-row v-show="showSearch" class="mb8">
|
||||
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
|
||||
<el-form-item :label="$t('wxFansMsg.appName')" prop="wxAccountAppid">
|
||||
<el-select v-model="state.queryForm.wxAccountAppid" :placeholder="$t('fans.appName')" clearable class="w100">
|
||||
<el-option v-for="item in accountList" :key="item.appid" :label="item.name" :value="item.appid"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('wxFansMsg.nickName')" prop="nickName">
|
||||
<el-input v-model="state.queryForm.nickName" :placeholder="t('wxFansMsg.inputNickNameTip')"
|
||||
style="max-width: 180px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('wxFansMsg.repType')" prop="repType">
|
||||
<el-select v-model="state.queryForm.repType" :placeholder="$t('wxFansMsg.repType')" clearable class="w100">
|
||||
<el-option v-for="item in repType" :key="item.value" :label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="ml2">
|
||||
<el-button formDialogRef icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button formDialogRef icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button v-auth="'mp_wxFansMsg_export'" class="ml10" formDialogRef icon="Download" type="primary"
|
||||
@click="exportExcel">
|
||||
{{ $t('common.exportBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'mp_wxmsg_del'" :disabled="multiple" class="ml10" formDialogRef icon="Delete"
|
||||
type="primary" @click="handleDelete(selectObjs)">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" style="float: right;margin-right: 20px"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" style="width: 100%"
|
||||
@selection-change="handleSelectionChange" @sort-change="sortChangeHandle">
|
||||
<el-table-column align="center" type="selection" width="60"/>
|
||||
<el-table-column :label="t('wxFansMsg.index')" type="index" width="80"/>
|
||||
<el-table-column :label="t('wxFansMsg.appName')" prop="appName" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxFansMsg.repType')" prop="repType" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxFansMsg.openId')" prop="openId" show-overflow-tooltip/>
|
||||
<el-table-column :label="t('wxFansMsg.repContent')" prop="repContent" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'subscribe'"><el-tag type="success" size="mini">关注</el-tag></div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'unsubscribe'"><el-tag type="danger" size="mini">取消关注</el-tag></div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'CLICK'"><el-tag size="mini">点击菜单</el-tag>:【{{ scope.row.repName }}】</div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'VIEW'"><el-tag size="mini">点击菜单链接</el-tag>:【{{ scope.row.repUrl }}】</div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'scancode_waitmsg'"><el-tag size="mini">扫码结果:</el-tag>:【{{ scope.row.repContent }}】</div>
|
||||
<div v-if="scope.row.repType === 'text'">{{ scope.row.repContent }}</div>
|
||||
<div v-if="scope.row.repType === 'image'">
|
||||
<a target="_blank" :href="scope.row.repUrl"><img :src="scope.row.repUrl" style="width: 100px"></a>
|
||||
</div>
|
||||
<div v-if="['video','voice','link','shortvideo'].includes(scope.row.repType)">
|
||||
<el-tag>链接</el-tag>:<a :href="scope.row.repUrl" target="_blank">{{ scope.row.repName }}</a></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('wxFansMsg.readFlag')" prop="readFlag" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="readFlag" :value="scope.row.readFlag"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('wxFansMsg.createTime')" prop="createTime" show-overflow-tooltip/>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="wxMsgDo(scope.row,scope.index)">消息</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-card>
|
||||
<wx-msg ref="WxmsgRef"></wx-msg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxMsg" setup>
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {delObjs, fetchList} from "/@/api/mp/wx-fans-msg";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {fetchAccountList} from "/@/api/mp/wx-account";
|
||||
|
||||
const WxMsg = defineAsyncComponent(() => import("/@/components/wechart/wx-msg/index.vue"))
|
||||
|
||||
const {t} = useI18n()
|
||||
// 定义查询字典
|
||||
|
||||
const {repType} = useDict('repType')
|
||||
|
||||
const readFlag = ref([
|
||||
{
|
||||
value: '1',
|
||||
label: '是'
|
||||
}, {
|
||||
value: '0',
|
||||
label: '否'
|
||||
}
|
||||
])
|
||||
|
||||
const WxmsgRef = ref()
|
||||
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList,
|
||||
descs: ['create_time']
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
const accountList = ref([])
|
||||
|
||||
const getAccountList = () => {
|
||||
fetchAccountList().then(res => {
|
||||
accountList.value = res.data
|
||||
if(accountList.value.length > 0){
|
||||
state.queryForm.wxAccountAppid = accountList.value[0].appid
|
||||
getDataList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => state.queryForm.wxAccountAppid,() => {
|
||||
getDataList()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getAccountList()
|
||||
})
|
||||
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/mp/wxFansMsg/export', state.queryForm, 'wxFansMsg.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: any) => {
|
||||
objs.forEach((val: any) => {
|
||||
selectObjs.value.push(val.id)
|
||||
});
|
||||
multiple.value = !objs.length
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox().confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids).then(() => {
|
||||
getDataList(false);
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
}).catch((err: any) => {
|
||||
useMessage().error(err.msg)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
const wxMsgDo = (row) => {
|
||||
WxmsgRef.value.openDialog({
|
||||
wxUserId: row.wxUserId,
|
||||
appId: row.appId
|
||||
})
|
||||
}
|
||||
</script>
|
549
src/views/mp/wx-material/components/news-form.vue
Normal file
549
src/views/mp/wx-material/components/news-form.vue
Normal file
@ -0,0 +1,549 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="operateMaterial === 'add'?'新建图文':'修改图文'"
|
||||
:before-close="dialogNewsClose"
|
||||
:close-on-click-modal="false"
|
||||
v-model="dialogNewsVisible"
|
||||
:destroy-on-close="true"
|
||||
width="80%"
|
||||
top="20px">
|
||||
<div class="left">
|
||||
<div class="select-item">
|
||||
<div v-for="(news, index) in articlesAdd" :key="news.id">
|
||||
<div v-if="index==0"
|
||||
class="news-main father"
|
||||
:class="{'activeAddNews': isActiveAddNews === index}"
|
||||
@click="activeNews(index)">
|
||||
<div class="news-content">
|
||||
<img v-if="news.thumbUrl" class="material-img" :src="news.thumbUrl" />
|
||||
<div class="news-content-title">{{ news.title }}</div>
|
||||
</div>
|
||||
<div v-if="articlesAdd.length>1" class="child">
|
||||
<el-button icon="el-icon-top" @click="downNews(index)">下移</el-button>
|
||||
<el-button
|
||||
v-if="operateMaterial=='add'"
|
||||
icon="el-icon-delete"
|
||||
@click="minusNews(index)">删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="index>0"
|
||||
class="news-main-item father"
|
||||
: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 v-if="news.thumbUrl" class="material-img" :src="news.thumbUrl" height="100%" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="child">
|
||||
<el-button
|
||||
v-if="articlesAdd.length > index+1"
|
||||
icon="el-icon-sort-down"
|
||||
@click="downNews(index)">下移
|
||||
</el-button>
|
||||
<el-button icon="el-icon-sort-up" @click="upNews(index)">上移</el-button>
|
||||
<el-button
|
||||
v-if="operateMaterial=='add'"
|
||||
icon="el-icon-delete"
|
||||
@click="minusNews(index)">删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="articlesAdd.length<8 && operateMaterial=='add'" class="news-main-plus" @click="plusNews">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-loading="addMaterialLoading" class="right">
|
||||
<!--富文本编辑器组件-->
|
||||
<el-row>
|
||||
<editor v-model:get-html="articlesAdd[isActiveAddNews].content" style="margin-top: 20px"></editor>
|
||||
</el-row>
|
||||
<br><br><br><br>
|
||||
<div class="input-tt">封面和摘要:</div>
|
||||
<div>
|
||||
<div class="thumb-div">
|
||||
<img v-if="articlesAdd[isActiveAddNews].thumbUrl" class="material-img" :src="articlesAdd[isActiveAddNews].thumbUrl" :class="isActiveAddNews === 0 ? 'avatar':'avatar1'">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" :class="isActiveAddNews === 0 ? 'avatar':'avatar1'"></i>
|
||||
<div class="thumb-but">
|
||||
<wx-file-upload :uploadData="uploadData" @success="handleImageChange"></wx-file-upload>
|
||||
<el-button type="primary" @click="openMaterial">素材库选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="articlesAdd[isActiveAddNews].digest"
|
||||
:rows="8"
|
||||
type="textarea"
|
||||
placeholder="请输入摘要"
|
||||
class="digest"
|
||||
maxlength="120"></el-input>
|
||||
</div>
|
||||
<div class="input-tt">标题:</div>
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题"></el-input>
|
||||
<div class="input-tt">作者:</div>
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].author" placeholder="请输入作者"></el-input>
|
||||
<div class="input-tt">原文地址:</div>
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].contentSourceUrl" placeholder="请输入原文地址"></el-input>
|
||||
</div>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogNewsVisible = false" >{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" >{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<wx-material-select ref="WxMaterialSelectRef"></wx-material-select>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-news-form">
|
||||
|
||||
|
||||
import {useMessageBox} from "/@/hooks/message";
|
||||
import {addObj, materialNewsUpdate} from '/@/api/mp/wx-material'
|
||||
|
||||
const WxMaterialSelect = defineAsyncComponent(() => import("/@/components/wechart/wx-material-select/main.vue"))
|
||||
|
||||
const WxFileUpload = defineAsyncComponent(() => import("/@/components/wechart/fileUpload/index.vue"))
|
||||
|
||||
const WxMaterialSelectRef = ref()
|
||||
|
||||
const dialogNewsVisible = ref(false)
|
||||
|
||||
const operateMaterial = ref("add")
|
||||
|
||||
const addMaterialLoading = ref(false)
|
||||
|
||||
// 定义刷新表格emit
|
||||
const emit = defineEmits(['ok']);
|
||||
|
||||
const dialogNewsClose = () => {
|
||||
useMessageBox().confirm("修改内容可能还未保存,确定关闭吗?").then(() => {
|
||||
dialogNewsVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 公众号id
|
||||
const accountId = ref()
|
||||
|
||||
// 文章数据
|
||||
const articlesAdd = ref([
|
||||
{
|
||||
'title': '',
|
||||
'thumbMediaId': '',
|
||||
'author': '',
|
||||
'digest': '',
|
||||
'showCoverPic': '',
|
||||
'content': '',
|
||||
'contentSourceUrl': '',
|
||||
'needOpenComment': '',
|
||||
'onlyFansCanComment': '',
|
||||
'thumbUrl': ''
|
||||
}
|
||||
])
|
||||
// 激活文章
|
||||
const isActiveAddNews = ref(0)
|
||||
// 编辑媒体的id
|
||||
const articlesMediaId = ref()
|
||||
|
||||
const openDialog = (data: any,item?: any,mediaId?: any,type: any = 'add') => {
|
||||
// 设置组件内不用账号
|
||||
accountId.value = data.accountId
|
||||
uploadData.appId = data.accountId
|
||||
|
||||
dialogNewsVisible.value = true
|
||||
operateMaterial.value = 'add'
|
||||
|
||||
if(item){
|
||||
articlesAdd.value = item
|
||||
}
|
||||
if(mediaId){
|
||||
articlesMediaId.value = mediaId || ''
|
||||
}
|
||||
if(type){
|
||||
operateMaterial.value = type
|
||||
}
|
||||
}
|
||||
|
||||
const uploadData = reactive({
|
||||
mediaType: 'image',
|
||||
title: '',
|
||||
introduction: '',
|
||||
appId: ''
|
||||
})
|
||||
|
||||
const openMaterial = () => {
|
||||
WxMaterialSelectRef.value.openDialog({
|
||||
type: 'image',
|
||||
accountId: accountId.value
|
||||
})
|
||||
}
|
||||
|
||||
const handleImageChange = (response) => {
|
||||
articlesAdd.value[isActiveAddNews.value].thumbMediaId = response.data.mediaId
|
||||
articlesAdd.value[isActiveAddNews.value].thumbUrl = response.data.url
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
addMaterialLoading.value = true
|
||||
if (operateMaterial.value === 'add') {
|
||||
addObj({
|
||||
articles: articlesAdd.value,
|
||||
appId: accountId.value
|
||||
}).then(() => {
|
||||
addMaterialLoading.value = false
|
||||
dialogNewsVisible.value = false
|
||||
isActiveAddNews.value = 0
|
||||
articlesAdd.value = [
|
||||
{
|
||||
'title': '',
|
||||
'thumbMediaId': '',
|
||||
'author': '',
|
||||
'digest': '',
|
||||
'showCoverPic': '',
|
||||
'content': '',
|
||||
'contentSourceUrl': '',
|
||||
'needOpenComment': '',
|
||||
'onlyFansCanComment': '',
|
||||
'thumbUrl': ''
|
||||
}
|
||||
]
|
||||
emit('ok',)
|
||||
}).finally(() => {
|
||||
addMaterialLoading.value = false
|
||||
})
|
||||
}
|
||||
if (operateMaterial.value === 'edit') {
|
||||
materialNewsUpdate({
|
||||
articles: articlesAdd.value,
|
||||
mediaId: articlesMediaId.value,
|
||||
appId: accountId.value
|
||||
}).then(() => {
|
||||
addMaterialLoading.value = false
|
||||
dialogNewsVisible.value = false
|
||||
isActiveAddNews.value = 0
|
||||
articlesAdd.value = [
|
||||
{
|
||||
'title': '',
|
||||
'thumbMediaId': '',
|
||||
'author': '',
|
||||
'digest': '',
|
||||
'showCoverPic': '',
|
||||
'content': '',
|
||||
'contentSourceUrl': '',
|
||||
'needOpenComment': '',
|
||||
'onlyFansCanComment': '',
|
||||
'thumbUrl': ''
|
||||
}
|
||||
]
|
||||
emit("ok")
|
||||
}).finally(() => {
|
||||
addMaterialLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const activeNews = (index) => {
|
||||
isActiveAddNews.value = index
|
||||
}
|
||||
|
||||
const minusNews = (index) => {
|
||||
useMessageBox().confirm("确定删除该图文吗?").then(() => {
|
||||
articlesAdd.value.splice(index, 1)
|
||||
if (isActiveAddNews.value === index) {
|
||||
isActiveAddNews.value = 0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const plusNews = () => {
|
||||
articlesAdd.value.push({
|
||||
'title': '',
|
||||
'thumbMediaId': '',
|
||||
'author': '',
|
||||
'digest': '',
|
||||
'showCoverPic': '',
|
||||
'content': '',
|
||||
'contentSourceUrl': '',
|
||||
'needOpenComment': '',
|
||||
'onlyFansCanComment': '',
|
||||
'thumbUrl': ''
|
||||
})
|
||||
isActiveAddNews.value = articlesAdd.value.length -1
|
||||
}
|
||||
|
||||
const downNews = (index) => {
|
||||
const temp = articlesAdd.value[index]
|
||||
articlesAdd.value[index] = articlesAdd.value[index + 1]
|
||||
articlesAdd.value[index + 1] = temp
|
||||
isActiveAddNews.value = index + 1
|
||||
}
|
||||
|
||||
const upNews = (index) => {
|
||||
const temp = articlesAdd[index]
|
||||
articlesAdd[index] = articlesAdd[index - 1]
|
||||
articlesAdd[index - 1] = temp
|
||||
isActiveAddNews.value = index - 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tree-position {
|
||||
margin: 12px 20px 0 0
|
||||
}
|
||||
|
||||
.pagination {
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.add_but {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eaeaea;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
.left {
|
||||
display: inline-block;
|
||||
width: 35%;
|
||||
vertical-align: top;
|
||||
margin-top: 200px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.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:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 230px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar1 {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.digest {
|
||||
width: 60%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap: 10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
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) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*瀑布流样式*/
|
||||
.news-main {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.news-main-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
margin-left: -3px
|
||||
}
|
||||
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae
|
||||
}
|
||||
|
||||
.input-tt {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.activeAddNews {
|
||||
border: 5px solid #2bb673;
|
||||
}
|
||||
|
||||
.news-main-plus {
|
||||
width: 280px;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.icon-plus {
|
||||
margin: 10px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.select-item {
|
||||
width: 60%;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.father .child {
|
||||
display: none;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumb-but {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
556
src/views/mp/wx-material/index.vue
Normal file
556
src/views/mp/wx-material/index.vue
Normal file
@ -0,0 +1,556 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="4">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<query-tree :query="deptData.queryList"
|
||||
@node-click="handleNodeClick"/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<el-tabs v-model="materialType" @tab-click="handleClick">
|
||||
<el-tab-pane name="image" label="image">
|
||||
<template #label><i class="el-icon-picture"></i> 图片</template>
|
||||
<div class="add_but">
|
||||
<wx-file-upload @success="getDataList" :uploadData="uploadData" :type="['image/jpeg','image/png','image/gif','image/bmp','image/jpg']"></wx-file-upload>
|
||||
</div>
|
||||
<div v-loading="state.loading" class="waterfall">
|
||||
<div v-for="item in state.dataList" :key="item.id" class="waterfall-item">
|
||||
<a target="_blank" :href="item.url">
|
||||
<img class="material-img" :src="item.url">
|
||||
<div class="item-name">{{ item.name }}</div>
|
||||
</a>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="delMaterial(item)"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="state.dataList.length <=0 && !state.loading" class="el-table__empty-block">
|
||||
<span class="el-table__empty-text">暂无数据</span>
|
||||
</div>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="voice" label="voice">
|
||||
<template #label><i class="el-icon-microphone"></i> 语音</template>
|
||||
<div class="add_but">
|
||||
<wx-file-upload @success="getDataList" :uploadData="uploadData"></wx-file-upload>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="state.loading"
|
||||
:data="state.dataList"
|
||||
stripe
|
||||
border>
|
||||
<el-table-column
|
||||
prop="mediaId"
|
||||
label="media_id">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="名称">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="updateTime"
|
||||
label="更新时间">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作">
|
||||
<template v-slot="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
@click="handleDown(scope.row)">下载
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
@click="delMaterial(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="video" label="video">
|
||||
<template #label><i class="el-icon-video-play"></i> 视频</template>
|
||||
<div class="add_but">
|
||||
<el-button type="primary" @click="handleAddVideo">新建</el-button>
|
||||
</div>
|
||||
<el-dialog title="新建视频" v-model="dialogVideoVisible">
|
||||
<wx-file-upload @success="getDataList" :uploadData="uploadData" auto-upload="false" ref="uploadFileVideo" :type="['video/mp4']"></wx-file-upload>
|
||||
<el-form
|
||||
ref="uploadForm"
|
||||
:model="uploadData"
|
||||
v-loading="addMaterialLoading"
|
||||
:rules="uploadRules">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="uploadData.title" placeholder="标题将展示在相关播放页面,建议填写清晰、准确、生动的标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="introduction">
|
||||
<el-input
|
||||
v-model="uploadData.introduction"
|
||||
:rows="3"
|
||||
type="textarea"
|
||||
placeholder="介绍语将展示在相关播放页面,建议填写简洁明确、有信息量的内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVideoVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="subVideo">提 交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-table
|
||||
v-loading="state.loading"
|
||||
:data="state.dataList"
|
||||
stripe
|
||||
border>
|
||||
<el-table-column
|
||||
prop="mediaId"
|
||||
label="media_id">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="名称">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="updateTime"
|
||||
label="更新时间">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" icon="el-icon-view" @click="handleInfo(scope.row)">查看
|
||||
</el-button>
|
||||
<el-button type="text" icon="el-icon-delete" @click="delMaterial(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="news" label="news">
|
||||
<template #label><i class="el-icon-news"></i> 图文</template>
|
||||
<div class="add_but">
|
||||
<el-button type="primary" @click="handleAddNews">新 增</el-button>
|
||||
</div>
|
||||
<news-form ref="dialogNewsRef" @ok="getDataList"></news-form>
|
||||
<div v-loading="state.loading" class="waterfall">
|
||||
<div
|
||||
v-for="item in state.dataList"
|
||||
:key="item.id"
|
||||
class="waterfall-item">
|
||||
<wx-news :obj-data="item.content.newsItem"></wx-news>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="primary" icon="el-icon-edit" circle @click="handleEditNews(item)"></el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="delMaterial(item)"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="state.dataList.length <=0 && !state.loading" class="el-table__empty-block">
|
||||
<span class="el-table__empty-text">暂无数据</span>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-material">
|
||||
|
||||
import { fetchAccountList } from "/@/api/mp/wx-account";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {delObj, getMaterialVideo, getPage} from '/@/api/mp/wx-material'
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'))
|
||||
const NewsForm = defineAsyncComponent(() => import("./components/news-form.vue"))
|
||||
const WxFileUpload = defineAsyncComponent(() => import("/@/components/wechart/fileUpload/index.vue"))
|
||||
const WxNews = defineAsyncComponent(() => import("/@/components/wechart/wx-news/index.vue"))
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: () => {
|
||||
return fetchAccountList()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const checkAppId = ref()
|
||||
|
||||
const uploadData = ref({
|
||||
appId: '',
|
||||
mediaType: 'image',
|
||||
title: '',
|
||||
introduction: '',
|
||||
})
|
||||
|
||||
const materialType = ref("image")
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (data: any) => {
|
||||
checkAppId.value = data.appid
|
||||
uploadData.value.appId = data.appid
|
||||
state.queryForm.appId = data.appid
|
||||
state.queryForm.type = materialType.value
|
||||
getDataList()
|
||||
}
|
||||
|
||||
|
||||
const handleClick = (tab) => {
|
||||
if (checkAppId.value) {
|
||||
// getPage(this.page)
|
||||
} else {
|
||||
useMessage().error("请选择公众号")
|
||||
}
|
||||
materialType.value = tab.paneName
|
||||
uploadData.value.mediaType = tab.paneName
|
||||
state.queryForm.type = materialType.value
|
||||
getDataList()
|
||||
}
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
appId: '',
|
||||
type: ''
|
||||
},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false,
|
||||
props: {
|
||||
item: 'items',
|
||||
totalCount: 'totalCount'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
} = useTable(state)
|
||||
|
||||
const delMaterial = (item: any) => {
|
||||
useMessageBox().confirm("此操作将永久删除该文件, 是否继续?").then(() => {
|
||||
delObj({
|
||||
id: item.mediaId,
|
||||
appId: checkAppId.value
|
||||
}).then(() => {
|
||||
getDataList()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 视频
|
||||
|
||||
const dialogVideoVisible = ref(false)
|
||||
|
||||
const addMaterialLoading = ref(false)
|
||||
|
||||
const handleAddVideo = () => {
|
||||
dialogVideoVisible.value = true
|
||||
}
|
||||
|
||||
const uploadRules = reactive({
|
||||
title: [
|
||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||
],
|
||||
introduction: [
|
||||
{ required: true, message: '请输入描述', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
const uploadForm = ref()
|
||||
|
||||
const uploadFileVideo = ref()
|
||||
|
||||
const subVideo = () => {
|
||||
uploadForm.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
uploadFileVideo.value.submit()
|
||||
}).finally(() => {
|
||||
dialogVideoVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 图文
|
||||
|
||||
const dialogNewsRef = ref()
|
||||
|
||||
const handleAddNews = () => {
|
||||
dialogNewsRef.value.openDialog({
|
||||
accountId: checkAppId.value
|
||||
})
|
||||
}
|
||||
|
||||
const handleEditNews = (item) => {
|
||||
dialogNewsRef.value.openDialog({
|
||||
accountId: checkAppId.value,
|
||||
},JSON.parse(JSON.stringify(item.content.newsItem)),item.mediaId,'edit')
|
||||
}
|
||||
|
||||
const handleInfo = (row) => {
|
||||
getMaterialVideo({
|
||||
mediaId: row.mediaId,
|
||||
appId: checkAppId.value
|
||||
}).then((response) => {
|
||||
const downUrl = response.data.downUrl
|
||||
window.open(downUrl, '_blank')
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tree-position {
|
||||
margin: 12px 20px 0 0
|
||||
}
|
||||
|
||||
.pagination {
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.add_but {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eaeaea;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
.left {
|
||||
display: inline-block;
|
||||
width: 35%;
|
||||
vertical-align: top;
|
||||
margin-top: 200px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.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:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 230px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar1 {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.digest {
|
||||
width: 60%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap: 10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
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) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*瀑布流样式*/
|
||||
.news-main {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.news-main-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
margin-left: -3px
|
||||
}
|
||||
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae
|
||||
}
|
||||
|
||||
.input-tt {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.activeAddNews {
|
||||
border: 5px solid #2bb673;
|
||||
}
|
||||
|
||||
.news-main-plus {
|
||||
width: 280px;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.icon-plus {
|
||||
margin: 10px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.select-item {
|
||||
width: 60%;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.father .child {
|
||||
display: none;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumb-but {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
BIN
src/views/mp/wx-menu/assets/demo.png
Normal file
BIN
src/views/mp/wx-menu/assets/demo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
src/views/mp/wx-menu/assets/iphone_backImg.png
Normal file
BIN
src/views/mp/wx-menu/assets/iphone_backImg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
src/views/mp/wx-menu/assets/logo.png
Normal file
BIN
src/views/mp/wx-menu/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
BIN
src/views/mp/wx-menu/assets/menu_foot.png
Normal file
BIN
src/views/mp/wx-menu/assets/menu_foot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
src/views/mp/wx-menu/assets/menu_head.png
Normal file
BIN
src/views/mp/wx-menu/assets/menu_head.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
606
src/views/mp/wx-menu/index.vue
Normal file
606
src/views/mp/wx-menu/index.vue
Normal file
@ -0,0 +1,606 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4" :xs="24">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<query-tree :query="deptData.queryList"
|
||||
@node-click="handleNodeClick"/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-card class="layout-padding-auto" shadow="hover">
|
||||
<div v-loading="loading" class="public-account-management clearfix">
|
||||
<div class="left">
|
||||
<div class="weixin-hd">
|
||||
<div class="weixin-title">{{ name }}</div>
|
||||
</div>
|
||||
<div class="weixin-menu menu_main clearfix">
|
||||
<div v-for="(item, i) of menuList" :key="i" class="menu_bottom">
|
||||
<div :class="{ active: isActive === i }" class="menu_item el-icon-s-fold" @click="menuClick(i, item)">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<!-- 以下为二级菜单-->
|
||||
<div v-if="isSubMenuFlag === i" class="submenu">
|
||||
<template v-for="(subItem, k) in item.children">
|
||||
<div v-if="item.children" :key="k" class="subtitle menu_bottom">
|
||||
<div :class="{ active: isSubMenuActive === i + '' + k }" class="menu_subItem"
|
||||
@click="subMenuClick(subItem, i, k)">
|
||||
{{ subItem.name }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 二级菜单加号, 当长度 小于 5 才显示二级菜单的加号 -->
|
||||
<div v-if="!item.children || item.children.length < 5" class="menu_bottom menu_addicon"
|
||||
@click="addSubMenu(i, item)">
|
||||
<el-icon>
|
||||
<el-icon-plus/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 一级菜单加号 -->
|
||||
<div v-if="menuList.length < 3" class="menu_bottom menu_addicon" @click="addMenu">
|
||||
<el-icon>
|
||||
<el-icon-plus/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="save_div">
|
||||
<el-button class="save_btn" type="success" size="small" @click="handleSave">保存并发布菜单</el-button>
|
||||
<el-button class="save_btn" type="danger" size="small" @click="handleDelete">清空菜单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showRightFlag" class="right">
|
||||
<div class="configure_page">
|
||||
<div class="delete_btn">
|
||||
<el-button icon="Delete" size="mini" type="danger" @click="deleteMenu(tempObj)">删除当前菜单
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<span>菜单名称:</span>
|
||||
<el-input v-model="tempObj.name" class="input_width" clearable placeholder="请输入菜单名称"/>
|
||||
</div>
|
||||
<div v-if="showConfigureContent">
|
||||
<div class="menu_content">
|
||||
<span>菜单标识:</span>
|
||||
<el-input v-model="tempObj.menuKey" class="input_width" clearable placeholder="请输入菜单 KEY"/>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<span>菜单内容:</span>
|
||||
<el-select v-model="tempObj.type" class="menu_option" clearable placeholder="请选择">
|
||||
<el-option v-for="item in menuOptions" :key="item.value" :label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'view'">
|
||||
<span>跳转链接:</span>
|
||||
<el-input class="input_width" v-model="tempObj.url" placeholder="请输入链接" clearable />
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'miniprogram'">
|
||||
<div class="applet">
|
||||
<span>小程序的 appid :</span>
|
||||
<el-input class="input_width" v-model="tempObj.miniProgramAppId" placeholder="请输入小程序的appid" clearable />
|
||||
</div>
|
||||
<div class="applet">
|
||||
<span>小程序的页面路径:</span>
|
||||
<el-input class="input_width" v-model="tempObj.miniProgramPagePath"
|
||||
placeholder="请输入小程序的页面路径,如:pages/index" clearable />
|
||||
</div>
|
||||
<div class="applet">
|
||||
<span>小程序的备用网页:</span>
|
||||
<el-input class="input_width" v-model="tempObj.url" placeholder="不支持小程序的老版本客户端将打开本网页" clearable />
|
||||
</div>
|
||||
<p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'article_view_limited'">
|
||||
<el-row>
|
||||
<div class="select-item" v-if="tempObj && tempObj.replyArticles">
|
||||
<wx-news :objData="tempObj.replyArticles" />
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteMaterial" />
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-row>
|
||||
<el-col :span="24" style="text-align: center">
|
||||
<el-button type="success" @click="openMaterial">
|
||||
素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<wx-material-select ref="dialogNewsRef" @selectMaterial="selectMaterial" />
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'click' || tempObj.type === 'scancode_waitmsg'">
|
||||
<wx-reply :objData="tempObj" v-if="hackResetWxReplySelect"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="wx-menu" setup>
|
||||
import { saveObj,getObj } from '/@/api/mp/wx-menu'
|
||||
|
||||
// 部门树使用的数据
|
||||
import {fetchAccountList} from "/@/api/mp/wx-account";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
|
||||
const WxMaterialSelect = defineAsyncComponent(() => import("/@/components/wechart/wx-material-select/main.vue"))
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import("/@/components/wechart/wx-reply/index.vue"))
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'))
|
||||
|
||||
const WxNews = defineAsyncComponent(() => import("/@/components/wechart/wx-news/index.vue"))
|
||||
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (node: any) => {
|
||||
accountId.value = node.appid
|
||||
name.value = node.name
|
||||
getMenuFun()
|
||||
}
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: () => {
|
||||
return fetchAccountList()
|
||||
}
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const name = ref('测试公众号')
|
||||
|
||||
const accountId = ref('') // 公众号id
|
||||
// 一级菜单点中样式
|
||||
const isActive = ref(-1)
|
||||
// 一级菜单点中样式
|
||||
const isSubMenuActive = ref('-1')
|
||||
// 二级菜单显示标志
|
||||
const isSubMenuFlag = ref(-1)
|
||||
|
||||
const menuList = reactive([
|
||||
{
|
||||
name: '菜单名称',
|
||||
children: []
|
||||
}
|
||||
] as any)
|
||||
|
||||
const hackResetWxReplySelect = ref(false)
|
||||
|
||||
const menuOptions = ref([
|
||||
{
|
||||
value: 'view',
|
||||
label: '跳转网页',
|
||||
},
|
||||
{
|
||||
value: 'miniprogram',
|
||||
label: '跳转小程序',
|
||||
},
|
||||
{
|
||||
value: 'click',
|
||||
label: '点击回复',
|
||||
},
|
||||
{
|
||||
value: 'article_view_limited',
|
||||
label: '跳转图文消息',
|
||||
},
|
||||
{
|
||||
value: 'scancode_push',
|
||||
label: '扫码直接返回结果',
|
||||
},
|
||||
{
|
||||
value: 'scancode_waitmsg',
|
||||
label: '扫码回复',
|
||||
},
|
||||
{
|
||||
value: 'pic_sysphoto',
|
||||
label: '系统拍照发图',
|
||||
},
|
||||
{
|
||||
value: 'pic_photo_or_album',
|
||||
label: '拍照或者相册',
|
||||
},
|
||||
{
|
||||
value: 'pic_weixin',
|
||||
label: '微信相册',
|
||||
},
|
||||
{
|
||||
value: 'location_select',
|
||||
label: '选择地理位置',
|
||||
},
|
||||
])
|
||||
|
||||
const showRightFlag = ref(false)
|
||||
|
||||
let tempObj = ref({
|
||||
replyArticles: [] as any,
|
||||
articleId: '',
|
||||
appId: ''
|
||||
})
|
||||
|
||||
const tempSelfObj = reactive({
|
||||
grand: '', // 表示二级菜单
|
||||
index: '', // 表示一级菜单索引
|
||||
secondIndex: '' // 表示二级菜单索引
|
||||
})
|
||||
|
||||
const getMenuFun = () => {
|
||||
getObj(accountId.value).then((res) => {
|
||||
if (res.data) {
|
||||
const data = JSON.parse(res.data)
|
||||
if (data && data.button) {
|
||||
Object.assign(menuList,data.button)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const showConfigureContent = ref(true)
|
||||
|
||||
// 一级菜单点击事件
|
||||
const menuClick = (i, item) => {
|
||||
hackResetWxReplySelect.value = false
|
||||
nextTick(() => {
|
||||
hackResetWxReplySelect.value = true
|
||||
})
|
||||
showRightFlag.value = true // 右边菜单
|
||||
tempObj.value = item
|
||||
tempObj.value.appId = accountId.value
|
||||
showConfigureContent.value = !(item.children && item.children.length > 0) // 有子菜单,就不显示配置内容
|
||||
isActive.value = i
|
||||
isSubMenuFlag.value = i
|
||||
isSubMenuActive.value = '-1'
|
||||
tempSelfObj.grand = '1'//表示一级菜单
|
||||
tempSelfObj.index = i//表示一级菜单索引
|
||||
}
|
||||
|
||||
// 点击二级菜单
|
||||
const subMenuClick = (subItem, index, k) => {
|
||||
hackResetWxReplySelect.value = false
|
||||
nextTick(() => {
|
||||
hackResetWxReplySelect.value = true
|
||||
})
|
||||
showRightFlag.value = true // 右边菜单
|
||||
// Object.assign(tempObj, subItem) // 这个如果放在顶部,flag 会没有。因为重新赋值了。
|
||||
tempObj.value = subItem
|
||||
tempObj.value.appId = accountId.value
|
||||
showConfigureContent.value = true
|
||||
isActive.value = -1 // 一级菜单去除样式
|
||||
isSubMenuActive.value = (index + '' + k) // 二级菜单选中样式
|
||||
tempSelfObj.grand = '2'//表示二级菜单
|
||||
tempSelfObj.index = index//表示一级菜单索引
|
||||
tempSelfObj.secondIndex = k//表示二级菜单索引
|
||||
}
|
||||
|
||||
// 添加横向二级菜单;item 表示要操作的父菜单
|
||||
const addSubMenu = (i, item) => {
|
||||
if (!item.children || item.children.length <= 0) {
|
||||
item['children'] = []
|
||||
showConfigureContent.value = false
|
||||
}
|
||||
let addButton = {
|
||||
name: '子菜单名称',
|
||||
reply: {
|
||||
// 用于存储回复内容
|
||||
type: 'text',
|
||||
accountId: accountId.value, // 保证组件里,可以使用到对应的公众号
|
||||
},
|
||||
}
|
||||
item.children.push(addButton)
|
||||
}
|
||||
|
||||
// 添加横向一级菜单
|
||||
const addMenu = () => {
|
||||
const addButton = {
|
||||
name: '菜单名称',
|
||||
children: [],
|
||||
reply: {
|
||||
// 用于存储回复内容
|
||||
type: 'text',
|
||||
accountId: accountId.value, // 保证组件里,可以使用到对应的公众号
|
||||
},
|
||||
}
|
||||
menuList.push(addButton)
|
||||
}
|
||||
|
||||
const deleteMenu = () => {
|
||||
useMessageBox().confirm("确定要删除吗?").then(() => {
|
||||
|
||||
if(tempSelfObj.grand === '1'){
|
||||
menuList.splice(tempSelfObj.index, 1)
|
||||
}else if(tempSelfObj.grand === '2'){
|
||||
menuList[tempSelfObj.index].children.splice(tempSelfObj.secondIndex, 1)
|
||||
}
|
||||
useMessage().success("删除成功")
|
||||
Object.assign(tempObj,{})
|
||||
showRightFlag.value = false
|
||||
isActive.value = -1
|
||||
isSubMenuActive.value = '-1'
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
useMessageBox().confirm("确定要保存该菜单吗?").then(() => {
|
||||
saveObj(accountId.value, {
|
||||
button: menuList
|
||||
}).then(() => {
|
||||
useMessage().success("保存成功")
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const deleteMaterial = () => {
|
||||
tempObj.value.replyArticles = []
|
||||
tempObj.value.articleId = ""
|
||||
}
|
||||
|
||||
|
||||
const dialogNewsRef = ref()
|
||||
const openMaterial = () => {
|
||||
dialogNewsRef.value.openDialog({type: 'news', accountId: accountId.value})
|
||||
}
|
||||
|
||||
const selectMaterial = (item) => {
|
||||
const articleId = item.articleId;
|
||||
const articles = item.content.newsItem;
|
||||
// 提示,针对多图文
|
||||
if (articles.length > 1) {
|
||||
// this.$alert('您选择的是多图文,将默认跳转第一篇', '提示', {
|
||||
// confirmButtonText: '确定'
|
||||
// })
|
||||
}
|
||||
|
||||
// 设置菜单的回复
|
||||
tempObj.value.articleId = articleId;
|
||||
tempObj.value.replyArticles = [];
|
||||
articles.forEach(article => {
|
||||
tempObj.value.replyArticles.push({
|
||||
title: article.title,
|
||||
description: article.digest,
|
||||
picUrl: article.picUrl,
|
||||
url: article.url,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const handleDelete = () => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 公共颜色变量 */
|
||||
.clearfix {
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.weixin-hd {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 426px;
|
||||
left: 0px;
|
||||
width: 300px;
|
||||
height: 64px;
|
||||
background: transparent url("assets/menu_head.png") no-repeat 0 0;
|
||||
background-position: 0 0;
|
||||
background-size: 100%
|
||||
}
|
||||
|
||||
.weixin-title {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.weixin-menu {
|
||||
background: transparent url("assets/menu_foot.png") no-repeat 0 0;
|
||||
padding-left: 43px;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.menu_option {
|
||||
width: 40% !important;
|
||||
}
|
||||
|
||||
.public-account-management {
|
||||
min-width: 1200px;
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
width: 350px;
|
||||
height: 715px;
|
||||
background: url("assets/iphone_backImg.png") no-repeat;
|
||||
background-size: 100% auto;
|
||||
padding: 518px 25px 88px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
/*第一级菜单*/
|
||||
.menu_main {
|
||||
.menu_bottom {
|
||||
position: relative;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
width: 85.5px;
|
||||
text-align: center;
|
||||
border: 1px solid #ebedee;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
|
||||
&.menu_addicon {
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
|
||||
&.active {
|
||||
border: 1px solid #2bb673;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_subItem {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.active {
|
||||
border: 1px solid #2bb673;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: #2bb673;
|
||||
}
|
||||
|
||||
/*第二级菜单*/
|
||||
.submenu {
|
||||
position: absolute;
|
||||
width: 85.5px;
|
||||
bottom: 45px;
|
||||
|
||||
.subtitle {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.save_div {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
|
||||
.save_btn {
|
||||
bottom: 20px;
|
||||
left: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*右边菜单内容*/
|
||||
.right {
|
||||
float: left;
|
||||
width: 63%;
|
||||
background-color: #e8e7e7;
|
||||
padding: 20px;
|
||||
margin-left: 20px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
.configure_page {
|
||||
.delete_btn {
|
||||
text-align: right;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.menu_content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.configur_content {
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
padding: 20px 10px;
|
||||
border-radius: 5px
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: #29b6f6;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.applet {
|
||||
margin-bottom: 20px;
|
||||
|
||||
span {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
.input_width {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.material {
|
||||
.input_width {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.el-textarea {
|
||||
width: 80%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-input {
|
||||
width: 70%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--素材样式-->
|
||||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
text-align: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
.select-item {
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.select-item2 {
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.ope-row {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
238
src/views/mp/wx-statistics/index.vue
Normal file
238
src/views/mp/wx-statistics/index.vue
Normal file
@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-card class="layout-padding-auto">
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-date-picker
|
||||
v-model="beginTime"
|
||||
class="input_width"
|
||||
placeholder="选择开始时间"
|
||||
@change="check">
|
||||
</el-date-picker>
|
||||
<el-date-picker
|
||||
v-model="endTime"
|
||||
class="input_width"
|
||||
placeholder="选择结束时间"
|
||||
@change="check">
|
||||
</el-date-picker>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row >
|
||||
<el-col :span="4" :xs="24">
|
||||
<query-tree :query="deptData.queryList"
|
||||
@node-click="handleNodeClick"/>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-row :gutter="15" class="home-card-two mb15">
|
||||
<el-col :span="12">
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="userCumulateRef"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="userShardRef"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="upstreamMsgDistMonthRef"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="interfaceSummaryRef"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-statistics">
|
||||
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {fetchAccountList, fetchStatistics} from "/@/api/mp/wx-account";
|
||||
import {markRaw} from "vue";
|
||||
import * as echarts from "echarts";
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'))
|
||||
|
||||
const beginTime = ref(new Date().getTime() - 3600 * 1000 * 24 * 7)
|
||||
const endTime = ref(new Date().getTime() - 3600 * 1000 * 24)
|
||||
|
||||
const check = () => {
|
||||
const start = new Date(beginTime.value)
|
||||
const end = new Date(endTime.value)
|
||||
if (end.getTime() >= new Date().getTime()) {
|
||||
useMessage().error("统计结束日小于当前日期,请重新选择")
|
||||
return false
|
||||
}
|
||||
|
||||
if (end.getTime() - start.getTime() >= 3600 * 1000 * 24 * 7) {
|
||||
useMessage().error("时间间隔7天以内,请重新选择")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const accountId = ref()
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (node: any) => {
|
||||
accountId.value = node.appid
|
||||
initdata()
|
||||
}
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: () => {
|
||||
return fetchAccountList()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const userCumulateRef = ref()
|
||||
|
||||
// 初始化折线图
|
||||
const userCumulate = () => {
|
||||
const userCumulate = markRaw(echarts.init(userCumulateRef.value));
|
||||
const option = {
|
||||
title: {
|
||||
text: '用户分析数据',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: LintData.value[0],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: LintData.value[1]
|
||||
}
|
||||
]
|
||||
}
|
||||
userCumulate.setOption(option)
|
||||
}
|
||||
|
||||
const userShardRef = ref()
|
||||
|
||||
// 初始化折线图
|
||||
const userShard = () => {
|
||||
const userShard = markRaw(echarts.init(userShardRef.value));
|
||||
const option = {
|
||||
title: {
|
||||
text: '接口分析数据',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: LintData.value[2],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: LintData.value[3]
|
||||
}
|
||||
]
|
||||
}
|
||||
userShard.setOption(option)
|
||||
}
|
||||
|
||||
const upstreamMsgDistMonthRef = ref()
|
||||
|
||||
// 初始化折线图
|
||||
const upstreamMsgDistMonth = () => {
|
||||
const upstreamMsgDistMonth = markRaw(echarts.init(upstreamMsgDistMonthRef.value));
|
||||
const option = {
|
||||
title: {
|
||||
text: '消息分析数据',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: LintData.value[4],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: LintData.value[5]
|
||||
}
|
||||
]
|
||||
}
|
||||
upstreamMsgDistMonth.setOption(option)
|
||||
}
|
||||
|
||||
const interfaceSummaryRef = ref()
|
||||
|
||||
// 初始化折线图
|
||||
const interfaceSummary = () => {
|
||||
const interfaceSummary = markRaw(echarts.init(interfaceSummaryRef.value));
|
||||
const option = {
|
||||
title: {
|
||||
text: '图文分享数据',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: LintData.value[0],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: LintData.value[1]
|
||||
}
|
||||
]
|
||||
}
|
||||
interfaceSummary.setOption(option)
|
||||
}
|
||||
|
||||
|
||||
const LintData = ref([
|
||||
[],[],[],[],[],[],[],[]
|
||||
])
|
||||
|
||||
|
||||
const initdata = () => {
|
||||
fetchStatistics({
|
||||
appId: accountId.value,
|
||||
interval: new Date(beginTime.value).getTime() + '-' + new Date(endTime.value).getTime()
|
||||
}).then((res) => {
|
||||
LintData.value = res.data
|
||||
}).finally(() => {
|
||||
userCumulate()
|
||||
userShard()
|
||||
upstreamMsgDistMonth()
|
||||
interfaceSummary()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home-card-item{
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
border-radius: 4px;
|
||||
transition: all ease 0.3s;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
background: var(--el-color-white);
|
||||
color: var(--el-text-color-primary);
|
||||
border: 1px solid var(--next-border-color-light);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user