提交 c9e75e46 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 新增匹配老师数据和清空用户数据

上级 37cd14b1
import httpRequest from '@/utils/axios'
// 获取实验试题列表
export function getExperimentQuestionList() {
return httpRequest.get('/api/lab/v1/student/experiment-question/list')
}
// 学生创建群组页面-获取老师维护的群组
export function getTeacherGroups(params?: { type?: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/teacher-groups', { params })
}
// 学生创建标签页面-获取老师维护的标签
export function getTeacherTags() {
return httpRequest.get('/api/lab/v1/experiment/tag/teacher-tags')
}
// 学生创建营销物料页面-获取老师维护的营销物料
export function getTeacherMaterials(params?: { type?: string }) {
return httpRequest.get('/api/lab/v1/experiment/marketing-ai/teacher-materials', { params })
}
import type { Ref } from 'vue'
import { getExperimentQuestionList, getTeacherGroups, getTeacherTags, getTeacherMaterials } from '@/api/question'
import { useUserStore } from '@/stores/user'
type QuesitonItem = {
experiment_id: string
id: string
type: string
}
type OptionItem = {
id: string
name: string
}
type MaterialItem = {
id: string
name: string
type: string
content: ''
}
export function useQuestion(questionType: string | Ref<string>, type?: string) {
questionType = shallowRef(questionType)
const useStore = useUserStore()
// 试题列表
const questionList = ref<QuesitonItem[]>([])
const fetchQuestionList = async () => {
const res = await getExperimentQuestionList()
questionList.value = res.data.items
}
const hasQuestion = computed(() => {
return !!questionList.value.find((item) => item.type === questionType.value)
})
onMounted(async () => {
if (useStore.role?.id === 1) {
await fetchQuestionList()
}
if (hasQuestion.value) {
if (questionType.value == '202') await fetchTeacherTagList()
if (questionType.value == '301' || questionType.value == '302') await fetchTeacherGroupList()
if (['401', '402', '403', '404', '405', '406', '407', '408'].includes(questionType.value)) {
await fetchTeacherMaterialList()
}
}
})
const teacherGroupList = ref<OptionItem[]>([])
const fetchTeacherGroupList = async () => {
const res = await getTeacherGroups({ type })
teacherGroupList.value = res.data.items
}
const teacherTagList = ref<OptionItem[]>([])
const fetchTeacherTagList = async () => {
const res = await getTeacherTags()
teacherTagList.value = res.data.items
}
const teacherMaterialAllList = ref<MaterialItem[]>([])
const teacherMaterialList = computed(() => {
const questionTypes: any = {
'401': 1,
'402': 2,
'403': 3,
'404': 4,
'405': 5,
'406': 6,
'407': 7,
'408': 8,
}
return teacherMaterialAllList.value.filter((item) => item.type == questionTypes[questionType.value])
})
const fetchTeacherMaterialList = async () => {
const res = await getTeacherMaterials()
teacherMaterialAllList.value = res.data.items
}
return {
hasQuestion,
questionList,
fetchQuestionList,
teacherGroupList,
fetchTeacherGroupList,
teacherTagList,
fetchTeacherTagList,
teacherMaterialList,
fetchTeacherMaterialList,
}
}
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
updateDynamicGroup, updateDynamicGroup,
getGroupInfo, getGroupInfo,
createRFMGroup, createRFMGroup,
updateRFMGroup updateRFMGroup,
} from '../api' } from '../api'
import UserRule from '@/components/rule/UserRule.vue' import UserRule from '@/components/rule/UserRule.vue'
import EventRule from '@/components/rule/EventRule.vue' import EventRule from '@/components/rule/EventRule.vue'
...@@ -18,6 +18,7 @@ import LabelRule from '@/components/rule/LabelRule.vue' ...@@ -18,6 +18,7 @@ import LabelRule from '@/components/rule/LabelRule.vue'
import UserActionRule from '@/components/rule/UserActionRule.vue' import UserActionRule from '@/components/rule/UserActionRule.vue'
import RFMRule from '@/components/rule/RFMRule.vue' import RFMRule from '@/components/rule/RFMRule.vue'
import { pick, merge } from 'lodash-es' import { pick, merge } from 'lodash-es'
import { useQuestion } from '@/composables/useQuestion'
interface Props { interface Props {
data: Partial<Group> data: Partial<Group>
...@@ -47,7 +48,8 @@ const form: any = reactive({ ...@@ -47,7 +48,8 @@ const form: any = reactive({
event_attr_rule: { current_logic_operate: 'and', items: [] }, event_attr_rule: { current_logic_operate: 'and', items: [] },
tag_rule: { current_logic_operate: 'and', items: [] }, tag_rule: { current_logic_operate: 'and', items: [] },
user_action_rule: { current_logic_operate: 'and', items: [] }, user_action_rule: { current_logic_operate: 'and', items: [] },
rules: { R: {}, F: {}, M: {} } rules: { R: {}, F: {}, M: {} },
teacher_id: '',
}) })
function genRuleData(data: any) { function genRuleData(data: any) {
...@@ -56,7 +58,7 @@ function genRuleData(data: any) { ...@@ -56,7 +58,7 @@ function genRuleData(data: any) {
} }
function fetchInfo() { function fetchInfo() {
if (!props.data.id) return if (!props.data.id) return
getGroupInfo({ id: props.data.id }).then(res => { getGroupInfo({ id: props.data.id }).then((res) => {
const { detail } = res.data const { detail } = res.data
const user_attr_rule = genRuleData(detail.user_attr_rule) const user_attr_rule = genRuleData(detail.user_attr_rule)
const event_attr_rule = genRuleData(detail.event_attr_rule) const event_attr_rule = genRuleData(detail.event_attr_rule)
...@@ -82,7 +84,7 @@ function fetchInfo() { ...@@ -82,7 +84,7 @@ function fetchInfo() {
event_attr_rule: { ...event_attr_rule, items: eventRuleItems }, event_attr_rule: { ...event_attr_rule, items: eventRuleItems },
user_action_rule, user_action_rule,
tag_rule: { ...tag_rule, items: tagRuleItems }, tag_rule: { ...tag_rule, items: tagRuleItems },
update_rule: { type: 1, info: 1 } update_rule: { type: 1, info: 1 },
}) })
}) })
} }
...@@ -90,9 +92,13 @@ function fetchInfo() { ...@@ -90,9 +92,13 @@ function fetchInfo() {
watchEffect(() => fetchInfo()) watchEffect(() => fetchInfo())
const rules = ref<FormRules>({ const rules = ref<FormRules>({
name: [{ required: true, message: '请输入群组名称' }] name: [{ required: true, message: '请输入群组名称' }],
}) })
const questionType = props.data.type === '1' ? '301' : '302'
const { hasQuestion, teacherGroupList } = useQuestion(questionType, props.data.type)
// 提交 // 提交
function handleSubmit() { function handleSubmit() {
formRef.value?.validate().then(() => (isUpdate.value ? handleUpdate() : handleCreate())) formRef.value?.validate().then(() => (isUpdate.value ? handleUpdate() : handleCreate()))
...@@ -122,7 +128,7 @@ async function handleCreate() { ...@@ -122,7 +128,7 @@ async function handleCreate() {
user_attr_rule: JSON.stringify([form.user_attr_rule]), user_attr_rule: JSON.stringify([form.user_attr_rule]),
event_attr_rule: JSON.stringify([form.event_attr_rule]), event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([{ ...form.tag_rule, ...tagRule }]), tag_rule: JSON.stringify([{ ...form.tag_rule, ...tagRule }]),
user_action_rule: JSON.stringify(form.user_action_rule) user_action_rule: JSON.stringify(form.user_action_rule),
}, },
[ [
'name', 'name',
...@@ -132,7 +138,8 @@ async function handleCreate() { ...@@ -132,7 +138,8 @@ async function handleCreate() {
'user_attr_rule', 'user_attr_rule',
'event_attr_rule', 'event_attr_rule',
'tag_rule', 'tag_rule',
'user_action_rule' 'user_action_rule',
'teacher_id',
] ]
) )
await createDynamicGroup(params) await createDynamicGroup(params)
...@@ -142,9 +149,9 @@ async function handleCreate() { ...@@ -142,9 +149,9 @@ async function handleCreate() {
{ {
...form, ...form,
update_rule: JSON.stringify(form.update_rule), update_rule: JSON.stringify(form.update_rule),
rules: JSON.stringify(form.rules) rules: JSON.stringify(form.rules),
}, },
['name', 'status', 'update_status', 'update_rule', 'rules'] ['name', 'status', 'update_status', 'update_rule', 'rules', 'teacher_id']
) )
await createRFMGroup(params) await createRFMGroup(params)
} }
...@@ -156,7 +163,7 @@ async function handleCreate() { ...@@ -156,7 +163,7 @@ async function handleCreate() {
async function handleUpdate() { async function handleUpdate() {
if (props.data.type === '1') { if (props.data.type === '1') {
// 静态群组 // 静态群组
const params = pick(form, ['id', 'name', 'status']) const params = pick(form, ['id', 'name', 'status', 'teacher_id'])
await updateStaticGroup(params) await updateStaticGroup(params)
} else if (props.data.type === '2') { } else if (props.data.type === '2') {
// 动态群组 // 动态群组
...@@ -186,7 +193,7 @@ async function handleUpdate() { ...@@ -186,7 +193,7 @@ async function handleUpdate() {
user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]), user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]),
event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }]), event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }]),
tag_rule: JSON.stringify([{ ...form.tag_rule, ...tagRule }]), tag_rule: JSON.stringify([{ ...form.tag_rule, ...tagRule }]),
user_action_rule: JSON.stringify(form.user_action_rule) user_action_rule: JSON.stringify(form.user_action_rule),
}, },
[ [
'id', 'id',
...@@ -197,7 +204,8 @@ async function handleUpdate() { ...@@ -197,7 +204,8 @@ async function handleUpdate() {
'user_attr_rule', 'user_attr_rule',
'event_attr_rule', 'event_attr_rule',
'tag_rule', 'tag_rule',
'user_action_rule' 'user_action_rule',
'teacher_id',
] ]
) )
await updateDynamicGroup(params) await updateDynamicGroup(params)
...@@ -209,7 +217,8 @@ async function handleUpdate() { ...@@ -209,7 +217,8 @@ async function handleUpdate() {
'status', 'status',
'update_status', 'update_status',
'update_rule', 'update_rule',
'rules' 'rules',
'teacher_id',
]) ])
await updateRFMGroup(params) await updateRFMGroup(params)
} }
...@@ -221,7 +230,7 @@ async function handleUpdate() { ...@@ -221,7 +230,7 @@ async function handleUpdate() {
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="980px" @closed="$emit('update:modelValue', false)"> <el-dialog :title="title" :close-on-click-modal="false" width="980px" @closed="$emit('update:modelValue', false)">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px"> <el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="110px">
<el-form-item label="群组名称" prop="name"> <el-form-item label="群组名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" /> <el-input v-model="form.name" placeholder="请输入" />
</el-form-item> </el-form-item>
...@@ -269,6 +278,11 @@ async function handleUpdate() { ...@@ -269,6 +278,11 @@ async function handleUpdate() {
</div> </div>
</el-form-item> </el-form-item>
</template> </template>
<el-form-item label="匹配老师标签" prop="teacher_id" v-if="hasQuestion">
<el-select v-model="form.teacher_id" style="width: 100%">
<el-option v-for="item in teacherGroupList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-switch v-model="form.status" active-text="生效" active-value="1" inactive-text="失效" inactive-value="0" /> <el-switch v-model="form.status" active-text="生效" active-value="1" inactive-text="失效" inactive-value="0" />
</el-form-item> </el-form-item>
......
...@@ -6,6 +6,7 @@ import { updateStatusRuleList, dateUnitList, weekList, labelList } from '@/utils ...@@ -6,6 +6,7 @@ import { updateStatusRuleList, dateUnitList, weekList, labelList } from '@/utils
import { createLabel, updateLabel } from '../api' import { createLabel, updateLabel } from '../api'
import { useLabelType } from '../composables/useLabelType' import { useLabelType } from '../composables/useLabelType'
import { pick } from 'lodash-es' import { pick } from 'lodash-es'
import { useQuestion } from '@/composables/useQuestion'
const props = defineProps<{ const props = defineProps<{
data?: Label data?: Label
...@@ -30,7 +31,8 @@ const form = reactive({ ...@@ -30,7 +31,8 @@ const form = reactive({
update_rule: { type: 1, info: 1 }, update_rule: { type: 1, info: 1 },
status: '1', status: '1',
label: '', label: '',
weight: 0 weight: 0,
teacher_id: '',
}) })
watchEffect(() => { watchEffect(() => {
if (props.data) { if (props.data) {
...@@ -48,9 +50,11 @@ const rules = ref<FormRules>({ ...@@ -48,9 +50,11 @@ const rules = ref<FormRules>({
name: [{ required: true, message: '请输入标签名称' }], name: [{ required: true, message: '请输入标签名称' }],
label: [{ required: true, message: '请选择标签类型' }], label: [{ required: true, message: '请选择标签类型' }],
type_id: [{ required: true, message: '请选择标签目录' }], type_id: [{ required: true, message: '请选择标签目录' }],
update_status: [{ required: true, message: '请选择更新评率' }] update_status: [{ required: true, message: '请选择更新评率' }],
}) })
const { hasQuestion, teacherTagList } = useQuestion('202')
// 提交 // 提交
function handleSubmit() { function handleSubmit() {
formRef?.validate().then(() => (isUpdate ? handleUpdate() : handleCreate())) formRef?.validate().then(() => (isUpdate ? handleUpdate() : handleCreate()))
...@@ -64,7 +68,8 @@ function handleCreate() { ...@@ -64,7 +68,8 @@ function handleCreate() {
'update_rule', 'update_rule',
'status', 'status',
'label', 'label',
'weight' 'weight',
'teacher_id',
]) ])
createLabel(params).then(() => { createLabel(params).then(() => {
ElMessage({ message: '创建成功', type: 'success' }) ElMessage({ message: '创建成功', type: 'success' })
...@@ -82,7 +87,8 @@ function handleUpdate() { ...@@ -82,7 +87,8 @@ function handleUpdate() {
'update_rule', 'update_rule',
'status', 'status',
'label', 'label',
'weight' 'weight',
'teacher_id',
]) ])
updateLabel(params).then(() => { updateLabel(params).then(() => {
ElMessage({ message: '修改成功', type: 'success' }) ElMessage({ message: '修改成功', type: 'success' })
...@@ -94,7 +100,7 @@ function handleUpdate() { ...@@ -94,7 +100,7 @@ function handleUpdate() {
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @closed="$emit('update:modelValue', false)"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @closed="$emit('update:modelValue', false)">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px"> <el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="110px">
<el-form-item label="标签名称" prop="name"> <el-form-item label="标签名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" /> <el-input v-model="form.name" placeholder="请输入" />
</el-form-item> </el-form-item>
...@@ -103,6 +109,11 @@ function handleUpdate() { ...@@ -103,6 +109,11 @@ function handleUpdate() {
<el-option v-for="item in labelList" :key="item.value" :label="item.label" :value="item.value"></el-option> <el-option v-for="item in labelList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="匹配老师标签" prop="teacher_id" v-if="hasQuestion">
<el-select v-model="form.teacher_id" style="width: 100%">
<el-option v-for="item in teacherTagList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="标签目录" prop="type_id"> <el-form-item label="标签目录" prop="type_id">
<el-select v-model="form.type_id" style="width: 100%"> <el-select v-model="form.type_id" style="width: 100%">
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id"></el-option> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
......
...@@ -29,6 +29,7 @@ export interface Label { ...@@ -29,6 +29,7 @@ export interface Label {
updated_operator: Operator updated_operator: Operator
label: string label: string
weight: string weight: string
teacher_id?: string
} }
// 标签更新规则 // 标签更新规则
export interface LabelUpdateRule { export interface LabelUpdateRule {
...@@ -38,7 +39,10 @@ export interface LabelUpdateRule { ...@@ -38,7 +39,10 @@ export interface LabelUpdateRule {
export type LabelListRequest = Pick<Label, 'id' | 'name'> & { experiment_id?: string } export type LabelListRequest = Pick<Label, 'id' | 'name'> & { experiment_id?: string }
export type LabelUpdateRequest = Pick<Label, 'id' | 'name' | 'type_id' | 'update_status' | 'update_rule' | 'status' | 'label'> & { export type LabelUpdateRequest = Pick<
Label,
'id' | 'name' | 'type_id' | 'update_status' | 'update_rule' | 'status' | 'label' | 'teacher_id'
> & {
experiment_id?: string experiment_id?: string
} }
......
<script setup> <script setup>
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { materialMethodList } from '@/utils/dictionary' import { materialMethodList } from '@/utils/dictionary'
import { useQuestion } from '@/composables/useQuestion'
const materialType = useMapStore().getMapValuesByKey('experiment_marketing_material_type') const materialType = useMapStore().getMapValuesByKey('experiment_marketing_material_type')
...@@ -14,7 +15,7 @@ const formRef = ref() ...@@ -14,7 +15,7 @@ const formRef = ref()
const rules = ref({ const rules = ref({
type: [{ required: true, message: '请选择营销内容类型' }], type: [{ required: true, message: '请选择营销内容类型' }],
name: [{ required: true, message: '请输入内容名称' }] name: [{ required: true, message: '请输入内容名称' }],
}) })
async function handleValidate() { async function handleValidate() {
...@@ -35,25 +36,61 @@ function wayDisabled(item, type) { ...@@ -35,25 +36,61 @@ function wayDisabled(item, type) {
} }
return false return false
} }
const questionType = computed(() => {
const questionTypes = {
1: '401',
2: '402',
3: '403',
4: '404',
5: '405',
6: '406',
7: '407',
8: '408',
}
return questionTypes[form.value.type]
})
const { hasQuestion, teacherMaterialList } = useQuestion(questionType, form.value.type)
</script> </script>
<template> <template>
<el-card shadow="never"> <el-card shadow="never">
<template #header>基础信息</template> <template #header>基础信息</template>
<el-form label-suffix=":" label-width="130" :model="form" :rules="rules" ref="formRef" :disabled="action === 'view'"> <el-form
label-suffix=":"
label-width="130"
:model="form"
:rules="rules"
ref="formRef"
:disabled="action === 'view'">
<el-form-item label="营销内容类型" prop="type"> <el-form-item label="营销内容类型" prop="type">
<el-radio-group v-model="form.type" :disabled="action === 'update'" @change="form.way = '2'"> <el-radio-group v-model="form.type" :disabled="action === 'update'" @change="form.way = '2'">
<el-radio v-for="item in materialType" :key="item.id" :value="item.value" :disabled="$route.query.type ? item.value !== $route.query.type : false">{{ item.label }}</el-radio> <el-radio
v-for="item in materialType"
:key="item.id"
:value="item.value"
:disabled="$route.query.type ? item.value !== $route.query.type : false"
>{{ item.label }}</el-radio
>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="内容名称" prop="name"> <el-form-item label="内容名称" prop="name">
<el-input v-model="form.name" placeholder="请输入内容名称" /> <el-input v-model="form.name" placeholder="请输入内容名称" />
</el-form-item> </el-form-item>
<el-form-item label="匹配老师标签" prop="teacher_id" v-if="hasQuestion">
<el-select v-model="form.teacher_id" style="width: 100%">
<el-option v-for="item in teacherMaterialList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="创作方式" prop="way"> <el-form-item label="创作方式" prop="way">
<el-radio-group v-model="form.way"> <el-radio-group v-model="form.way">
<el-radio v-for="item in materialMethodList" :key="item.value" :value="item.value" :disabled="wayDisabled(item, form.type)">{{ <el-radio
item.label v-for="item in materialMethodList"
}}</el-radio> :key="item.value"
:value="item.value"
:disabled="wayDisabled(item, form.type)"
>{{ item.label }}</el-radio
>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
......
...@@ -25,6 +25,7 @@ const form: any = reactive({ ...@@ -25,6 +25,7 @@ const form: any = reactive({
key_points: '', key_points: '',
content: '', content: '',
extend_info: {}, extend_info: {},
teacher_id: '',
}) })
const detail = ref() const detail = ref()
......
...@@ -120,3 +120,8 @@ export function getConnectionList() { ...@@ -120,3 +120,8 @@ export function getConnectionList() {
export function syncMember() { export function syncMember() {
return httpRequest.get('/api/lab/v1/experiment/member/sync-member') return httpRequest.get('/api/lab/v1/experiment/member/sync-member')
} }
// 学生用户列表-清空数据(lab系统)
export function clearMember() {
return httpRequest.get('/api/lab/v1/experiment/member/clear')
}
<script setup lang="ts"> <script setup lang="ts">
import { Plus, Download, Upload, Delete } from '@element-plus/icons-vue' import { Plus, Download, Upload, Delete } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue' import AppList from '@/components/base/AppList.vue'
import { getMemberList, deleteMember, getMemberConnectionsList, syncMember } from '../api' import { getMemberList, deleteMember, getMemberConnectionsList, syncMember, clearMember } from '../api'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus' import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import type { MemberProp, ConnectionsProp } from '../types' import type { MemberProp, ConnectionsProp } from '../types'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
...@@ -211,6 +211,17 @@ const handleSync = async () => { ...@@ -211,6 +211,17 @@ const handleSync = async () => {
ElMessage({ message: '同步成功', type: 'success' }) ElMessage({ message: '同步成功', type: 'success' })
handleRefresh() handleRefresh()
} }
const handleClear = async () => {
await ElMessageBox.confirm('确定要清空用户数据吗?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await clearMember()
ElMessage({ message: '清空成功', type: 'success' })
handleRefresh()
}
</script> </script>
<template> <template>
...@@ -270,6 +281,7 @@ const handleSync = async () => { ...@@ -270,6 +281,7 @@ const handleSync = async () => {
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<el-button type="danger" @click="handleClear" v-if="userStore.role?.id == 1">清空数据</el-button>
</el-space> </el-space>
<el-space> <el-space>
<router-link to="/analyze/user"><el-button type="primary">用户分析</el-button></router-link> <router-link to="/analyze/user"><el-button type="primary">用户分析</el-button></router-link>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论