提交 1fb15727 authored 作者: lihuihui's avatar lihuihui
......@@ -113,6 +113,7 @@
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useArrayUnique": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
......@@ -196,6 +197,7 @@
"useParallax": true,
"usePermission": true,
"usePointer": true,
"usePointerLock": true,
"usePointerSwipe": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
......
......@@ -3,7 +3,12 @@ require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript'],
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'./.eslintrc-auto-import.json'
],
parserOptions: {
ecmaVersion: 'latest'
},
......
......@@ -114,6 +114,7 @@ declare global {
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
......@@ -197,6 +198,7 @@ declare global {
const useParallax: typeof import('@vueuse/core')['useParallax']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
......
差异被折叠。
......@@ -18,9 +18,9 @@
"@element-plus/icons-vue": "^2.0.10",
"@tinymce/tinymce-vue": "^5.0.0",
"@vueuse/core": "^9.12.0",
"axios": "^1.3.1",
"axios": "^1.3.3",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.2.28",
"element-plus": "^2.2.30",
"lodash-es": "^4.17.21",
"pinia": "^2.0.30",
"vue": "^3.2.47",
......@@ -29,18 +29,18 @@
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@types/blueimp-md5": "^2.18.0",
"@types/node": "^18.11.18",
"@types/node": "^18.13.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/tsconfig": "^0.1.3",
"ali-oss": "^6.17.1",
"chalk": "^5.2.0",
"eslint": "^8.33.0",
"eslint": "^8.34.0",
"eslint-plugin-vue": "^9.9.0",
"npm-run-all": "^4.1.5",
"sass": "^1.58.0",
"sass": "^1.58.1",
"typescript": "~4.9.5",
"unplugin-auto-import": "^0.13.0",
"unplugin-auto-import": "^0.14.2",
"vite": "^4.1.1",
"vue-tsc": "^1.0.24"
}
......
......@@ -54,6 +54,16 @@ export function getMetaEventList() {
}
// 获取实验下的所有标签
export function getTagList(params?: { check_role?: boolean }) {
export function getTagList(params?: { check_role?: 0 | 1 }) {
return httpRequest.get('/api/lab/v1/experiment/tag/all', { params })
}
// 获取实验下的所有连接
export function getConnectionList() {
return httpRequest.get('/api/lab/v1/experiment/connection/all')
}
// 搜索紫荆用户
export function searchUser(params: any) {
return httpRequest.get('/api/lab/v1/experiment/system/search-user', { params })
}
.rule {
display: flex;
.el-form-item {
display: inline-flex;
margin-right: 10px;
margin-bottom: 0;
}
.el-form-item__content {
margin-left: 0 !important;
}
.el-select,
.el-input {
width: 120px;
......
差异被折叠。
<script setup lang="ts">
import { searchUser } from '@/api/base'
interface SearchUser {
id: string
realname: string
nickname: string
username: string
mobile: string
email: string
}
let loading = $ref(false)
let userList = $ref<SearchUser[]>()
const remoteMethod = (q: string) => {
if (!q) return
loading = true
searchUser({ q: q }).then(res => {
loading = false
userList = res.data.items
})
}
</script>
<template>
<el-select remote filterable value-key="id" :loading="loading" :remote-method="remoteMethod" style="width: 100%">
<el-option
v-for="item in userList"
:key="item.id"
:label="item.realname || item.nickname || item.username"
:value="item.id">
<span>{{ item.realname || item.nickname || item.username }}</span>
<template v-if="item.mobile">
<el-divider direction="vertical" />
<span>{{ item.mobile }}</span>
</template>
<template v-if="item.email">
<el-divider direction="vertical" />
<span>{{ item.email }}</span>
</template>
</el-option>
</el-select>
</template>
......@@ -38,35 +38,44 @@ function toggleOperate(rule: EventRule) {
}
// 添加条件
function handleAdd() {
eventAttrRule.value.items.push({
function handleAdd(items: EventRuleItem[]) {
items.push({
happen_info: { is_happened: true, event_id: '', event_name: '', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: '' }
})
}
// 删除条件
function handleRemove(index: number) {
eventAttrRule.value.items.splice(index, 1)
}
// 事件改变
function handleEventChange(value: string, rule: EventRuleItem) {
const currentEvent = metaEventList.value.find(item => item.id === value)
rule.happen_info.event_name = currentEvent?.name || ''
function handleRemove(items: EventRuleItem[], index: number) {
items.splice(index, 1)
}
// 添加属性条件
function handleAttrAdd(attrs: RuleAttr[]) {
attrs.push({ attr_id: '', attr: '', attr_name: '', attr_type: '', operate: '', operate_name: '', value: '' })
}
// 删除属性条件
function handleAttrRemove(attrs: RuleAttr[], index: number) {
attrs.splice(index, 1)
}
// 发生条件改变
function handleHappenOperateChange(value: string, item: EventRuleItem) {
item.happen_info.attr_list = []
item.trigger_info.value = ''
}
// 事件改变
function handleEventChange(value: string, item: EventRuleItem) {
const currentEvent = metaEventList.value.find(item => item.id === value)
item.happen_info.event_name = currentEvent?.name || ''
item.happen_info.attr_list = []
}
// 属性改变
function handleAttrChange(value: string, attr: RuleAttr, rule: EventRuleItem) {
const found = getEventAttrList(rule.happen_info.event_id).find(item => item.id === value)
function handleAttrChange(value: string, attr: RuleAttr, item: EventRuleItem) {
const found = getEventAttrList(item.happen_info.event_id).find(item => item.id === value)
attr.attr = found?.english_name || ''
attr.attr_name = found?.name || ''
attr.attr_type = found?.type || ''
......@@ -75,6 +84,7 @@ function handleAttrChange(value: string, attr: RuleAttr, rule: EventRuleItem) {
attr.operate_name = ''
attr.value = ''
}
// 条件改变
function handleOperateChange(value: string, item: RuleAttr) {
const found = getOperatorList(item.attr_type).find(item => item.value === value)
......@@ -82,10 +92,11 @@ function handleOperateChange(value: string, item: RuleAttr) {
item.value = ''
}
// 条件改变
function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
// 触发条件改变
function handleTriggerOperateChange(value: string, item: EventRuleItem) {
const found = numberOperatorList.find(item => item.value === value)
rule.trigger_info.operate_name = found?.label || ''
item.trigger_info.operate_name = found?.label || ''
item.trigger_info.value = ''
}
</script>
......@@ -104,11 +115,13 @@ function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
<el-row>
<!-- 发生 -->
<el-form-item>
<el-select v-model="rule.happen_info.is_happened">
<el-option v-for="option in happenInfoList" v-bind="option"></el-option>
<el-select
v-model="rule.happen_info.is_happened"
@change="value => handleHappenOperateChange(value, rule)">
<el-option v-for="option in happenInfoList" :key="option.label" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-form-item v-if="rule.happen_info.is_happened">
<el-select v-model="rule.happen_info.event_id" @change="value => handleEventChange(value, rule)">
<el-option
v-for="option in metaEventList"
......@@ -117,58 +130,77 @@ function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
:value="option.id"></el-option>
</el-select>
</el-form-item>
<el-button text :icon="Plus" @click="handleAttrAdd(rule.happen_info.attr_list)">添加条件</el-button>
<el-button text :icon="CloseBold" @click="handleRemove(index)"></el-button>
<el-button
text
:icon="Plus"
@click="handleAttrAdd(rule.happen_info.attr_list)"
v-if="rule.happen_info.is_happened"
>添加条件</el-button
>
<el-button text :icon="CloseBold" @click="handleRemove(eventAttrRule.items, index)"></el-button>
</el-row>
<!-- 属性条件 -->
<el-row class="rule-item" justify="space-between" v-for="(attr, index) in rule.happen_info.attr_list">
<div>
<template v-if="rule.happen_info.is_happened">
<!-- 属性条件 -->
<el-row
justify="space-between"
class="rule-item"
v-for="(attr, index) in rule.happen_info.attr_list"
:key="index">
<div>
<el-form-item>
<el-select v-model="attr.attr_id" @change="value => handleAttrChange(value, attr, rule)">
<el-option
v-for="option in getEventAttrList(rule.happen_info.event_id)"
:key="option.id"
:label="option.name"
:value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)">
<el-option
v-for="option in getOperatorList(attr.attr_type)"
:key="option.value"
:label="option.alias || option.label"
:value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="attr.value" />
</el-form-item>
</div>
<el-button
text
:icon="CloseBold"
@click="handleAttrRemove(rule.happen_info.attr_list, index)"></el-button>
</el-row>
<!-- 触发 -->
<el-row style="margin-top: 10px">
<el-form-item>
<el-select v-model="attr.attr_id" @change="value => handleAttrChange(value, attr, rule)">
<el-option
v-for="option in getEventAttrList(rule.happen_info.event_id)"
:key="option.id"
:label="option.name"
:value="option.id"></el-option>
<el-select model-value="触发次数">
<el-option v-for="option in triggerInfoList" :key="option.value" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)">
<el-select
v-model="rule.trigger_info.operate"
@change="value => handleTriggerOperateChange(value, rule)">
<el-option
v-for="option in getOperatorList(attr.attr_type)"
v-for="option in numberOperatorList"
:key="option.value"
:label="option.alias || option.label"
:value="option.value"></el-option>
:value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="attr.value" />
<el-input v-model="rule.trigger_info.value" />
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleAttrRemove(rule.happen_info.attr_list, index)"></el-button>
</el-row>
<!-- 触发 -->
<el-row style="margin-top: 10px">
<el-form-item>
<el-select model-value="触发次数">
<el-option v-for="option in triggerInfoList" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="rule.trigger_info.operate" @change="value => handleTriggerOperateChange(value, rule)">
<el-option
v-for="option in numberOperatorList"
:label="option.alias || option.label"
:value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="rule.trigger_info.value" />
</el-form-item>
</el-row>
</el-row>
</template>
</section>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd">添加条件</el-button>
<el-button text :icon="Plus" @click="handleAdd(eventAttrRule.items)">添加条件</el-button>
</el-card>
</template>
......
<script setup lang="ts"></script>
<script setup lang="ts">
import type { TagRule } from '@/types'
import { PriceTag, Plus, CloseBold } from '@element-plus/icons-vue'
import { useTag } from '@/composables/useAllData'
<template></template>
const tagRule = ref(inject('tagRule') as TagRule)
const { tagList } = useTag()
// 获取逻辑运算符名称
function getLogicalName(value: 'and' | 'or') {
return value === 'or' ? '或' : '且'
}
// 切换逻辑运算符
function toggleOperate(rule: TagRule) {
rule.current_logic_operate = rule.current_logic_operate === 'or' ? 'and' : 'or'
}
// 添加条件
function handleAdd(items: string[]) {
items.push('')
}
// 删除
function handleRemove(items: string[], index: number) {
items.splice(index, 1)
}
</script>
<template>
<el-card shadow="never">
<template #header>
<el-button circle color="#567722" :icon="PriceTag"></el-button>
标签满足以下条件
</template>
<div class="rule" v-if="tagRule.items.length">
<div class="rule-operator">
<span @click="toggleOperate(tagRule)">{{ getLogicalName(tagRule.current_logic_operate) }}</span>
</div>
<div class="rule-list">
<el-row justify="space-between" class="rule-item" v-for="(item, index) in tagRule.items" :key="index">
<div>
标签 等于
<el-form-item>
<el-select v-model="tagRule.items[index]">
<el-option
v-for="option in tagList"
:key="option.id"
:label="option.name"
:value="option.id"></el-option>
</el-select>
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleRemove(tagRule.items, index)"></el-button>
</el-row>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd(tagRule.items)">添加条件</el-button>
</el-card>
</template>
<style src="@/assets/styles/rule.scss"></style>
......@@ -27,21 +27,13 @@ function toggleOperate(rule: UserAttrRule) {
}
// 添加条件
function handleAdd() {
userAttrRule.value.items.push({
attr_id: '',
attr: '',
attr_name: '',
attr_type: '',
operate: '',
operate_name: '',
value: ''
})
function handleAdd(items: RuleAttr[]) {
items.push({ attr_id: '', attr: '', attr_name: '', attr_type: '', operate: '', operate_name: '', value: '' })
}
// 删除
function handleRemove(rule: UserAttrRule, index: number) {
rule.items.splice(index, 1)
function handleRemove(items: RuleAttr[], index: number) {
items.splice(index, 1)
}
// 属性改变
......@@ -75,7 +67,7 @@ function handleOperateChange(value: string, item: RuleAttr) {
<span @click="toggleOperate(userAttrRule)">{{ getLogicalName(userAttrRule.current_logic_operate) }}</span>
</div>
<div class="rule-list">
<el-row class="rule-item" v-for="(item, index) in userAttrRule.items" justify="space-between">
<el-row justify="space-between" class="rule-item" v-for="(item, index) in userAttrRule.items" :key="index">
<div>
<el-form-item>
<el-select v-model="item.attr_id" @change="value => handleAttrChange(value, item)">
......@@ -90,6 +82,7 @@ function handleOperateChange(value: string, item: RuleAttr) {
<el-select v-model="item.operate" @change="value => handleOperateChange(value, item)">
<el-option
v-for="option in getOperatorList(item.attr_type)"
:key="option.value"
:label="option.alias || option.label"
:value="option.value"></el-option>
</el-select>
......@@ -98,11 +91,11 @@ function handleOperateChange(value: string, item: RuleAttr) {
<el-input v-model="item.value" />
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleRemove(userAttrRule, index)"></el-button>
<el-button text :icon="CloseBold" @click="handleRemove(userAttrRule.items, index)"></el-button>
</el-row>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd">添加条件</el-button>
<el-button text :icon="Plus" @click="handleAdd(userAttrRule.items)">添加条件</el-button>
</el-card>
</template>
......
import { getMetaUserAttrList, getMetaEventList, getTagList } from '@/api/base'
import { getMetaUserAttrList, getMetaEventList, getTagList, getConnectionList } from '@/api/base'
interface AttrType {
// 用户属性类型
export interface AttrType {
id: string
name: string
type: string
......@@ -8,6 +9,31 @@ interface AttrType {
english_name: string
pinyin: string
}
// 事件类型
interface MetaEventType {
id: string
name: string
english_name: string
pinyin: string
event_attrs: AttrType[]
}
// 标签类型
export interface TagType {
id: string
name: string
}
// 连接类型
export interface ConnectionType {
id: string
name: string
type: string
status: '0' | '1'
config_attributes: any
}
// 所有用户属性
const userAttrList = ref<AttrType[]>([])
export function useUserAttr() {
function fetchUserAttrList() {
......@@ -21,14 +47,8 @@ export function useUserAttr() {
return { fetchUserAttrList, userAttrList }
}
interface MetaEvent {
id: string
name: string
english_name: string
pinyin: string
event_attrs: AttrType[]
}
const metaEventList = ref<MetaEvent[]>([])
// 所有事件
const metaEventList = ref<MetaEventType[]>([])
export function useMetaEvent() {
function fetchMetaEventList() {
getMetaEventList().then((res: any) => {
......@@ -41,10 +61,11 @@ export function useMetaEvent() {
return { fetchMetaEventList, metaEventList }
}
const tagList = ref([])
// 所有标签
const tagList = ref<TagType[]>([])
export function useTag() {
function fetchTagList() {
getTagList({ check_role: true }).then((res: any) => {
getTagList({ check_role: 1 }).then((res: any) => {
tagList.value = res.data.items
})
}
......@@ -53,3 +74,21 @@ export function useTag() {
})
return { fetchTagList, tagList }
}
// 所有标签
const connectionList = ref<ConnectionType[]>([])
export function useConnection() {
function fetchConnectionList() {
getConnectionList().then((res: any) => {
connectionList.value = res.data.items.map((item: any) => {
const attrs = JSON.parse(item.config_attributes)
const name = Array.isArray(attrs) ? attrs.find((item: any) => item.prop === 'name')?.value : attrs.name
return { ...item, config_attributes: attrs, name }
})
})
}
onMounted(() => {
if (!connectionList.value?.length) fetchConnectionList()
})
return { fetchConnectionList, connectionList }
}
......@@ -12,6 +12,11 @@ export function getGroupList(params?: GroupListRequest) {
return httpRequest.get('/api/lab/v1/experiment/group/list', { params })
}
// 获取群组详情
export function getGroupInfo(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/detail', { params })
}
// 创建静态群组
export function createStaticGroup(data: StaticGroupCreateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/create-static-group', data)
......@@ -42,6 +47,11 @@ export function getGroupStatistics(params: { group_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/statistics-detail', { params })
}
// 更新群组数据信息
export function updateGroupStatistics(data: { id: string }) {
return httpRequest.post('/api/lab/v1/experiment/group/statistics-user-group', data)
}
// 获取群组成员
export function getGroupMembers(params: { group_id: string; name?: string; id?: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/members', { params })
......
......@@ -3,7 +3,10 @@ import type { Group } from '../types'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { updateStatusRuleList, dateUnitList, weekList } from '@/utils/dictionary'
import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamicGroup } from '../api'
import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamicGroup, getGroupInfo } from '../api'
import UserRule from '@/components/rule/UserRule.vue'
import EventRule from '@/components/rule/EventRule.vue'
import LabelRule from '@/components/rule/LabelRule.vue'
import { pick } from 'lodash-es'
interface Props {
......@@ -31,12 +34,17 @@ const form = reactive({
name: '',
type: '',
status: '1',
update_status: '1',
update_status: '2',
update_rule: { type: 1, info: 1 },
user_attr_rule: [],
event_attr_rule: [],
tag_rule: []
user_attr_rule: { current_logic_operate: 'and', items: [] },
event_attr_rule: { current_logic_operate: 'and', items: [] },
tag_rule: { current_logic_operate: 'and', items: [] }
})
provide('userAttrRule', toRef(form, 'user_attr_rule'))
provide('eventAttrRule', toRef(form, 'event_attr_rule'))
provide('tagRule', toRef(form, 'tag_rule'))
watchEffect(() => {
if (props.data?.id) {
let updateRule = { type: 1, info: 1 }
......@@ -49,6 +57,19 @@ watchEffect(() => {
}
})
function fetchInfo() {
if (!props.data.id) return
getGroupInfo({ id: props.data.id }).then(res => {
const { detail } = res.data
const [user_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.user_attr_rule
const [event_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.event_attr_rule
const [tag_rule = { current_logic_operate: 'and', items: [] }] = detail.tag_rule
Object.assign(form, { user_attr_rule, event_attr_rule, tag_rule })
})
}
watchEffect(() => fetchInfo())
const rules = ref<FormRules>({
name: [{ required: true, message: '请输入群组名称' }],
url: [{ required: true, message: '请选择标签类型图标' }]
......@@ -70,9 +91,9 @@ async function handleCreate() {
{
...form,
update_rule: JSON.stringify(form.update_rule),
user_attr_rule: JSON.stringify(form.user_attr_rule),
event_attr_rule: JSON.stringify(form.event_attr_rule),
tag_rule: JSON.stringify(form.tag_rule)
user_attr_rule: JSON.stringify([form.user_attr_rule]),
event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([form.tag_rule])
},
['name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status']
)
......@@ -94,9 +115,9 @@ async function handleUpdate() {
{
...form,
update_rule: JSON.stringify(form.update_rule),
user_attr_rule: JSON.stringify(form.user_attr_rule),
event_attr_rule: JSON.stringify(form.event_attr_rule),
tag_rule: JSON.stringify(form.tag_rule)
user_attr_rule: JSON.stringify([form.user_attr_rule]),
event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([form.tag_rule])
},
['id', 'name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status']
)
......@@ -109,7 +130,7 @@ async function handleUpdate() {
</script>
<template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')">
<el-dialog :title="title" :close-on-click-modal="false" width="800px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px">
<el-form-item label="群组名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
......@@ -117,14 +138,22 @@ async function handleUpdate() {
<template v-if="data.type === '2'">
<el-form-item label="更新频率" prop="update_status">
<el-radio-group v-model="form.update_status">
<el-radio v-for="item in updateStatusRuleList" :key="item.value" :label="item.value">
<el-radio
v-for="item in updateStatusRuleList"
:key="item.value"
:label="item.value"
:disabled="item.value === '1'">
{{ item.label }}
</el-radio>
</el-radio-group>
<div class="update-rule-wrap" v-if="form.update_status === '1'">
<span></span>
<el-select v-model="form.update_rule.type" placeholder=" " style="width: 60px">
<el-option v-for="item in dateUnitList" :label="item.label" :value="item.value"></el-option>
<el-option
v-for="item in dateUnitList"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
<template v-if="form.update_rule.type === 1">
<span>的凌晨更新</span>
......@@ -132,14 +161,18 @@ async function handleUpdate() {
<template v-if="form.update_rule.type === 2">
<span></span>
<el-select v-model="form.update_rule.info" placeholder=" " style="width: 80px">
<el-option v-for="item in weekList" :label="item.label" :value="item.value"></el-option>
<el-option
v-for="item in weekList"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
<span>的凌晨更新</span>
</template>
<template v-if="form.update_rule.type === 3">
<span></span>
<el-select v-model="form.update_rule.info" placeholder=" " style="width: 60px">
<el-option v-for="item in 6" :label="item" :value="item"></el-option>
<el-option v-for="item in 6" :key="item" :label="item" :value="item"></el-option>
</el-select>
<span>天的凌晨更新</span>
</template>
......@@ -149,6 +182,14 @@ async function handleUpdate() {
<el-form-item label="状态" prop="status">
<el-switch v-model="form.status" active-text="生效" active-value="1" inactive-text="失效" inactive-value="0" />
</el-form-item>
<template v-if="data.type === '2'">
<!-- 用户属性规则 -->
<UserRule></UserRule>
<!-- 事件属性规则 -->
<EventRule style="margin-top: 20px"></EventRule>
<!-- 标签 -->
<LabelRule style="margin-top: 20px"></LabelRule>
</template>
</el-form>
<template #footer>
<el-row justify="center">
......
......@@ -3,23 +3,23 @@ import type { Group } from '../types'
import { UserFilled, Plus, Delete } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
import { getNameByValue, groupTypeList, updateStatusRuleList } from '@/utils/dictionary'
import { getNameByValue, groupTypeList, updateStatusRuleList, updateStatusList } from '@/utils/dictionary'
import { useMapStore } from '@/stores/map'
import { getGroupStatistics, getGroupMembers, clearGroupMembers } from '../api'
import { getGroupStatistics, getGroupMembers, clearGroupMembers, updateGroupStatistics } from '../api'
import BindMembers from './BindMembers.vue'
const props = defineProps<{
data: Group
}>()
const emit = defineEmits<{
defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const statusList = useMapStore().getMapValuesByKey('system_status')
const detail = reactive({ count: 0, status: 1, updated_time: '-' })
const detail = reactive({ count: 0, status: '1', updated_time: '-' })
function fetchInfo() {
getGroupStatistics({ group_id: props.data.id }).then(res => {
Object.assign(detail, res.data.detail)
......@@ -27,6 +27,14 @@ function fetchInfo() {
}
watchEffect(() => fetchInfo())
// 立即更新
function handleUpdate() {
updateGroupStatistics({ id: props.data.id }).then(() => {
ElMessage({ type: 'success', message: '更新成功' })
fetchInfo()
})
}
// 清空群组成员
function handleClearMembers() {
ElMessageBox.confirm('确定要删清空该群组成员吗?', '提示').then(() => {
......@@ -36,6 +44,11 @@ function handleClearMembers() {
})
})
}
// 添加群组成员
let selectMembersVisible = $ref(false)
function handleAdd() {
selectMembersVisible = true
}
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
......@@ -63,11 +76,6 @@ function handleRefresh() {
fetchInfo()
appList?.refetch()
}
// 添加群组成员
let selectMembersVisible = $ref(false)
function handleAdd() {
selectMembersVisible = true
}
</script>
<template>
......@@ -120,8 +128,8 @@ function handleAdd() {
<dl>
<dt>更新状态</dt>
<dd>
<span>完成</span>
<el-button type="primary" plain size="small">立即更新</el-button>
<span style="margin-right: 10px">{{ getNameByValue(detail.status, updateStatusList) }}</span>
<el-button type="primary" plain @click="handleUpdate" size="small">立即更新</el-button>
</dd>
</dl>
</template>
......
......@@ -6,6 +6,7 @@ import { ElMessageBox, ElMessage } from 'element-plus'
import { getNameByValue, groupTypeList, updateStatusRuleList } from '@/utils/dictionary'
import { getGroupList, deleteGroup } from '../api'
import { useMapStore } from '@/stores/map'
import SelectUser from '@/components/SelectUser.vue'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const ViewDialog = defineAsyncComponent(() => import('../components/ViewDialog.vue'))
......@@ -14,17 +15,23 @@ const statusList = useMapStore().getMapValuesByKey('system_status')
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listParams = reactive({ name: '', id: '', status: '', updated_operator: '' })
const listOptions = computed(() => {
return {
remote: {
httpRequest: getGroupList,
params: { name: '', id: '', status: '', updated_operator: '' }
params: listParams,
beforeRequest(params: any, isReset: boolean) {
if (isReset) listParams.updated_operator = ''
params.updated_operator = listParams.updated_operator
return params
}
},
filters: [
{ type: 'input', prop: 'name', placeholder: '请输入群组名称' },
{ type: 'input', prop: 'id', placeholder: '请输入群组ID' },
{ type: 'select', prop: 'status', placeholder: '请选择群组状态', options: statusList },
{ type: 'input', prop: 'updated_operator', placeholder: '更新人' }
{ type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' }
],
columns: [
{ type: 'selection' },
......@@ -119,7 +126,9 @@ function handleView(row: Group) {
>
</el-space>
</template>
<template #filter-user>
<SelectUser v-model="listParams.updated_operator" placeholder="更新人" @change="handleRefresh"></SelectUser>
</template>
<template #table-status="{ row }: { row: Group }">
<el-tag :type="row.status === '1' ? 'success' : 'danger'">
{{ getNameByValue(row.status, statusList) }}
......
......@@ -53,6 +53,11 @@ export function getLabelStatistics(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/statistics', { params })
}
// 更新标签数据信息
export function updateLabelStatistics(data: { id: string }) {
return httpRequest.post('/api/lab/v1/experiment/tag/statistics-user-tag', data)
}
// 获取标签规则
export function getLabelRule(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/rule', { params })
......
......@@ -26,7 +26,7 @@ const form = reactive({
id: '',
name: '',
type_id: '',
update_status: '1',
update_status: '2',
update_rule: { type: 1, info: 1 },
status: '1'
})
......@@ -98,14 +98,22 @@ function handleUpdate() {
</el-form-item>
<el-form-item label="更新频率" prop="update_status">
<el-radio-group v-model="form.update_status">
<el-radio v-for="item in updateStatusRuleList" :key="item.value" :label="item.value">
<el-radio
v-for="item in updateStatusRuleList"
:key="item.value"
:label="item.value"
:disabled="item.value === '1'">
{{ item.label }}
</el-radio>
</el-radio-group>
<div class="update-rule-wrap" v-if="form.update_status === '1'">
<span></span>
<el-select v-model="form.update_rule.type" placeholder=" " style="width: 60px">
<el-option v-for="item in dateUnitList" :label="item.label" :value="item.value"></el-option>
<el-option
v-for="item in dateUnitList"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
<template v-if="form.update_rule.type === 1">
<span>的凌晨更新</span>
......@@ -113,14 +121,14 @@ function handleUpdate() {
<template v-if="form.update_rule.type === 2">
<span></span>
<el-select v-model="form.update_rule.info" placeholder=" " style="width: 80px">
<el-option v-for="item in weekList" :label="item.label" :value="item.value"></el-option>
<el-option v-for="item in weekList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<span>的凌晨更新</span>
</template>
<template v-if="form.update_rule.type === 3">
<span></span>
<el-select v-model="form.update_rule.info" placeholder=" " style="width: 60px">
<el-option v-for="item in 6" :label="item" :value="item"></el-option>
<el-option v-for="item in 6" :key="item" :label="item" :value="item"></el-option>
</el-select>
<span>天的凌晨更新</span>
</template>
......
......@@ -2,9 +2,9 @@
import type { Label } from '../types'
import { UserFilled } from '@element-plus/icons-vue'
import { useMapStore } from '@/stores/map'
import { getNameByValue, updateStatusRuleList } from '@/utils/dictionary'
import { getLabelStatistics } from '../api'
// import { ElMessage } from 'element-plus'
import { getNameByValue, updateStatusRuleList, updateStatusList } from '@/utils/dictionary'
import { getLabelStatistics, updateLabelStatistics } from '../api'
import { ElMessage } from 'element-plus'
const props = defineProps<{
data: Label
......@@ -12,14 +12,20 @@ const props = defineProps<{
const statusList = useMapStore().getMapValuesByKey('system_status')
const detail = reactive({ count: 0, status: 1, updated_time: '-' })
const detail = reactive({ count: 0, status: '1', updated_time: '-' })
function fetchInfo() {
getLabelStatistics({ id: props.data.id }).then(res => {
console.log(res)
Object.assign(detail, res.data.detail)
})
}
watchEffect(() => fetchInfo())
function handleUpdate() {
updateLabelStatistics({ id: props.data.id }).then(() => {
ElMessage({ type: 'success', message: '更新成功' })
fetchInfo()
})
}
</script>
<template>
......@@ -62,8 +68,8 @@ watchEffect(() => fetchInfo())
<dl>
<dt>更新状态</dt>
<dd>
<span>完成</span>
<el-button type="primary" plain size="small">立即更新</el-button>
<span style="margin-right: 10px">{{ getNameByValue(detail.status, updateStatusList) }}</span>
<el-button type="primary" plain @click="handleUpdate" size="small">立即更新</el-button>
</dd>
</dl>
</div>
......
......@@ -8,6 +8,7 @@ import { getLabelList, deleteLabel } from '../api'
import { useMapStore } from '@/stores/map'
import { getNameByValue, updateStatusRuleList } from '@/utils/dictionary'
import { useLabelType } from '../composables/useLabelType'
import SelectUser from '@/components/SelectUser.vue'
const LabelFormDialog = defineAsyncComponent(() => import('../components/LabelFormDialog.vue'))
const LabelViewDialog = defineAsyncComponent(() => import('../components/LabelViewDialog.vue'))
......@@ -18,11 +19,17 @@ const { typeList } = useLabelType()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listParams = reactive({ name: '', type_id: '', status: '', updated_operator: '' })
const listOptions = computed(() => {
return {
remote: {
httpRequest: getLabelList,
params: { name: '', type_id: '', status: '', updated_operator: '' }
params: listParams,
beforeRequest(params: any, isReset: boolean) {
if (isReset) listParams.updated_operator = ''
params.updated_operator = listParams.updated_operator
return params
}
},
filters: [
{ type: 'input', prop: 'name', placeholder: '请输入标签名称' },
......@@ -35,7 +42,7 @@ const listOptions = computed(() => {
valueKey: 'id'
},
{ type: 'select', prop: 'status', placeholder: '请选择标签状态', options: statusList },
{ type: 'input', prop: 'updated_operator', placeholder: '更新人' }
{ type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' }
],
columns: [
{ label: '序号', type: 'index', width: 60 },
......@@ -107,6 +114,9 @@ function handleRule(row: Label) {
<el-button type="primary" :icon="Plus" @click="handleAdd">新建</el-button>
</el-space>
</template>
<template #filter-user>
<SelectUser v-model="listParams.updated_operator" placeholder="更新人" @change="handleRefresh"></SelectUser>
</template>
<template #table-status="{ row }: { row: Label }">
<el-tag :type="row.status === '1' ? 'success' : 'danger'">
{{ getNameByValue(row.status, statusList) }}
......
import httpRequest from '@/utils/axios'
import type { TripTemplateListRequest, TripTemplateCreateRequest, TripTemplateUpdateRequest } from './types'
// 获取旅程模板列表
export function getTripTemplateList(params: TripTemplateListRequest) {
return httpRequest.get('/api/lab/v1/experiment/itinerary/list', { params })
......@@ -14,3 +15,13 @@ export function createTripTemplate(data: TripTemplateCreateRequest) {
export function updateTripTemplate(data: TripTemplateUpdateRequest) {
return httpRequest.post('/api/lab/v1/experiment/itinerary/update', data)
}
// 获取旅程连接
export function getTripConnections(params: { itinerary_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/itinerary/connections', { params })
}
// 旅程绑定连接
export function bindTripConnections(data: { itinerary_id: string; connection_ids: string[] }) {
return httpRequest.post('/api/lab/v1/experiment/itinerary/bind-connections', data)
}
<script setup lang="ts">
import type { TripTemplate } from '../types'
import { ElMessage } from 'element-plus'
import { getTripConnections, bindTripConnections } from '../api'
import type { ConnectionType } from '@/composables/useAllData'
import { useConnection } from '@/composables/useAllData'
import ConnectionIcon from '@/components/ConnectionIcon.vue'
const props = defineProps<{
data: TripTemplate
}>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const { connectionList } = useConnection()
const multipleSelection = ref<ConnectionType[]>([])
function fetchList() {
getTripConnections({ itinerary_id: props.data.id }).then(res => {
multipleSelection.value = res.data.items
})
}
watchEffect(() => fetchList())
function toggleSelection(data: ConnectionType) {
const foundIndex = multipleSelection.value.findIndex(item => item.id === data.id)
foundIndex === -1 ? multipleSelection.value.push(data) : multipleSelection.value.splice(foundIndex, 1)
}
function isActive(data: ConnectionType) {
return !!multipleSelection.value.find(item => item.id === data.id)
}
// 保存
function handleSave() {
const ids = multipleSelection.value.map(item => item.id)
bindTripConnections({ itinerary_id: props.data.id, connection_ids: ids }).then(() => {
ElMessage({ message: '保存成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog title="配置连接" width="800px" append-to-body @update:modelValue="$emit('update:modelValue')">
<div class="connection-list">
<div
class="connection-item"
v-for="item in connectionList"
:key="item.id"
:class="{ 'is-active': isActive(item) }"
@click="toggleSelection(item)">
<el-checkbox :model-value="isActive(item)" />
<div class="connection-item__icon"><ConnectionIcon :name="item.type + ''" /></div>
<p>{{ item.name }}</p>
</div>
</div>
<template #footer>
<el-row justify="center">
<el-button plain auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
<el-button type="primary" plain auto-insert-space @click="handleSave">保存</el-button>
</el-row>
</template>
</el-dialog>
</template>
<style lang="scss">
.connection-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.connection-item {
position: relative;
height: 100px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff;
border: 1px dashed #bbb;
cursor: pointer;
&.is-active {
background-color: rgb(204 247 131 / 38%);
}
.el-checkbox {
position: absolute;
left: 10px;
top: 0;
}
}
</style>
......@@ -32,7 +32,7 @@ watchEffect(() => {
const rules = ref<FormRules>({
name: [{ required: true, message: '请输入模板名称' }],
type: [{ required: true, message: '请选择模板类型' }],
type: [{ required: true, message: '请选择旅程类型' }],
score: [{ required: true, message: '请输入不超过100的整数数值' }]
})
......@@ -66,7 +66,7 @@ function handleUpdate() {
<el-form-item label="模板名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
</el-form-item>
<el-form-item label="模板类型" prop="type">
<el-form-item label="旅程类型" prop="type">
<el-select v-model="form.type" style="width: 100%">
<el-option v-for="item in tripTemplateTypeList" :key="item.value" v-bind="item"></el-option>
</el-select>
......
......@@ -13,7 +13,7 @@ const statusList = useMapStore().getMapValuesByKey('system_status')
<el-form ref="formRef" label-suffix=":">
<el-row justify="space-between">
<el-form-item label="模板名称" prop="name"> {{ data.name }} </el-form-item>
<el-form-item label="模板类型" prop="type">
<el-form-item label="旅程类型" prop="type">
{{ getNameByValue(data.type, tripTemplateTypeList) }}
</el-form-item>
<el-form-item label="旅程分值" prop="score"> {{ data.score }} </el-form-item>
......
......@@ -9,6 +9,7 @@ import { getNameByValue, tripTemplateTypeList } from '@/utils/dictionary'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const ViewDialog = defineAsyncComponent(() => import('../components/ViewDialog.vue'))
const BindConnection = defineAsyncComponent(() => import('../components/BindConnection.vue'))
const statusList = useMapStore().getMapValuesByKey('system_status')
......@@ -22,7 +23,7 @@ const listOptions = computed(() => {
},
filters: [
{ type: 'input', prop: 'name', placeholder: '请输入旅程模板名称' },
{ type: 'select', prop: 'type', placeholder: '请选择旅程模板类型', options: tripTemplateTypeList },
{ type: 'select', prop: 'type', placeholder: '请选择旅程类型', options: tripTemplateTypeList },
{ type: 'select', prop: 'status', placeholder: '请选择旅程模板状态', options: statusList }
],
columns: [
......@@ -30,7 +31,7 @@ const listOptions = computed(() => {
{ label: '模板名称', prop: 'name' },
{ label: '所属实验', prop: 'experiment.name' },
{
label: '模板类型',
label: '旅程类型',
prop: 'type',
computed({ row }: { row: TripTemplate }) {
return getNameByValue(row.type, tripTemplateTypeList)
......@@ -81,6 +82,13 @@ function handleView(row: TripTemplate) {
currentRow = row
viewVisible = true
}
// 配置
let configVisible = $ref(false)
function handleConfig(row: TripTemplate) {
currentRow = row
configVisible = true
}
</script>
<template>
......@@ -95,7 +103,7 @@ function handleView(row: TripTemplate) {
<el-tag :type="row.status === '1' ? 'success' : 'danger'">{{ getNameByValue(row.status, statusList) }}</el-tag>
</template>
<template #table-x="{ row }: { row: TripTemplate }">
<el-button type="primary" plain>配置</el-button>
<el-button type="primary" plain @click="handleConfig(row)">配置</el-button>
<el-button type="primary" plain @click="handleView(row)">查看</el-button>
<el-button type="primary" plain @click="handleUpdate(row)">编辑</el-button>
<el-button type="primary" plain @click="handleRemove(row)">删除</el-button>
......@@ -106,4 +114,6 @@ function handleView(row: TripTemplate) {
<FormDialog v-model="formVisible" :data="currentRow" @update="handleRefresh" v-if="formVisible"></FormDialog>
<!-- 查看 -->
<ViewDialog v-model="viewVisible" :data="currentRow" v-if="viewVisible && currentRow"></ViewDialog>
<!-- 配置 -->
<BindConnection v-model="configVisible" :data="currentRow" v-if="configVisible && currentRow"></BindConnection>
</template>
......@@ -104,3 +104,9 @@ export interface EventRuleItem {
value: string
}
}
// 标签规则
export interface TagRule {
current_logic_operate: 'and' | 'or'
items: string[]
}
......@@ -7,7 +7,7 @@ export function getNameByValue(value: string | number, list: Dictionary[]) {
return list.find(item => item.value === value)?.label || value
}
// 旅程模板类型
// 旅程类型
export const tripTemplateTypeList = [
{ label: '自由旅程', value: '1' },
{ label: '固定旅程', value: '2' }
......@@ -25,6 +25,13 @@ export const updateStatusRuleList = [
{ label: '手动更新', value: '2' }
]
// 更新状态
export const updateStatusList = [
{ label: '未开始', value: '1' },
{ label: '进行中', value: '2' },
{ label: '完成', value: '3' }
]
export const dateUnitList = [
{ label: '天', value: 1 },
{ label: '周', value: 2 },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论