Merge branch 'master' of https://gitee.com/xingyuv/yudao-ui-admin-vben
commit
1f75f376f6
10
README.md
10
README.md
|
@ -13,11 +13,8 @@
|
||||||
## 开发文档
|
## 开发文档
|
||||||
[开发文档](./dev.md)
|
[开发文档](./dev.md)
|
||||||
|
|
||||||
### 说明
|
## 交流群
|
||||||
- 本项目为ruoyi-vue-pro vue3 antdv 版本ui
|
<img alt="index.vue" width="400px" src="./docimg/wx.jpg"></img>
|
||||||
- 基于vben2.9.0版本并升级到最新的依赖,后续将升级antdv4
|
|
||||||
- 目前开发中
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## 开发进度
|
## 开发进度
|
||||||
- 系统管理 页面适配 99%
|
- 系统管理 页面适配 99%
|
||||||
|
@ -94,9 +91,6 @@ pnpm front
|
||||||
pnpm build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
## 交流群
|
|
||||||
<img alt="index.vue" width="400px" src="./docimg/code.jpg"></img>
|
|
||||||
|
|
||||||
## Git 贡献提交规范
|
## Git 贡献提交规范
|
||||||
|
|
||||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { configVisualizerConfig } from './visualizer'
|
||||||
import { configThemePlugin } from './theme'
|
import { configThemePlugin } from './theme'
|
||||||
import { configSvgIconsPlugin } from './svgSprite'
|
import { configSvgIconsPlugin } from './svgSprite'
|
||||||
|
|
||||||
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
export async function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
||||||
const { VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv
|
const { VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv
|
||||||
|
|
||||||
const vitePlugins: (PluginOption | PluginOption[])[] = [
|
const vitePlugins: (PluginOption | PluginOption[])[] = [
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { PluginOption } from 'vite'
|
||||||
|
|
||||||
export function configSvgIconsPlugin(isBuild: boolean) {
|
export function configSvgIconsPlugin(isBuild: boolean) {
|
||||||
const svgIconsPlugin = createSvgIconsPlugin({
|
const svgIconsPlugin = createSvgIconsPlugin({
|
||||||
|
@ -13,5 +14,5 @@ export function configSvgIconsPlugin(isBuild: boolean) {
|
||||||
// default
|
// default
|
||||||
symbolId: 'icon-[dir]-[name]'
|
symbolId: 'icon-[dir]-[name]'
|
||||||
})
|
})
|
||||||
return svgIconsPlugin
|
return svgIconsPlugin as PluginOption
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import visualizer from 'rollup-plugin-visualizer'
|
import visualizer from 'rollup-plugin-visualizer'
|
||||||
import { isReportMode } from '../../utils'
|
import { isReportMode } from '../../utils'
|
||||||
|
import { PluginOption } from 'vite'
|
||||||
|
|
||||||
export function configVisualizerConfig() {
|
export function configVisualizerConfig() {
|
||||||
if (isReportMode()) {
|
if (isReportMode()) {
|
||||||
|
@ -11,7 +12,7 @@ export function configVisualizerConfig() {
|
||||||
open: true,
|
open: true,
|
||||||
gzipSize: true,
|
gzipSize: true,
|
||||||
brotliSize: true
|
brotliSize: true
|
||||||
}) as Plugin
|
}) as PluginOption
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
BIN
docimg/code.jpg
BIN
docimg/code.jpg
Binary file not shown.
Before Width: | Height: | Size: 144 KiB |
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
|
@ -140,8 +140,10 @@
|
||||||
"url": "https://github.com/xingyuv/issues"
|
"url": "https://github.com/xingyuv/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/xingyuv",
|
"homepage": "https://github.com/xingyuv",
|
||||||
|
"packageManager": "pnpm@8.1.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.0.0"
|
"node": ">= 16.0.0",
|
||||||
|
"pnpm": ">=7.30.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
|
4084
pnpm-lock.yaml
4084
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
||||||
import { defHttp } from '@/utils/http/axios'
|
import { defHttp } from '@/utils/http/axios'
|
||||||
import { UploadFileParams } from '@/types/axios'
|
|
||||||
|
|
||||||
export interface ProfileDept {
|
export interface ProfileDept {
|
||||||
id: number
|
id: number
|
||||||
|
@ -87,10 +86,7 @@ export function updateUserPwdApi(oldPassword: string, newPassword: string) {
|
||||||
|
|
||||||
// 用户头像上传
|
// 用户头像上传
|
||||||
export function uploadAvatarApi(data) {
|
export function uploadAvatarApi(data) {
|
||||||
const params: UploadFileParams = {
|
return defHttp.put({ url: Api.uploadAvatarApi, data: { file: data } })
|
||||||
file: data
|
|
||||||
}
|
|
||||||
return defHttp.uploadFile({ url: Api.uploadAvatarApi }, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 社交绑定,使用 code 授权码
|
// 社交绑定,使用 code 授权码
|
||||||
|
|
|
@ -7,5 +7,6 @@ export default {
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
detail: 'Detail',
|
detail: 'Detail',
|
||||||
export: 'Export',
|
export: 'Export',
|
||||||
import: 'Import'
|
import: 'Import',
|
||||||
|
sync: 'Sync'
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,6 @@ export default {
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
detail: '详情',
|
detail: '详情',
|
||||||
export: '导出',
|
export: '导出',
|
||||||
import: '导入'
|
import: '导入',
|
||||||
|
sync: '同步'
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,54 @@ export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): str
|
||||||
return dayjs(date).format(format)
|
return dayjs(date).format(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function beginOfDay(date) {
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||||
|
}
|
||||||
|
|
||||||
|
export function endOfDay(date) {
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function betweenDay(date1, date2) {
|
||||||
|
date1 = convertDate(date1)
|
||||||
|
date2 = convertDate(date2)
|
||||||
|
// 计算差值
|
||||||
|
return Math.floor((date2.getTime() - date1.getTime()) / (24 * 3600 * 1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDate(date, fmt) {
|
||||||
|
date = convertDate(date)
|
||||||
|
const o = {
|
||||||
|
'M+': date.getMonth() + 1, //月份
|
||||||
|
'd+': date.getDate(), //日
|
||||||
|
'H+': date.getHours(), //小时
|
||||||
|
'm+': date.getMinutes(), //分
|
||||||
|
's+': date.getSeconds(), //秒
|
||||||
|
'q+': Math.floor((date.getMonth() + 3) / 3), //季度
|
||||||
|
S: date.getMilliseconds() //毫秒
|
||||||
|
}
|
||||||
|
if (/(y+)/.test(fmt)) {
|
||||||
|
// 年份
|
||||||
|
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||||
|
}
|
||||||
|
for (const k in o) {
|
||||||
|
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||||
|
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addTime(date, time) {
|
||||||
|
date = convertDate(date)
|
||||||
|
return new Date(date.getTime() + time)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertDate(date) {
|
||||||
|
if (typeof date === 'string') {
|
||||||
|
return new Date(date)
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
export const dateUtil = dayjs
|
export const dateUtil = dayjs
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<div class="mb-2">头像</div>
|
<div class="mb-2">头像</div>
|
||||||
<CropperAvatar
|
<CropperAvatar
|
||||||
:value="avatar"
|
:value="avatar"
|
||||||
|
:uploadApi="uploadAvatarApi as any"
|
||||||
btnText="更换头像"
|
btnText="更换头像"
|
||||||
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
||||||
@change="updateAvatar"
|
@change="updateAvatar"
|
||||||
|
@ -56,7 +57,6 @@ async function updateAvatar({ src, data }) {
|
||||||
const userinfo = userStore.getUserInfo
|
const userinfo = userStore.getUserInfo
|
||||||
userinfo.user.avatar = src
|
userinfo.user.avatar = src
|
||||||
userStore.setUserInfo(userinfo)
|
userStore.setUserInfo(userinfo)
|
||||||
console.log('data', data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
|
|
@ -12,6 +12,30 @@ export interface ListItem {
|
||||||
color?: string
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tab的list
|
||||||
|
export const settingList = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
name: '基本设置',
|
||||||
|
component: 'BaseSetting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
name: '安全设置',
|
||||||
|
component: 'SecureSetting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
name: '账号绑定',
|
||||||
|
component: 'AccountBind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
name: '新消息通知',
|
||||||
|
component: 'MsgNotify'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// 基础设置 form
|
// 基础设置 form
|
||||||
export const baseSetschemas: FormSchema[] = [
|
export const baseSetschemas: FormSchema[] = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
{ icon: IconEnum.DOWNLOAD, label: '生成', auth: 'infra:codegen:download', onClick: handleGenTable.bind(null, record) },
|
{ icon: IconEnum.DOWNLOAD, label: '生成', auth: 'infra:codegen:download', onClick: handleGenTable.bind(null, record) },
|
||||||
{
|
{
|
||||||
icon: IconEnum.RESET,
|
icon: IconEnum.RESET,
|
||||||
label: '同步',
|
label: t('action.sync'),
|
||||||
auth: 'infra:codegen:update',
|
auth: 'infra:codegen:update',
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: '确认要强制同步' + record.tableName + '表结构吗?',
|
title: '确认要强制同步' + record.tableName + '表结构吗?',
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ref, unref } from 'vue'
|
||||||
import { BasicModal, useModalInner } from '@/components/Modal'
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
import { BasicForm, useForm } from '@/components/Form'
|
import { BasicForm, useForm } from '@/components/Form'
|
||||||
import { formSchema } from './dataSourceConfig.data'
|
import { formSchema } from './dataSourceConfig.data'
|
||||||
import { createPost, getPost, updatePost } from '@/api/system/post'
|
import { createDataSourceConfig, getDataSourceConfig, updateDataSourceConfig } from '@/api/infra/dataSourceConfig'
|
||||||
|
|
||||||
const emit = defineEmits(['success', 'register'])
|
const emit = defineEmits(['success', 'register'])
|
||||||
const isUpdate = ref(true)
|
const isUpdate = ref(true)
|
||||||
|
@ -26,7 +26,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
||||||
setModalProps({ confirmLoading: false })
|
setModalProps({ confirmLoading: false })
|
||||||
isUpdate.value = !!data?.isUpdate
|
isUpdate.value = !!data?.isUpdate
|
||||||
if (unref(isUpdate)) {
|
if (unref(isUpdate)) {
|
||||||
const res = await getPost(data.record.id)
|
const res = await getDataSourceConfig(data.record.id)
|
||||||
setFieldsValue({ ...res })
|
setFieldsValue({ ...res })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -36,9 +36,9 @@ async function handleSubmit() {
|
||||||
const values = await validate()
|
const values = await validate()
|
||||||
setModalProps({ confirmLoading: true })
|
setModalProps({ confirmLoading: true })
|
||||||
if (unref(isUpdate)) {
|
if (unref(isUpdate)) {
|
||||||
await updatePost(values)
|
await updateDataSourceConfig(values)
|
||||||
} else {
|
} else {
|
||||||
await createPost(values)
|
await createDataSourceConfig(values)
|
||||||
}
|
}
|
||||||
closeModal()
|
closeModal()
|
||||||
emit('success')
|
emit('success')
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'mp:account:update', onClick: handleEdit.bind(null, record) }]"
|
||||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'mp:account:update', onClick: handleEdit.bind(null, record) },
|
:drop-down-actions="[
|
||||||
{
|
{
|
||||||
icon: IconEnum.RESET,
|
icon: IconEnum.RESET,
|
||||||
label: '生成二维码',
|
label: '生成二维码',
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<BasicTable @register="registerTable">
|
<BasicTable @register="registerTable">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<a-button type="primary" v-auth="['mp:user:sync']" :preIcon="IconEnum.RESET" @click="handleSync">
|
<a-button type="primary" v-auth="['mp:user:sync']" :preIcon="IconEnum.RESET" @click="handleSync">
|
||||||
{{ t('action.create') }}
|
{{ t('action.sync') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column }">
|
<template #bodyCell="{ column }">
|
||||||
|
@ -49,7 +49,7 @@ const [registerTable, { getForm, reload }] = useTable({
|
||||||
/** 同步按钮操作 */
|
/** 同步按钮操作 */
|
||||||
async function handleSync() {
|
async function handleSync() {
|
||||||
createConfirm({
|
createConfirm({
|
||||||
title: '同步粉丝',
|
title: t('action.sync'),
|
||||||
iconType: 'warning',
|
iconType: 'warning',
|
||||||
content: '是否确认同步粉丝?',
|
content: '是否确认同步粉丝?',
|
||||||
async onOk() {
|
async onOk() {
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<!-- <template>
|
||||||
|
<Card title="接口分析数据" :loading="loading">
|
||||||
|
<div ref="chartRef" :style="{ width, height }"></div>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref, ref, watch } from 'vue'
|
||||||
|
import { Card } from 'ant-design-vue'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
loading: Boolean,
|
||||||
|
newUser: propTypes.array,
|
||||||
|
cancelUser: propTypes.array,
|
||||||
|
width: propTypes.string.def('100%'),
|
||||||
|
height: propTypes.string.def('300px')
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
|
const newUserData = ref<any[]>(props.newUser)
|
||||||
|
const cancelUserData = ref<any[]>(props.cancelUser)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.loading,
|
||||||
|
() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setOptions({
|
||||||
|
color: ['#67C23A', '#e5323e', '#E6A23C', '#409EFF'],
|
||||||
|
legend: {
|
||||||
|
data: ['被动回复用户消息的次数', '失败次数', '最大耗时', '总耗时']
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
xAxis: {
|
||||||
|
data: [] // X 轴的日期范围
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '被动回复用户消息的次数',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: 0,
|
||||||
|
data: [] // 被动回复用户消息的次数的数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '失败次数',
|
||||||
|
type: 'bar',
|
||||||
|
data: [] // 失败次数的数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '最大耗时',
|
||||||
|
type: 'bar',
|
||||||
|
data: [] // 最大耗时的数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '总耗时',
|
||||||
|
type: 'bar',
|
||||||
|
data: [] // 总耗时的数据
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script> -->
|
||||||
|
<template><div>开发中</div></template>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<!-- <template>
|
||||||
|
<Card title="消息概况数据" :loading="loading">
|
||||||
|
<div ref="chartRef" :style="{ width, height }"></div>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref, ref, watch } from 'vue'
|
||||||
|
import { Card } from 'ant-design-vue'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
loading: Boolean,
|
||||||
|
newUser: propTypes.array,
|
||||||
|
cancelUser: propTypes.array,
|
||||||
|
width: propTypes.string.def('100%'),
|
||||||
|
height: propTypes.string.def('300px')
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
|
const newUserData = ref<any[]>(props.newUser)
|
||||||
|
const cancelUserData = ref<any[]>(props.cancelUser)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.loading,
|
||||||
|
() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setOptions({
|
||||||
|
color: ['#67C23A', '#e5323e'],
|
||||||
|
legend: {
|
||||||
|
data: ['用户发送人数', '用户发送条数']
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
xAxis: {
|
||||||
|
data: [] // X 轴的日期范围
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
minInterval: 1
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '用户发送人数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: newUserData.value // 用户发送人数的数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户发送条数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: cancelUserData.value // 用户发送条数的数据
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script> -->
|
||||||
|
<template><div>开发中</div></template>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<!-- <template>
|
||||||
|
<Card title="累计用户数据" :loading="loading">
|
||||||
|
<div ref="chartRef" :style="{ width, height }"></div>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref, onMounted, ref, watch } from 'vue'
|
||||||
|
import { Card } from 'ant-design-vue'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { getUserCumulate } from '@/api/mp/statistics'
|
||||||
|
import { beginOfDay, endOfDay, formatToDateTime } from '@/utils/dateUtil'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
loading: Boolean,
|
||||||
|
accountId: propTypes.number,
|
||||||
|
dataTime: {
|
||||||
|
type: Array as PropType<Date[]>,
|
||||||
|
default: () => [
|
||||||
|
beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7)), // -7 天
|
||||||
|
endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24))
|
||||||
|
]
|
||||||
|
},
|
||||||
|
width: propTypes.string.def('100%'),
|
||||||
|
height: propTypes.string.def('300px')
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
|
const optionsData = ref()
|
||||||
|
|
||||||
|
async function initData() {
|
||||||
|
const res = await getUserCumulate({
|
||||||
|
accountId: props.accountId,
|
||||||
|
date: [formatToDateTime(props.dataTime[0]), formatToDateTime(props.dataTime[1])]
|
||||||
|
})
|
||||||
|
optionsData.value = res.cumulateUser
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => props.loading,
|
||||||
|
() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setOptions({
|
||||||
|
legend: {
|
||||||
|
data: ['累计用户量']
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: optionsData.value
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
minInterval: 1
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '累计用户量',
|
||||||
|
data: [], // 累计用户量的数据
|
||||||
|
type: 'line',
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await initData()
|
||||||
|
})
|
||||||
|
</script> -->
|
||||||
|
<template><div>开发中</div></template>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<!-- <template>
|
||||||
|
<Card title="用户增减数据" :loading="loading">
|
||||||
|
<div ref="chartRef" :style="{ width, height }"></div>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref, onMounted, ref, watch } from 'vue'
|
||||||
|
import { Card } from 'ant-design-vue'
|
||||||
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { getUserSummary } from '@/api/mp/statistics'
|
||||||
|
import { addTime, beginOfDay, betweenDay, endOfDay, formatDate, formatToDateTime } from '@/utils/dateUtil'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
loading: Boolean,
|
||||||
|
accountId: propTypes.number,
|
||||||
|
dataTime: {
|
||||||
|
type: Array as PropType<Date[]>,
|
||||||
|
default: () => [
|
||||||
|
beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7)), // -7 天
|
||||||
|
endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24))
|
||||||
|
]
|
||||||
|
},
|
||||||
|
width: propTypes.string.def('100%'),
|
||||||
|
height: propTypes.string.def('300px')
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement | null>(null)
|
||||||
|
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
||||||
|
|
||||||
|
const optionsData = ref()
|
||||||
|
|
||||||
|
const xAxisDate = ref()
|
||||||
|
|
||||||
|
async function initData() {
|
||||||
|
const res = await getUserSummary({
|
||||||
|
accountId: props.accountId,
|
||||||
|
date: [formatToDateTime(props.dataTime[0]), formatToDateTime(props.dataTime[1])]
|
||||||
|
})
|
||||||
|
optionsData.value = res
|
||||||
|
xAxisDate.value = []
|
||||||
|
const days = betweenDay(props.dataTime[0], props.dataTime[1]) // 相差天数
|
||||||
|
for (let i = 0; i <= days; i++) {
|
||||||
|
xAxisDate.value.push(formatDate(addTime(props.dataTime[0], 3600 * 1000 * 24 * i), 'yyyy-MM-dd'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => props.loading,
|
||||||
|
() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setOptions({
|
||||||
|
color: ['#67C23A', '#e5323e'],
|
||||||
|
legend: {
|
||||||
|
data: ['新增用户', '取消关注的用户']
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
xAxis: {
|
||||||
|
data: xAxisDate.value // X 轴的日期范围
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
minInterval: 1
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '新增用户',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: 0,
|
||||||
|
data: optionsData.value.newUserData // 新增用户的数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '取消关注的用户',
|
||||||
|
type: 'bar',
|
||||||
|
data: optionsData.value.cancelUserData // 取消关注的用户的数据
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await initData()
|
||||||
|
})
|
||||||
|
</script> -->
|
||||||
|
<template><div>开发中</div></template>
|
|
@ -1,3 +1,20 @@
|
||||||
<template>
|
<!-- <template>
|
||||||
<div>开发中</div>
|
<div>
|
||||||
|
<UserSummaryChart class="md:w-1/2 w-full" :loading="loading" :accountId="accountId" />
|
||||||
|
<UserCumulateChart class="md:w-1/2 w-full" :loading="loading" :accountId="accountId" />
|
||||||
|
<UpstreamMessageChart class="md:w-1/2 w-full" :loading="loading" :accountId="accountId" />
|
||||||
|
<InterfaceSummaryChart class="md:w-1/2 w-full" :loading="loading" :accountId="accountId" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts" setup name="Statistics">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import UserSummaryChart from './components/UserSummaryChart.vue'
|
||||||
|
import UserCumulateChart from './components/UserCumulateChart.vue'
|
||||||
|
import UpstreamMessageChart from './components/UpstreamMessageChart.vue'
|
||||||
|
import InterfaceSummaryChart from './components/InterfaceSummaryChart.vue'
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
|
||||||
|
const accountId = ref(1)
|
||||||
|
</script> -->
|
||||||
|
<template><div>开发中</div></template>
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" @register="registerModal" :title="isUpdate ? '编辑' : '新增'" @ok="handleSubmit">
|
||||||
|
<BasicForm @register="registerForm" />
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup name="PostModal">
|
||||||
|
import { ref, unref } from 'vue'
|
||||||
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
|
import { BasicForm, useForm } from '@/components/Form'
|
||||||
|
import { formSchema } from './tag.data'
|
||||||
|
import { createTag, updateTag, getTag } from '@/api/mp/tag'
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'register'])
|
||||||
|
const isUpdate = ref(true)
|
||||||
|
|
||||||
|
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
||||||
|
labelWidth: 120,
|
||||||
|
baseColProps: { span: 24 },
|
||||||
|
schemas: formSchema,
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
actionColOptions: { span: 23 }
|
||||||
|
})
|
||||||
|
|
||||||
|
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||||
|
resetFields()
|
||||||
|
setModalProps({ confirmLoading: false })
|
||||||
|
isUpdate.value = !!data?.isUpdate
|
||||||
|
if (unref(isUpdate)) {
|
||||||
|
const res = await getTag(data.record.id)
|
||||||
|
setFieldsValue({ ...res })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const values = await validate()
|
||||||
|
setModalProps({ confirmLoading: true })
|
||||||
|
if (unref(isUpdate)) {
|
||||||
|
await updateTag(values)
|
||||||
|
} else {
|
||||||
|
await createTag(values)
|
||||||
|
}
|
||||||
|
closeModal()
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
setModalProps({ confirmLoading: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,3 +1,90 @@
|
||||||
<template>
|
<template>
|
||||||
<div>开发中</div>
|
<div>
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<template #toolbar>
|
||||||
|
<a-button type="primary" v-auth="['mp:tag:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
|
||||||
|
{{ t('action.create') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="warning" v-auth="['mp:tag:sync']" :preIcon="IconEnum.RESET" @click="handleSync">
|
||||||
|
{{ t('action.sync') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'mp:tag:update', onClick: handleEdit.bind(null, record) },
|
||||||
|
{
|
||||||
|
icon: IconEnum.DELETE,
|
||||||
|
color: 'error',
|
||||||
|
label: t('action.delete'),
|
||||||
|
auth: 'mp:tag:delete',
|
||||||
|
popConfirm: {
|
||||||
|
title: t('common.delMessage'),
|
||||||
|
placement: 'left',
|
||||||
|
confirm: handleDelete.bind(null, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<TagModal @register="registerModal" @success="reload()" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts" setup name="Tag">
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import { useModal } from '@/components/Modal'
|
||||||
|
import TagModal from './TagModal.vue'
|
||||||
|
import { IconEnum } from '@/enums/appEnum'
|
||||||
|
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
||||||
|
import { deleteTag, getTagPage, syncTag } from '@/api/mp/tag'
|
||||||
|
import { columns, searchFormSchema } from './tag.data'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { createConfirm, createMessage } = useMessage()
|
||||||
|
const [registerModal, { openModal }] = useModal()
|
||||||
|
|
||||||
|
const [registerTable, { getForm, reload }] = useTable({
|
||||||
|
title: '标签列表',
|
||||||
|
api: getTagPage,
|
||||||
|
columns,
|
||||||
|
formConfig: { labelWidth: 120, schemas: searchFormSchema },
|
||||||
|
useSearchForm: true,
|
||||||
|
showTableSetting: true,
|
||||||
|
actionColumn: {
|
||||||
|
width: 140,
|
||||||
|
title: t('common.action'),
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleCreate() {
|
||||||
|
openModal(true, { isUpdate: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
openModal(true, { record, isUpdate: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSync() {
|
||||||
|
createConfirm({
|
||||||
|
title: t('action.sync'),
|
||||||
|
iconType: 'warning',
|
||||||
|
content: t('common.exportMessage'),
|
||||||
|
async onOk() {
|
||||||
|
await syncTag(getForm().getFieldsValue().accountId)
|
||||||
|
createMessage.success(t('common.exportSuccessText'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(record: Recordable) {
|
||||||
|
await deleteTag(record.id)
|
||||||
|
createMessage.success(t('common.delSuccessText'))
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { getSimpleAccounts } from '@/api/mp/account'
|
||||||
|
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
||||||
|
|
||||||
|
export const columns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '编号',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标签名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '粉丝数',
|
||||||
|
dataIndex: 'count',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 180,
|
||||||
|
customRender: ({ text }) => {
|
||||||
|
return useRender.renderDate(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '公众号',
|
||||||
|
field: 'accountId',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
api: () => getSimpleAccounts(),
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id'
|
||||||
|
},
|
||||||
|
colProps: { span: 8 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签名称',
|
||||||
|
field: 'name',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 8 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const formSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '编号',
|
||||||
|
field: 'id',
|
||||||
|
show: false,
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签名称',
|
||||||
|
field: 'name',
|
||||||
|
required: true,
|
||||||
|
component: 'Input'
|
||||||
|
}
|
||||||
|
]
|
|
@ -20,7 +20,7 @@ const __APP_INFO__ = {
|
||||||
lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
|
lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
export default async ({ command, mode }: ConfigEnv): Promise<UserConfig> => {
|
||||||
const root = process.cwd()
|
const root = process.cwd()
|
||||||
|
|
||||||
const env = loadEnv(mode, root)
|
const env = loadEnv(mode, root)
|
||||||
|
@ -92,7 +92,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
},
|
},
|
||||||
|
|
||||||
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
|
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
|
||||||
plugins: createVitePlugins(viteEnv, isBuild),
|
plugins: await createVitePlugins(viteEnv, isBuild),
|
||||||
|
|
||||||
optimizeDeps: { include, exclude }
|
optimizeDeps: { include, exclude }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue