init
This commit is contained in:
149
src/views/biz/mp/wx-account-fans/form.vue
Normal file
149
src/views/biz/mp/wx-account-fans/form.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<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="12" 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="12" 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="12" 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="12" 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="12" 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="12" class="mb20">
|
||||
<el-form-item :label="t('fans.tagIds')" prop="tagIds">
|
||||
<el-select v-model="form.tagIds" :placeholder="t('fans.inputTagTip')" 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>
|
||||
24
src/views/biz/mp/wx-account-fans/i18n/en.ts
Normal file
24
src/views/biz/mp/wx-account-fans/i18n/en.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
fans: {
|
||||
index: '#',
|
||||
importwxAccountFansTip: 'import WxAccountFans',
|
||||
id: 'id',
|
||||
openid: 'openid',
|
||||
subscribeStatus: 'subscribeStatus',
|
||||
subscribeTime: 'subscribeTime',
|
||||
nickname: 'nickname',
|
||||
gender: 'gender',
|
||||
language: 'language',
|
||||
country: 'country',
|
||||
province: 'province',
|
||||
isBlack: 'black',
|
||||
city: 'city',
|
||||
tagIds: 'tagIds',
|
||||
headimgUrl: 'headimgUrl',
|
||||
remark: 'remark',
|
||||
wxAccountId: 'wxAccountId',
|
||||
wxAccountName: 'wxAccountName',
|
||||
wxAccountAppid: 'wxAccountAppid',
|
||||
inputNicknameTip: 'input nickname',
|
||||
},
|
||||
};
|
||||
26
src/views/biz/mp/wx-account-fans/i18n/zh-cn.ts
Normal file
26
src/views/biz/mp/wx-account-fans/i18n/zh-cn.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export default {
|
||||
fans: {
|
||||
index: '#',
|
||||
importwxAccountFansTip: '导入微信公众号粉丝表',
|
||||
id: '主键',
|
||||
openid: '用户标识',
|
||||
subscribeStatus: '订阅状态',
|
||||
subscribeTime: '订阅时间',
|
||||
nickname: '昵称',
|
||||
gender: '性别',
|
||||
language: '语言',
|
||||
country: '国家',
|
||||
province: '省份',
|
||||
isBlack: '黑名单',
|
||||
city: '城市',
|
||||
tagIds: '分组',
|
||||
headimgUrl: ' headimgUrl',
|
||||
remark: '备注',
|
||||
wxAccountId: '微信公众号ID',
|
||||
wxAccountName: '微信公众号',
|
||||
wxAccountAppid: '公众号appid',
|
||||
inputremarkTip: '请输入备注',
|
||||
inputTagTip: '请选择分组',
|
||||
inputNicknameTip: '请输入粉丝昵称',
|
||||
},
|
||||
};
|
||||
222
src/views/biz/mp/wx-account-fans/index.vue
Normal file
222
src/views/biz/mp/wx-account-fans/index.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<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" :placeholder="$t('fans.inputNicknameTip')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('fans.wxAccountName')" prop="wxAccountAppid">
|
||||
<el-select v-model="state.queryForm.wxAccountAppid" :placeholder="$t('fans.wxAccountName')" clearable>
|
||||
<el-option v-for="item in accountList" :key="item.appid" :label="item.name" :value="item.appid" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button 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 type="primary" class="ml10" icon="Sort" @click="asyncFans" v-auth="'mp_wxaccountfans_sync'">同步</el-button>
|
||||
|
||||
<right-toolbar
|
||||
:export="'mp_wxaccountfans_sync'"
|
||||
@exportExcel="exportExcel"
|
||||
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%"
|
||||
@sort-change="sortChangeHandle"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column :label="t('fans.index')" type="index" width="60" />
|
||||
<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.isBlack')" prop="isBlack" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="blackList" :value="scope.row.isBlack"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('fans.tagIds')" prop="tagIds" show-overflow-tooltip>
|
||||
<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="250" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button icon="edit-pen" text type="primary" @click="formDialogRef.openDialog(scope.row, state.queryForm.wxAccountAppid)"
|
||||
>{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" @click="handleDelete([scope.row.id])">{{ $t('common.delBtn') }} </el-button>
|
||||
<el-button icon="CircleCheck" text type="primary" @click="handelUnBlack([scope.row.id])" v-if="scope.row.isBlack"> 取消拉黑 </el-button>
|
||||
<el-button icon="warning" text type="primary" @click="handelBlack([scope.row.id])" v-else> 拉黑 </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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxAccountFans" setup>
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { black, delObjs, fetchList, sync, unblack } 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 blackList = ref([
|
||||
{
|
||||
label: '是',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: '0',
|
||||
},
|
||||
]);
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref();
|
||||
// 搜索变量
|
||||
const queryRef = ref();
|
||||
const showSearch = ref(true);
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any;
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList,
|
||||
createdIsNeed: false,
|
||||
});
|
||||
|
||||
// table hook
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = 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 handleDelete = (ids: string[]) => {
|
||||
useMessageBox()
|
||||
.confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids)
|
||||
.then(() => {
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
})
|
||||
.catch((err: any) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handelBlack = (ids: string[]) => {
|
||||
useMessageBox()
|
||||
.confirm('是否要拉黑用户')
|
||||
.then(() => {
|
||||
black(ids, state.queryForm.wxAccountAppid)
|
||||
.then(() => {
|
||||
getDataList();
|
||||
useMessage().success('拉黑用户成功');
|
||||
})
|
||||
.catch((err: any) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handelUnBlack = (ids: string[]) => {
|
||||
useMessageBox()
|
||||
.confirm('是否要取消拉黑用户')
|
||||
.then(() => {
|
||||
unblack(ids, state.queryForm.wxAccountAppid)
|
||||
.then(() => {
|
||||
getDataList();
|
||||
useMessage().success('设置成功');
|
||||
})
|
||||
.catch((err: any) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
91
src/views/biz/mp/wx-account-tag/form.vue
Normal file
91
src/views/biz/mp/wx-account-tag/form.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<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('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" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="WxAccountTagDialog" setup>
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { addObj, putObj } from '/@/api/mp/wx-account-tag';
|
||||
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({
|
||||
wxAccountAppid: '',
|
||||
tag: '',
|
||||
id: '',
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any, appid: string) => {
|
||||
visible.value = true;
|
||||
form.wxAccountAppid = '';
|
||||
form.tag = '';
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields();
|
||||
}
|
||||
if (row) {
|
||||
Object.assign(form, row);
|
||||
}
|
||||
form.wxAccountAppid = appid;
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
if (form.id) {
|
||||
await putObj(form);
|
||||
useMessage().success(t('common.editSuccessText'));
|
||||
} else {
|
||||
await addObj(form);
|
||||
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/biz/mp/wx-account-tag/i18n/en.ts
Normal file
18
src/views/biz/mp/wx-account-tag/i18n/en.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export default {
|
||||
wxAccountTag: {
|
||||
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/biz/mp/wx-account-tag/i18n/zh-cn.ts
Normal file
18
src/views/biz/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',
|
||||
},
|
||||
};
|
||||
188
src/views/biz/mp/wx-account-tag/index.vue
Normal file
188
src/views/biz/mp/wx-account-tag/index.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<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>
|
||||
<el-option v-for="item in accountList" :key="item.appid" :label="item.name" :value="item.appid" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<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(null, 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>
|
||||
<el-button plain type="primary" icon="Sort" @click="asyncTag">同步</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"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="40" />
|
||||
<el-table-column :label="t('wxAccountTag.index')" type="index" width="60" />
|
||||
<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 icon="edit-pen" text type="primary" @click="formDialogRef.openDialog(scope.row, state.queryForm.wxAccountAppid)">{{
|
||||
$t('common.editBtn')
|
||||
}}</el-button>
|
||||
<el-button icon="delete" 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" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxAccountTag" setup>
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { delObjs, getPage, sync } 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, tableStyle } = useTable(state);
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/mp/wxAccountTag/export', state.queryForm, 'wxAccountTag.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox()
|
||||
.confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs({
|
||||
ids: ids,
|
||||
wxAccountAppid: state.queryForm.wxAccountAppid,
|
||||
})
|
||||
.then(() => {
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
})
|
||||
.catch((err: any) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const asyncTag = () => {
|
||||
sync(state.queryForm.wxAccountAppid).then(() => {
|
||||
getDataList();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
158
src/views/biz/mp/wx-account/form.vue
Normal file
158
src/views/biz/mp/wx-account/form.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<el-dialog 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" label-width="90px">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" 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="12" 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="12" 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="12" 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="12" 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="12" 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="12" 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" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</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';
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
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: '' as string | undefined,
|
||||
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' },
|
||||
{ validator: rule.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 = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
if (form.appsecret && form.appsecret.includes('*')) {
|
||||
form.appsecret = undefined;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
if (form.id) {
|
||||
await putObj(form);
|
||||
useMessage().success(t('common.editSuccessText'));
|
||||
} else {
|
||||
await addObj(form);
|
||||
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>
|
||||
25
src/views/biz/mp/wx-account/i18n/en.ts
Normal file
25
src/views/biz/mp/wx-account/i18n/en.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
account: {
|
||||
index: '#',
|
||||
importwxAccountTip: 'import WxAccount',
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
account: 'account',
|
||||
appid: 'appid',
|
||||
appsecret: 'appsecret',
|
||||
url: 'url',
|
||||
export: 'export',
|
||||
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',
|
||||
},
|
||||
};
|
||||
25
src/views/biz/mp/wx-account/i18n/zh-cn.ts
Normal file
25
src/views/biz/mp/wx-account/i18n/zh-cn.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
account: {
|
||||
index: '#',
|
||||
importwxAccountTip: '导入公众号账户表',
|
||||
id: '主键',
|
||||
name: '名称',
|
||||
account: '微信号',
|
||||
appid: 'appid',
|
||||
appsecret: '密钥',
|
||||
export: '导出',
|
||||
url: ' url',
|
||||
token: 'token',
|
||||
aeskey: '加密密钥',
|
||||
qrUrl: '图片',
|
||||
inputIdTip: '请输入主键',
|
||||
inputNameTip: '请输入名称',
|
||||
inputAccountTip: '请输入微信号',
|
||||
inputAppidTip: '请输入appid',
|
||||
inputAppsecretTip: '请输入密钥',
|
||||
inputUrlTip: '请输入 url',
|
||||
inputTokenTip: '请输入token',
|
||||
inputAeskeyTip: '请输入加密密钥',
|
||||
inputQrUrlTip: '请输入图片',
|
||||
},
|
||||
};
|
||||
218
src/views/biz/mp/wx-account/index.vue
Normal file
218
src/views/biz/mp/wx-account/index.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<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>
|
||||
<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" icon="folder-add" type="primary" @click="formDialogRef.openDialog()">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
v-auth="'mp_wxaccount_add'"
|
||||
class="ml10"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
:export="'app_social_details_del'"
|
||||
@exportExcel="exportExcel"
|
||||
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"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="60" />
|
||||
<el-table-column :label="t('account.index')" type="index" width="60" />
|
||||
<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">
|
||||
<ImageUpload v-model:imageUrl="scope.row.qrUrl" height="80px" width="80px" disabled />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.action')" width="100" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
更多功能
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button icon="edit-pen" v-auth="'mp_wxaccount_edit'" text type="primary" @click="formDialogRef.openDialog(scope.row.id)"
|
||||
>{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button icon="delete" v-auth="'mp_wxaccount_del'" text type="primary" @click="handleDelete([scope.row.id])"
|
||||
>{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button icon="DArrowRight" v-auth="'mp_wxaccount_del'" text type="primary" @click="access(scope.row)">接入 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button icon="Grid" v-auth="'mp_wxaccount_del'" text type="primary" @click="generate(scope.row)">二维码 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button icon="refresh" v-auth="'mp_wxaccount_del'" text type="primary" @click="quota(scope.row)">quota </el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<el-dialog v-model="dialogFormVisible" title="接入">
|
||||
<el-input v-model="wxurl">
|
||||
<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';
|
||||
import other from '/@/utils/other';
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { copyText } = commonFunction();
|
||||
// 引入组件
|
||||
const ImageUpload = defineAsyncComponent(() => import('/@/components/Upload/Image.vue'));
|
||||
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, tableStyle } = useTable(state);
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/mp/account/export', state.queryForm, 'account.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = (ids: string[]) => {
|
||||
useMessageBox()
|
||||
.confirm(t('common.delConfirmText'))
|
||||
.then(() => {
|
||||
delObjs(ids)
|
||||
.then(() => {
|
||||
getDataList();
|
||||
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;
|
||||
let url = '/mp/wx-portal/' + row.appid;
|
||||
wxurl.value = row.url + proxy.baseURL + other.adaptationUrl(url);
|
||||
};
|
||||
|
||||
const generate = (row: any) => {
|
||||
generateQr(row.appid)
|
||||
.then(() => {
|
||||
useMessage().success('获取成功');
|
||||
getDataList();
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
};
|
||||
|
||||
const quota = (row) => {
|
||||
clearQuota(row.appid)
|
||||
.then(() => {
|
||||
useMessage().success('清空api的调用quota成功');
|
||||
})
|
||||
.catch(() => {
|
||||
useMessage().error('清空api的调用quota失败');
|
||||
});
|
||||
};
|
||||
</script>
|
||||
353
src/views/biz/mp/wx-auto-reply/index.vue
Normal file
353
src/views/biz/mp/wx-auto-reply/index.vue
Normal file
@@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<splitpanes>
|
||||
<pane size="20">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-scrollbar>
|
||||
<query-tree class="mt10" :query="deptData.queryList" @node-click="handleNodeClick" placeholder="请输入微信公众号名称" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</pane>
|
||||
<pane size="80">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<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%" max-height="600px" @sort-change="sortChangeHandle">
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<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="edit" @click="handleEdit(scope.row)">编辑 </el-button>
|
||||
<el-button link 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%" max-height="600px" @sort-change="sortChangeHandle">
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<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="edit" link @click="handleEdit(scope.row)">编辑 </el-button>
|
||||
<el-button 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%"
|
||||
max-height="600px"
|
||||
@sort-change="sortChangeHandle"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<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="edit" link @click="handleEdit(scope.row)">编辑 </el-button>
|
||||
<el-button 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>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
<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 {addObj, delObj, getPage, putObj} from '/@/api/mp/wx-auto-reply';
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'));
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import('/@/components/Wechat/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: (name?: string) => {
|
||||
return fetchAccountList({
|
||||
name: name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
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: '1',
|
||||
appId: '',
|
||||
},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false,
|
||||
});
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle } = 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();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
}
|
||||
if (handleType.value === 'edit') {
|
||||
putObj(objData.value)
|
||||
.then(() => {
|
||||
useMessage().success('修改成功');
|
||||
getDataList();
|
||||
dialog1Visible.value = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
hackResetWxReplySelect.value = false; //销毁组件
|
||||
nextTick(() => {
|
||||
hackResetWxReplySelect.value = true; //重建组件
|
||||
});
|
||||
handleType.value = 'add';
|
||||
dialog1Visible.value = true;
|
||||
objData.value = {
|
||||
repType: 'text',
|
||||
appId: accountId.value,
|
||||
};
|
||||
};
|
||||
|
||||
// 默认选择第一个公众号
|
||||
onMounted(async () => {
|
||||
const { data } = await deptData.queryList();
|
||||
if (data?.length > 0) {
|
||||
handleNodeClick(data[0]);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
64
src/views/biz/mp/wx-fans-msg/i18n/en.ts
Normal file
64
src/views/biz/mp/wx-fans-msg/i18n/en.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
export default {
|
||||
wxFansMsg: {
|
||||
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/biz/mp/wx-fans-msg/i18n/zh-cn.ts
Normal file
64
src/views/biz/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',
|
||||
},
|
||||
};
|
||||
194
src/views/biz/mp/wx-fans-msg/index.vue
Normal file
194
src/views/biz/mp/wx-fans-msg/index.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<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('wxFansMsg.appName')" clearable >
|
||||
<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>
|
||||
<el-option v-for="item in repType" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<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%"
|
||||
@sort-change="sortChangeHandle"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column :label="t('wxFansMsg.index')" type="index" width="60" />
|
||||
<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.nickName')" prop="nickName" 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">关注</el-tag></div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'unsubscribe'">
|
||||
<el-tag type="danger">取消关注</el-tag>
|
||||
</div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'CLICK'"><el-tag>点击菜单</el-tag>:【{{ scope.row.repName }}】</div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'VIEW'"><el-tag>点击菜单链接</el-tag>:【{{ scope.row.repUrl }}】</div>
|
||||
<div v-if="scope.row.repType === 'event' && scope.row.repEvent === 'scancode_waitmsg'">
|
||||
<el-tag>扫码结果:</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 icon="ChatSquare" 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" />
|
||||
</div>
|
||||
<wx-msg ref="WxmsgRef"></wx-msg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="systemWxMsg" setup>
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList } from '/@/api/mp/wx-fans-msg';
|
||||
import { useMessage } 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/Wechat/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, tableStyle } = 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();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => state.queryForm.wxAccountAppid,
|
||||
() => {
|
||||
getDataList();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getAccountList();
|
||||
});
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/act/wxFansMsg/export', state.queryForm, 'wxFansMsg.xlsx');
|
||||
};
|
||||
|
||||
const wxMsgDo = (row) => {
|
||||
WxmsgRef.value.openDialog({
|
||||
wxUserId: row.wxUserId,
|
||||
appId: row.appId,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
539
src/views/biz/mp/wx-material/components/news-form.vue
Normal file
539
src/views/biz/mp/wx-material/components/news-form.vue
Normal file
@@ -0,0 +1,539 @@
|
||||
<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="top" @click="downNews(index)">下移</el-button>
|
||||
<el-button v-if="operateMaterial == 'add'" 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="sort-down" @click="downNews(index)">下移 </el-button>
|
||||
<el-button icon="sort-up" @click="upNews(index)">上移</el-button>
|
||||
<el-button v-if="operateMaterial == 'add'" 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="6"
|
||||
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/Wechat/wx-material-select/main.vue'));
|
||||
|
||||
const WxFileUpload = defineAsyncComponent(() => import('/@/components/Wechat/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 {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
553
src/views/biz/mp/wx-material/index.vue
Normal file
553
src/views/biz/mp/wx-material/index.vue
Normal file
@@ -0,0 +1,553 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<splitpanes>
|
||||
<pane size="20">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-scrollbar>
|
||||
<query-tree class="mt10" :query="deptData.queryList" @node-click="handleNodeClick" placeholder="请输入微信公众号名称" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</pane>
|
||||
<pane size="80">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-tabs v-model="materialType" @tab-click="handleClick">
|
||||
<el-tab-pane name="image" label="image">
|
||||
<template #label><i class="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="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="microphone"></i> 语音</template>
|
||||
<div class="add_but">
|
||||
<wx-file-upload @success="getDataList" :uploadData="uploadData" :type="['mp3', 'wma', 'wav', 'amr']"></wx-file-upload>
|
||||
</div>
|
||||
<el-table v-loading="state.loading" :data="state.dataList" stripe border max-height="600px">
|
||||
<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="download" plain @click="handleDown(scope.row)">下载 </el-button>
|
||||
<el-button type="text" 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="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
|
||||
max-height="600px"
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<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="view" @click="handleInfo(scope.row)">查看 </el-button>
|
||||
<el-button type="text" 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="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="edit" circle @click="handleEditNews(item)"></el-button>
|
||||
<el-button type="danger" 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>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</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, getMaterialOther, 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/Wechat/fileUpload/index.vue'));
|
||||
const WxNews = defineAsyncComponent(() => import('/@/components/Wechat/wx-news/index.vue'));
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: (name?: string) => {
|
||||
return fetchAccountList({
|
||||
name: name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
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, tableStyle } = useTable(state);
|
||||
|
||||
const delMaterial = (item: any) => {
|
||||
useMessageBox()
|
||||
.confirm('此操作将永久删除该文件, 是否继续?')
|
||||
.then(() => {
|
||||
delObj({
|
||||
id: item.mediaId,
|
||||
appId: checkAppId.value,
|
||||
})
|
||||
.then(() => {
|
||||
getDataList();
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 视频
|
||||
|
||||
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().then(() => {
|
||||
dialogVideoVisible.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleDown = (row: any) => {
|
||||
getMaterialOther({
|
||||
mediaId: row.mediaId,
|
||||
fileName: row.name,
|
||||
appId: checkAppId.value,
|
||||
}).then((response) => {
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement('a');
|
||||
link.style.display = 'none';
|
||||
link.href = url;
|
||||
link.setAttribute('download', row.name);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
});
|
||||
};
|
||||
|
||||
// 图文
|
||||
|
||||
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');
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
};
|
||||
|
||||
// 默认选择第一个公众号
|
||||
onMounted(async () => {
|
||||
const { data } = await deptData.queryList();
|
||||
if (data?.length > 0) {
|
||||
handleNodeClick(data[0]);
|
||||
}
|
||||
});
|
||||
</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/biz/mp/wx-menu/assets/demo.png
Normal file
BIN
src/views/biz/mp/wx-menu/assets/demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
src/views/biz/mp/wx-menu/assets/iphone_backImg.png
Normal file
BIN
src/views/biz/mp/wx-menu/assets/iphone_backImg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
src/views/biz/mp/wx-menu/assets/logo.png
Normal file
BIN
src/views/biz/mp/wx-menu/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/views/biz/mp/wx-menu/assets/menu_foot.png
Normal file
BIN
src/views/biz/mp/wx-menu/assets/menu_foot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/views/biz/mp/wx-menu/assets/menu_head.png
Normal file
BIN
src/views/biz/mp/wx-menu/assets/menu_head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
221
src/views/biz/mp/wx-menu/assets/wx-menu.scss
Normal file
221
src/views/biz/mp/wx-menu/assets/wx-menu.scss
Normal file
@@ -0,0 +1,221 @@
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
416
src/views/biz/mp/wx-menu/index.vue
Normal file
416
src/views/biz/mp/wx-menu/index.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<splitpanes>
|
||||
<pane size="20">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-scrollbar>
|
||||
<query-tree class="mt10" :query="deptData.queryList" @node-click="handleNodeClick" placeholder="请输入微信公众号名称" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</pane>
|
||||
<pane>
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-scrollbar>
|
||||
<div v-loading="loading" class="clearfix public-account-management">
|
||||
<div class="left">
|
||||
<div class="weixin-hd">
|
||||
<div class="weixin-title">{{ name }}</div>
|
||||
</div>
|
||||
<div class="clearfix weixin-menu menu_main">
|
||||
<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.sub_button">
|
||||
<div v-if="item.sub_button" :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.sub_button || item.sub_button.length < 5" class="menu_bottom menu_addicon" @click="addSubMenu(i, item)">
|
||||
<el-icon>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</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>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center gap-4 mt-4 mb-6 save_div">
|
||||
<el-button
|
||||
class="save_btn !px-6 !h-9 hover:scale-105 transition-transform"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleSave"
|
||||
>
|
||||
<el-icon class="mr-1"><Check /></el-icon>
|
||||
保存发布
|
||||
</el-button>
|
||||
<el-button
|
||||
class="save_btn !px-6 !h-9 hover:scale-105 transition-transform"
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="handleDelete"
|
||||
>
|
||||
<el-icon class="mr-1"><Delete /></el-icon>
|
||||
清空菜单
|
||||
</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.key" 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="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-scrollbar>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="wx-menu" setup>
|
||||
import {getObj, publishObj, saveObj} from '/@/api/mp/wx-menu';
|
||||
|
||||
// 部门树使用的数据
|
||||
import {fetchAccountList} from '/@/api/mp/wx-account';
|
||||
import {useMessage, useMessageBox} from '/@/hooks/message';
|
||||
|
||||
const WxMaterialSelect = defineAsyncComponent(() => import('/@/components/Wechat/wx-material-select/main.vue'));
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import('/@/components/Wechat/wx-reply/index.vue'));
|
||||
|
||||
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'));
|
||||
|
||||
const WxNews = defineAsyncComponent(() => import('/@/components/Wechat/wx-news/index.vue'));
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (node: any) => {
|
||||
accountId.value = node.appid;
|
||||
name.value = node.name;
|
||||
getMenuFun();
|
||||
};
|
||||
|
||||
const deptData = reactive({
|
||||
queryList: (name?: string) => {
|
||||
return fetchAccountList({
|
||||
name: name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
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: '菜单名称',
|
||||
sub_button: [],
|
||||
},
|
||||
] 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);
|
||||
}
|
||||
} else {
|
||||
menuList.length = 0;
|
||||
Object.assign(menuList, {
|
||||
name: '菜单名称',
|
||||
sub_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.sub_button && item.sub_button.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.sub_button || item.sub_button.length <= 0) {
|
||||
item['sub_button'] = [];
|
||||
showConfigureContent.value = false;
|
||||
}
|
||||
let addButton = {
|
||||
name: '子菜单名称',
|
||||
reply: {
|
||||
// 用于存储回复内容
|
||||
type: 'text',
|
||||
accountId: accountId.value, // 保证组件里,可以使用到对应的公众号
|
||||
},
|
||||
};
|
||||
item.sub_button.push(addButton);
|
||||
};
|
||||
|
||||
// 添加横向一级菜单
|
||||
const addMenu = () => {
|
||||
const addButton = {
|
||||
name: '菜单名称',
|
||||
sub_button: [],
|
||||
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].sub_button.splice(tempSelfObj.secondIndex, 1);
|
||||
}
|
||||
useMessage().success('删除成功');
|
||||
Object.assign(tempObj, {});
|
||||
showRightFlag.value = false;
|
||||
isActive.value = -1;
|
||||
isSubMenuActive.value = '-1';
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要保存该菜单吗?');
|
||||
await saveObj(accountId.value, { button: menuList });
|
||||
await publishObj(accountId.value);
|
||||
useMessage().success('发布成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
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 = () => {};
|
||||
|
||||
// 默认选择第一个公众号
|
||||
onMounted(async () => {
|
||||
const { data } = await deptData.queryList();
|
||||
if (data?.length > 0) {
|
||||
handleNodeClick(data[0]);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './assets/wx-menu.scss';
|
||||
</style>
|
||||
238
src/views/biz/mp/wx-statistics/index.vue
Normal file
238
src/views/biz/mp/wx-statistics/index.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<splitpanes>
|
||||
<pane size="20">
|
||||
<el-date-picker v-model="beginTime" placeholder="选择开始时间" @change="check" style="width: 50%"></el-date-picker>
|
||||
<el-date-picker v-model="endTime" style="width: 50%" placeholder="选择结束时间" @change="check"></el-date-picker>
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-scrollbar>
|
||||
<query-tree class="mt10" :query="deptData.queryList" @node-click="handleNodeClick" placeholder="请输入微信公众号名称" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</pane>
|
||||
<pane size="80" class="ml10">
|
||||
<splitpanes horizontal>
|
||||
<pane>
|
||||
<splitpanes>
|
||||
<pane>
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="userCumulateRef"></div>
|
||||
</div>
|
||||
</pane>
|
||||
<pane>
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="userShardRef"></div>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
<pane>
|
||||
<splitpanes>
|
||||
<pane>
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="upstreamMsgDistMonthRef"></div>
|
||||
</div>
|
||||
</pane>
|
||||
<pane>
|
||||
<div class="home-card-item">
|
||||
<div style="height: 100%" ref="interfaceSummaryRef"></div>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</div>
|
||||
</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: (name?: string) => {
|
||||
return fetchAccountList({
|
||||
name: name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
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;
|
||||
})
|
||||
.catch((err) => {
|
||||
useMessage().error(err.msg);
|
||||
})
|
||||
.finally(() => {
|
||||
userCumulate();
|
||||
userShard();
|
||||
upstreamMsgDistMonth();
|
||||
interfaceSummary();
|
||||
});
|
||||
};
|
||||
|
||||
// 默认选择第一个公众号
|
||||
onMounted(async () => {
|
||||
const { data } = await deptData.queryList();
|
||||
if (data?.length > 0) {
|
||||
handleNodeClick(data[0]);
|
||||
}
|
||||
});
|
||||
</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);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user