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)
|
||||
|
||||
### 说明
|
||||
- 本项目为ruoyi-vue-pro vue3 antdv 版本ui
|
||||
- 基于vben2.9.0版本并升级到最新的依赖,后续将升级antdv4
|
||||
- 目前开发中
|
||||
</div>
|
||||
## 交流群
|
||||
<img alt="index.vue" width="400px" src="./docimg/wx.jpg"></img>
|
||||
|
||||
## 开发进度
|
||||
- 系统管理 页面适配 99%
|
||||
|
@ -94,9 +91,6 @@ pnpm front
|
|||
pnpm build
|
||||
```
|
||||
|
||||
## 交流群
|
||||
<img alt="index.vue" width="400px" src="./docimg/code.jpg"></img>
|
||||
|
||||
## 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))
|
||||
|
|
|
@ -14,7 +14,7 @@ import { configVisualizerConfig } from './visualizer'
|
|||
import { configThemePlugin } from './theme'
|
||||
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 vitePlugins: (PluginOption | PluginOption[])[] = [
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||
import path from 'path'
|
||||
import { PluginOption } from 'vite'
|
||||
|
||||
export function configSvgIconsPlugin(isBuild: boolean) {
|
||||
const svgIconsPlugin = createSvgIconsPlugin({
|
||||
|
@ -13,5 +14,5 @@ export function configSvgIconsPlugin(isBuild: boolean) {
|
|||
// default
|
||||
symbolId: 'icon-[dir]-[name]'
|
||||
})
|
||||
return svgIconsPlugin
|
||||
return svgIconsPlugin as PluginOption
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import visualizer from 'rollup-plugin-visualizer'
|
||||
import { isReportMode } from '../../utils'
|
||||
import { PluginOption } from 'vite'
|
||||
|
||||
export function configVisualizerConfig() {
|
||||
if (isReportMode()) {
|
||||
|
@ -11,7 +12,7 @@ export function configVisualizerConfig() {
|
|||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true
|
||||
}) as Plugin
|
||||
}) as PluginOption
|
||||
}
|
||||
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"
|
||||
},
|
||||
"homepage": "https://github.com/xingyuv",
|
||||
"packageManager": "pnpm@8.1.0",
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
"node": ">= 16.0.0",
|
||||
"pnpm": ">=7.30.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{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 { UploadFileParams } from '@/types/axios'
|
||||
|
||||
export interface ProfileDept {
|
||||
id: number
|
||||
|
@ -87,10 +86,7 @@ export function updateUserPwdApi(oldPassword: string, newPassword: string) {
|
|||
|
||||
// 用户头像上传
|
||||
export function uploadAvatarApi(data) {
|
||||
const params: UploadFileParams = {
|
||||
file: data
|
||||
}
|
||||
return defHttp.uploadFile({ url: Api.uploadAvatarApi }, params)
|
||||
return defHttp.put({ url: Api.uploadAvatarApi, data: { file: data } })
|
||||
}
|
||||
|
||||
// 社交绑定,使用 code 授权码
|
||||
|
|
|
@ -7,5 +7,6 @@ export default {
|
|||
delete: 'Delete',
|
||||
detail: 'Detail',
|
||||
export: 'Export',
|
||||
import: 'Import'
|
||||
import: 'Import',
|
||||
sync: 'Sync'
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@ export default {
|
|||
delete: '删除',
|
||||
detail: '详情',
|
||||
export: '导出',
|
||||
import: '导入'
|
||||
import: '导入',
|
||||
sync: '同步'
|
||||
}
|
||||
|
|
|
@ -14,4 +14,54 @@ export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): str
|
|||
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
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<div class="mb-2">头像</div>
|
||||
<CropperAvatar
|
||||
:value="avatar"
|
||||
:uploadApi="uploadAvatarApi as any"
|
||||
btnText="更换头像"
|
||||
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
||||
@change="updateAvatar"
|
||||
|
@ -56,7 +57,6 @@ async function updateAvatar({ src, data }) {
|
|||
const userinfo = userStore.getUserInfo
|
||||
userinfo.user.avatar = src
|
||||
userStore.setUserInfo(userinfo)
|
||||
console.log('data', data)
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
|
|
|
@ -12,6 +12,30 @@ export interface ListItem {
|
|||
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
|
||||
export const baseSetschemas: FormSchema[] = [
|
||||
{
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{ icon: IconEnum.DOWNLOAD, label: '生成', auth: 'infra:codegen:download', onClick: handleGenTable.bind(null, record) },
|
||||
{
|
||||
icon: IconEnum.RESET,
|
||||
label: '同步',
|
||||
label: t('action.sync'),
|
||||
auth: 'infra:codegen:update',
|
||||
popConfirm: {
|
||||
title: '确认要强制同步' + record.tableName + '表结构吗?',
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ref, unref } from 'vue'
|
|||
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||
import { BasicForm, useForm } from '@/components/Form'
|
||||
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 isUpdate = ref(true)
|
||||
|
@ -26,7 +26,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
|||
setModalProps({ confirmLoading: false })
|
||||
isUpdate.value = !!data?.isUpdate
|
||||
if (unref(isUpdate)) {
|
||||
const res = await getPost(data.record.id)
|
||||
const res = await getDataSourceConfig(data.record.id)
|
||||
setFieldsValue({ ...res })
|
||||
}
|
||||
})
|
||||
|
@ -36,9 +36,9 @@ async function handleSubmit() {
|
|||
const values = await validate()
|
||||
setModalProps({ confirmLoading: true })
|
||||
if (unref(isUpdate)) {
|
||||
await updatePost(values)
|
||||
await updateDataSourceConfig(values)
|
||||
} else {
|
||||
await createPost(values)
|
||||
await createDataSourceConfig(values)
|
||||
}
|
||||
closeModal()
|
||||
emit('success')
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'mp:account:update', onClick: handleEdit.bind(null, record) },
|
||||
:actions="[{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'mp:account:update', onClick: handleEdit.bind(null, record) }]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
icon: IconEnum.RESET,
|
||||
label: '生成二维码',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<BasicTable @register="registerTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" v-auth="['mp:user:sync']" :preIcon="IconEnum.RESET" @click="handleSync">
|
||||
{{ t('action.create') }}
|
||||
{{ t('action.sync') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column }">
|
||||
|
@ -49,7 +49,7 @@ const [registerTable, { getForm, reload }] = useTable({
|
|||
/** 同步按钮操作 */
|
||||
async function handleSync() {
|
||||
createConfirm({
|
||||
title: '同步粉丝',
|
||||
title: t('action.sync'),
|
||||
iconType: 'warning',
|
||||
content: '是否确认同步粉丝?',
|
||||
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>
|
||||
<div>开发中</div>
|
||||
<!-- <template>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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')
|
||||
}
|
||||
|
||||
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||
export default async ({ command, mode }: ConfigEnv): Promise<UserConfig> => {
|
||||
const root = process.cwd()
|
||||
|
||||
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
|
||||
plugins: createVitePlugins(viteEnv, isBuild),
|
||||
plugins: await createVitePlugins(viteEnv, isBuild),
|
||||
|
||||
optimizeDeps: { include, exclude }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue