Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
693da4ec6d
|
@ -19,6 +19,7 @@
|
|||
"ant-design-vue": "^3.2.15",
|
||||
"axios": "^1.2.1",
|
||||
"echarts": "^5.4.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"jetlinks-store": "^0.0.3",
|
||||
"js-cookie": "^3.0.1",
|
||||
"less": "^4.1.3",
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { BASE_API_PATH } from "@/utils/variable";
|
||||
import server from '@/utils/request'
|
||||
import { SearchHistoryList } from 'components/Search/types'
|
||||
|
||||
export const FILE_UPLOAD = `${BASE_API_PATH}/file/static`;
|
||||
|
||||
/**
|
||||
* 保存查询记录
|
||||
* @param data
|
||||
* @param target
|
||||
*/
|
||||
export const saveSearchHistory = (data: any, target:string) => server.post(`/user/settings/${target}`, data)
|
||||
|
||||
/**
|
||||
* 获取查询记录
|
||||
* @param target
|
||||
*/
|
||||
export const getSearchHistory = (target:string) => server.get<SearchHistoryList[]>(`/user/settings/${target}`)
|
||||
|
||||
/**
|
||||
* 删除指定查询记录
|
||||
* @param id
|
||||
* @param target
|
||||
*/
|
||||
export const deleteSearchHistory = (target:string, id:string) => server.remove<SearchHistoryList[]>(`/user/settings/${target}/${id}`)
|
|
@ -51,4 +51,25 @@ export const _deploy = (id: string) => server.post(`/device-instance/${id}/deplo
|
|||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export const _undeploy = (id: string) => server.post(`/device-instance/${id}/undeploy`)
|
||||
export const _undeploy = (id: string) => server.post(`/device-instance/${id}/undeploy`)
|
||||
|
||||
/**
|
||||
* 批量激活设备
|
||||
* @param data 设备id数组
|
||||
* @returns
|
||||
*/
|
||||
export const batchDeployDevice = (data: string[]) => server.put(`/device-instance/batch/_deploy`, data)
|
||||
|
||||
/**
|
||||
* 批量注销设备
|
||||
* @param data 设备id数组
|
||||
* @returns
|
||||
*/
|
||||
export const batchUndeployDevice = (data: string[]) => server.put(`/device-instance/batch/_unDeploy`, data)
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param data 设备id数组
|
||||
* @returns
|
||||
*/
|
||||
export const batchDeleteDevice = (data: string[]) => server.put(`/device-instance/batch/_delete`, data)
|
||||
|
|
|
@ -37,3 +37,9 @@ export const getCodecs = () => server.get<{id: string, name: string}>('/device/p
|
|||
* @returns
|
||||
*/
|
||||
export const detail = (id: string) => server.get<ProductItem>(`/device-product/${id}`)
|
||||
|
||||
/**
|
||||
* 产品分类
|
||||
* @param data
|
||||
*/
|
||||
export const category = (data: any) => server.post('/device/category/_tree', data)
|
|
@ -21,7 +21,11 @@ const iconKeys = [
|
|||
'StopOutlined',
|
||||
'CheckOutlined',
|
||||
'CloseOutlined',
|
||||
'DownOutlined'
|
||||
'DownOutlined',
|
||||
'ImportOutlined',
|
||||
'ExportOutlined',
|
||||
'SyncOutlined',
|
||||
'ExclamationCircleOutlined'
|
||||
]
|
||||
|
||||
const Icon = (props: {type: string}) => {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
v-model:value='formData.data[item.name]'
|
||||
:options='item.options'
|
||||
/>
|
||||
<a-inputnumber
|
||||
<a-input-number
|
||||
v-else-if='item.component === componentType.inputNumber'
|
||||
v-bind='item.componentProps'
|
||||
v-model:value='formData.data[item.name]'
|
||||
|
|
|
@ -32,6 +32,7 @@ self.MonacoEnvironment = {
|
|||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Number],
|
||||
theme: { type: String, default: 'vs-dark' },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
@ -48,7 +49,7 @@ onMounted(() => {
|
|||
tabSize: 2,
|
||||
automaticLayout: true,
|
||||
scrollBeyondLastLine: false,
|
||||
theme: 'vs-dark', // 主题色: vs(默认高亮), vs-dark(黑色), hc-black(高亮黑色)
|
||||
theme: props.theme, // 主题色: vs(默认高亮), vs-dark(黑色), hc-black(高亮黑色)
|
||||
});
|
||||
|
||||
instance.onDidChangeModelContent(() => {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<template>
|
||||
<a-dropdown-button
|
||||
type='primary'
|
||||
@click='click'
|
||||
placement='bottomLeft'
|
||||
:visible='historyVisible'
|
||||
@visibleChange='visibleChange'
|
||||
>
|
||||
搜索
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<template v-if='!showEmpty'>
|
||||
<a-menu-item v-for='item in historyList' :key='item.id'>
|
||||
<div class='history-item'>
|
||||
<span @click.stop='itemClick(item.content)'>{{ item.name }}</span>
|
||||
<a-popconfirm
|
||||
title='确认删除吗?'
|
||||
placement='top'
|
||||
@confirm.stop='deleteHistory(item.id)'
|
||||
:okButtonProps='{
|
||||
loading: deleteLoading
|
||||
}'
|
||||
>
|
||||
<span class='delete'>
|
||||
<DeleteOutlined />
|
||||
</span>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class='history-empty'>
|
||||
<a-empty />
|
||||
</div>
|
||||
</template>
|
||||
</a-menu>
|
||||
</template>
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='SearchHistory'>
|
||||
import { SearchOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||
import { deleteSearchHistory, getSearchHistory } from '@/api/comm'
|
||||
import type { SearchHistoryList } from 'components/Search/types'
|
||||
|
||||
type Emit = {
|
||||
(event: 'click'): void
|
||||
(event: 'itemClick', data: string): void
|
||||
}
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const props = defineProps({
|
||||
target: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const historyList = ref<SearchHistoryList[]>([])
|
||||
const historyVisible = ref(false)
|
||||
const deleteLoading = ref(false)
|
||||
const showEmpty = ref(false)
|
||||
|
||||
const visibleChange = async (visible: boolean) => {
|
||||
historyVisible.value = visible
|
||||
if (visible) {
|
||||
const resp = await getSearchHistory(props.target)
|
||||
if (resp.success && resp.result.length) {
|
||||
historyList.value = resp.result.filter(item => item.content)
|
||||
showEmpty.value = false
|
||||
} else {
|
||||
showEmpty.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const click = () => {
|
||||
emit('click')
|
||||
}
|
||||
|
||||
const itemClick = (content: string) => {
|
||||
historyVisible.value = false
|
||||
emit('itemClick', content)
|
||||
}
|
||||
|
||||
const deleteHistory = async (id: string) => {
|
||||
deleteLoading.value = true
|
||||
const resp = await deleteSearchHistory(props.target, id)
|
||||
deleteLoading.value = false
|
||||
if (resp.success) {
|
||||
historyVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.history-empty {
|
||||
width: 200px;
|
||||
background-color: #fff;
|
||||
box-shadow: @box-shadow-base;
|
||||
border-radius: 2px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
|
||||
> span {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.delete {
|
||||
padding: 0 6px;
|
||||
flex: 0 0 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -6,6 +6,7 @@
|
|||
:options='typeOptions'
|
||||
v-model:value='termsModel.type'
|
||||
style='width: 100%;'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<span v-else>
|
||||
{{
|
||||
|
@ -17,65 +18,86 @@
|
|||
class='JSearch-item--column'
|
||||
:options='columnOptions'
|
||||
v-model:value='termsModel.column'
|
||||
@change='columnChange'
|
||||
/>
|
||||
<a-select
|
||||
class='JSearch-item--termType'
|
||||
:options='termTypeOptions'
|
||||
:options='termTypeOptions.option'
|
||||
v-model:value='termsModel.termType'
|
||||
@change='termTypeChange'
|
||||
/>
|
||||
<div class='JSearch-item--value'>
|
||||
<a-input
|
||||
v-if='component === componentType.input'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-select
|
||||
v-else-if='component === componentType.select'
|
||||
showSearch
|
||||
:loading='optionLoading'
|
||||
v-model:value='termsModel.value'
|
||||
:options='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
:filterOption='(v, option) => filterTreeSelectNode(v, option, "label")'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-inputnumber
|
||||
<a-input-number
|
||||
v-else-if='component === componentType.inputNumber'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-input-password
|
||||
v-else-if='component === componentType.password'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-switch
|
||||
v-else-if='component === componentType.switch'
|
||||
v-model:checked='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-radio-group
|
||||
v-else-if='component === componentType.radio'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-checkbox-group
|
||||
v-else-if='component === componentType.checkbox'
|
||||
v-model:value='termsModel.value'
|
||||
:options='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-time-picker
|
||||
v-else-if='component === componentType.time'
|
||||
valueFormat='HH:mm:ss'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-date-picker
|
||||
v-else-if='component === componentType.date'
|
||||
showTime
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
valueFormat='YYYY-MM-DD HH:mm:ss'
|
||||
style='width: 100%'
|
||||
@change='valueChange'
|
||||
/>
|
||||
<a-tree-select
|
||||
v-else-if='component === componentType.tree'
|
||||
v-else-if='component === componentType.treeSelect'
|
||||
showSearch
|
||||
v-model:value='termsModel.value'
|
||||
:tree-data='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
style='width: 100%'
|
||||
:fieldNames='{ label: "name", value: "id" }'
|
||||
@change='valueChange'
|
||||
:filterTreeNode='(v, option) => filterSelectNode(v, option)'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,18 +107,19 @@
|
|||
import { componentType } from 'components/Form'
|
||||
import { typeOptions, termType } from './util'
|
||||
import { PropType } from 'vue'
|
||||
import type { SearchItemProps, SearchItemData } from './types'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import type { SearchItemData, SearchProps, Terms } from './types'
|
||||
import { cloneDeep, get, isArray, isFunction } from 'lodash-es'
|
||||
import { filterTreeSelectNode, filterSelectNode } from '@/utils/comm'
|
||||
|
||||
type ItemDataProps = Omit<SearchItemData, 'title'>
|
||||
type ItemType = SearchProps['type']
|
||||
|
||||
interface Emit {
|
||||
(e: 'change', data: ItemDataProps): void
|
||||
(e: 'change', data: SearchItemData): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
columns: {
|
||||
type: Array as PropType<SearchItemProps[]>,
|
||||
type: Array as PropType<SearchProps[]>,
|
||||
default: () => [],
|
||||
required: true
|
||||
},
|
||||
|
@ -107,66 +130,146 @@ const props = defineProps({
|
|||
expand: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
termsItem: {
|
||||
type: Object as PropType<Terms>,
|
||||
default: {}
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const termsModel = reactive<ItemDataProps>({
|
||||
const termsModel = reactive<SearchItemData>({
|
||||
type: 'or',
|
||||
value: '',
|
||||
termType: 'eq',
|
||||
termType: 'like',
|
||||
column: ''
|
||||
})
|
||||
|
||||
const component = ref(componentType.input)
|
||||
|
||||
const options = ref([])
|
||||
const options = ref<any[]>([])
|
||||
|
||||
const columnOptions = ref<({ label: string, value: string})[]>([])
|
||||
const columnOptionMap = new Map()
|
||||
|
||||
const termTypeOptions = reactive(termType)
|
||||
const termTypeOptions = reactive({option: termType})
|
||||
|
||||
const getTermType = (type: string) => {
|
||||
const optionLoading = ref(false)
|
||||
|
||||
/**
|
||||
* 根据类型切换默termType值
|
||||
* @param type
|
||||
*/
|
||||
const getTermType = (type?: ItemType) => {
|
||||
termTypeOptions.option = termType
|
||||
switch (type) {
|
||||
case 'select':
|
||||
case 'treeSelect':
|
||||
case 'number':
|
||||
return 'eq'
|
||||
case 'date':
|
||||
case 'time':
|
||||
// 时间只有大于或小于两个值
|
||||
termTypeOptions.option = termType.filter(item => ['gt','lt'].includes(item.value))
|
||||
return 'gt'
|
||||
default:
|
||||
return 'like'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型返回组件
|
||||
* @param type
|
||||
*/
|
||||
const getComponent = (type?: ItemType) => {
|
||||
switch (type) {
|
||||
case 'select':
|
||||
component.value = componentType.select
|
||||
break;
|
||||
case 'treeSelect':
|
||||
component.value = componentType.treeSelect
|
||||
break;
|
||||
case 'date':
|
||||
component.value = componentType.date
|
||||
break;
|
||||
case 'time':
|
||||
component.value = componentType.time
|
||||
break;
|
||||
case 'number':
|
||||
component.value = componentType.inputNumber
|
||||
break;
|
||||
default:
|
||||
component.value = componentType.input
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleItemOptions = (option?: any[] | Function) => {
|
||||
options.value = []
|
||||
if (isArray(option)) {
|
||||
options.value = option
|
||||
} else if (isFunction(option)) {
|
||||
optionLoading.value = true
|
||||
option().then((res: any[]) => {
|
||||
optionLoading.value = false
|
||||
options.value = res
|
||||
}).catch((_: any) => {
|
||||
optionLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const columnChange = (value: string, isChange: boolean) => {
|
||||
const item = columnOptionMap.get(value)
|
||||
optionLoading.value = false
|
||||
// 设置value为undefined
|
||||
termsModel.column = value
|
||||
termsModel.termType = item.defaultTermType || getTermType(item.type)
|
||||
|
||||
getComponent(item.type) // 处理Item的组件类型
|
||||
|
||||
// 处理options 以及 request
|
||||
if ('options' in item) {
|
||||
handleItemOptions(item.options)
|
||||
}
|
||||
|
||||
termsModel.value = undefined
|
||||
|
||||
if (isChange) {
|
||||
valueChange()
|
||||
}
|
||||
}
|
||||
|
||||
const handleItem = () => {
|
||||
columnOptionMap.clear()
|
||||
columnOptions.value = []
|
||||
if (!props.columns.length) return
|
||||
|
||||
// 获取第一个值
|
||||
|
||||
const sortColumn = cloneDeep(props.columns)
|
||||
sortColumn?.sort((a, b) => a.sortIndex! - b.sortIndex!)
|
||||
|
||||
const _index = props.index > sortColumn.length ? sortColumn.length - 1 : props.index
|
||||
const _itemColumn = sortColumn[_index - 1]
|
||||
|
||||
termsModel.column = _itemColumn.column
|
||||
termsModel.termType = _itemColumn.defaultTermType || getTermType(_itemColumn.type as string)
|
||||
|
||||
columnOptions.value = props.columns.map(item => {
|
||||
columnOptions.value = props.columns.map(item => { // 对columns进行Map处理以及值处理
|
||||
columnOptionMap.set(item.column, item)
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.column
|
||||
}
|
||||
})
|
||||
|
||||
// 获取第一个值
|
||||
const sortColumn = cloneDeep(props.columns)
|
||||
sortColumn?.sort((a, b) => a.sortIndex! - b.sortIndex!)
|
||||
|
||||
const _index = props.index > sortColumn.length ? sortColumn.length - 1 : props.index
|
||||
const _itemColumn = sortColumn[_index - 1]
|
||||
|
||||
columnChange(_itemColumn.column, false)
|
||||
}
|
||||
|
||||
const valueChange = (value: any) => {
|
||||
const termTypeChange = () => {
|
||||
valueChange()
|
||||
}
|
||||
|
||||
const valueChange = () => {
|
||||
|
||||
emit('change', {
|
||||
type: termsModel.type,
|
||||
value: termsModel.value,
|
||||
|
@ -177,6 +280,27 @@ const valueChange = (value: any) => {
|
|||
|
||||
handleItem()
|
||||
|
||||
watch( props.termsItem, (newValue) => {
|
||||
|
||||
const path = props.index < 4 ? [0, 'terms', props.index - 1] : [1, 'terms', props.index - 4]
|
||||
const itemData: SearchItemData = get(newValue.terms, path)
|
||||
if (itemData) {
|
||||
termsModel.type = itemData.type
|
||||
termsModel.column = itemData.column
|
||||
termsModel.termType = itemData.termType
|
||||
termsModel.value = itemData.value
|
||||
const item = columnOptionMap.get(itemData.column)
|
||||
getComponent(item.type) // 处理Item的组件类型
|
||||
|
||||
// 处理options 以及 request
|
||||
if ('options' in item) {
|
||||
handleItemOptions(item.options)
|
||||
}
|
||||
} else {
|
||||
handleItem()
|
||||
}
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<a-popover
|
||||
title='搜索名称'
|
||||
trigger='click'
|
||||
v-model:visible='visible'
|
||||
@visibleChange='visibleChange'
|
||||
>
|
||||
<template #content>
|
||||
<div style='width: 240px'>
|
||||
<a-form ref='formRef' :model='modelRef'>
|
||||
<a-form-item
|
||||
name='name'
|
||||
:rules='[
|
||||
{ required: true, message: "请输入名称"}
|
||||
]'
|
||||
>
|
||||
<a-textarea
|
||||
v-model:value='modelRef.name'
|
||||
:rows='3'
|
||||
:maxlength='200'
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-button
|
||||
:loading='saveHistoryLoading'
|
||||
type='primary'
|
||||
class='save-btn'
|
||||
@click='saveHistory'
|
||||
>
|
||||
保存
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-button>
|
||||
<template #icon>
|
||||
<SaveOutlined />
|
||||
</template>
|
||||
保存
|
||||
</a-button>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='SaveHistory'>
|
||||
import type { Terms } from './types'
|
||||
import { PropType } from 'vue'
|
||||
import { saveSearchHistory } from '@/api/comm'
|
||||
import { SaveOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const props = defineProps({
|
||||
terms: {
|
||||
type: Object as PropType<Terms>,
|
||||
default: () => ({})
|
||||
},
|
||||
target: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const searchName = ref('')
|
||||
|
||||
const saveHistoryLoading = ref(false)
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
const modelRef = reactive({
|
||||
name: undefined
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存当前查询条件
|
||||
*/
|
||||
const saveHistory = async () => {
|
||||
// 获取当前查询条件并转化为字符串
|
||||
const formData = await formRef.value.validate()
|
||||
if (formData) {
|
||||
formData.content = JSON.stringify(props.terms)
|
||||
saveHistoryLoading.value = true
|
||||
const resp = await saveSearchHistory(formData, props.target)
|
||||
saveHistoryLoading.value = false
|
||||
if (resp.success) {
|
||||
visible.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const visibleChange = (e: boolean) => {
|
||||
visible.value = e
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.save-btn {
|
||||
width: 100%
|
||||
}
|
||||
</style>
|
|
@ -4,9 +4,9 @@
|
|||
<div v-if='props.type === "advanced"' :class='["JSearch-content senior", expand ? "senior-expand" : "", screenSize ? "big" : "small"]'>
|
||||
<div :class='["JSearch-items", expand ? "items-expand" : "", layout]'>
|
||||
<div class='left'>
|
||||
<SearchItem :expand='expand' :index='1' :columns='searchItems' />
|
||||
<SearchItem v-if='expand' :expand='expand' :index='2' :columns='searchItems' />
|
||||
<SearchItem v-if='expand' :expand='expand' :index='3' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='1' :columns='searchItems' @change='(v) => itemValueChange(v, 1)' :termsItem='terms'/>
|
||||
<SearchItem v-if='expand' :expand='expand' :index='2' :columns='searchItems' @change='(v) => itemValueChange(v, 2)' :termsItem='terms'/>
|
||||
<SearchItem v-if='expand' :expand='expand' :index='3' :columns='searchItems' @change='(v) => itemValueChange(v, 3)' :termsItem='terms'/>
|
||||
</div>
|
||||
<div class='center' v-if='expand'>
|
||||
<a-select
|
||||
|
@ -16,31 +16,17 @@
|
|||
/>
|
||||
</div>
|
||||
<div class='right' v-if='expand'>
|
||||
<SearchItem :expand='expand' :index='4' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='5' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='6' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='4' :columns='searchItems' @change='(v) => itemValueChange(v, 4)' :termsItem='terms'/>
|
||||
<SearchItem :expand='expand' :index='5' :columns='searchItems' @change='(v) => itemValueChange(v, 5)' :termsItem='terms'/>
|
||||
<SearchItem :expand='expand' :index='6' :columns='searchItems' @change='(v) => itemValueChange(v, 6)' :termsItem='terms'/>
|
||||
</div>
|
||||
</div>
|
||||
<div :class='["JSearch-footer", expand ? "expand" : ""]'>
|
||||
<div class='JSearch-footer--btns'>
|
||||
<a-dropdown-button type="primary">
|
||||
搜索
|
||||
<template #overlay>
|
||||
<a-menu v-if='!!historyList.length'>
|
||||
<a-menu-item>
|
||||
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-empty v-else />
|
||||
</template>
|
||||
<template #icon><SearchOutlined /></template>
|
||||
</a-dropdown-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
保存
|
||||
</a-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
<History :target='target' @click='searchSubmit' @itemClick='historyItemClick' />
|
||||
<SaveHistory :terms='terms' :target='target'/>
|
||||
<a-button @click='reset'>
|
||||
<template #icon><RedoOutlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -54,17 +40,17 @@
|
|||
<div v-else class='JSearch-content simple big'>
|
||||
<div class='JSearch-items'>
|
||||
<div class='left'>
|
||||
<SearchItem :expand='false' :index='1' />
|
||||
<SearchItem :expand='false' :index='1' :columns='searchItems' @change='(v) => itemValueChange(v, 1)' :termsItem='terms'/>
|
||||
</div>
|
||||
</div>
|
||||
<div class='JSearch-footer'>
|
||||
<div class='JSearch-footer--btns'>
|
||||
<a-button type="primary">
|
||||
<a-button type="primary" @click='searchSubmit'>
|
||||
<template #icon><SearchOutlined /></template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
<a-button @click='reset'>
|
||||
<template #icon><RedoOutlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -76,18 +62,25 @@
|
|||
<script setup lang='ts' name='Search'>
|
||||
import SearchItem from './Item.vue'
|
||||
import { typeOptions } from './util'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { omit } from 'lodash-es'
|
||||
import { SearchOutlined, DownOutlined } from '@ant-design/icons-vue';
|
||||
import type { SearchItemProps } from './types'
|
||||
import { useElementSize, useUrlSearchParams } from '@vueuse/core'
|
||||
import { cloneDeep, isFunction, isString, set } from 'lodash-es'
|
||||
import { SearchOutlined, DownOutlined, RedoOutlined } from '@ant-design/icons-vue';
|
||||
import { PropType } from 'vue'
|
||||
import { JColumnsProps } from 'components/Table/types'
|
||||
import SaveHistory from './SaveHistory.vue'
|
||||
import History from './History.vue'
|
||||
import type { SearchItemData, SearchProps, Terms } from './types'
|
||||
|
||||
type UrlParam = {
|
||||
q: string | null
|
||||
target: string | null
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'search', data: Terms): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
defaultParams: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
columns: {
|
||||
type: Array as PropType<JColumnsProps[]>,
|
||||
default: () => [],
|
||||
|
@ -97,7 +90,7 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: 'advanced'
|
||||
},
|
||||
key: {
|
||||
target: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
|
@ -107,11 +100,13 @@ const props = defineProps({
|
|||
const searchRef = ref(null)
|
||||
const { width } = useElementSize(searchRef)
|
||||
|
||||
const urlParams = useUrlSearchParams<UrlParam>('hash')
|
||||
|
||||
// 是否展开更多筛选
|
||||
const expand = ref(false)
|
||||
|
||||
// 第一组,第二组的关系
|
||||
const termType = ref('or')
|
||||
const termType = ref('and')
|
||||
// 搜索历史记录
|
||||
const historyList = ref([])
|
||||
|
||||
|
@ -120,7 +115,13 @@ const layout = ref('horizontal')
|
|||
// 当前组件宽度 true 大于1000
|
||||
const screenSize = ref(true)
|
||||
|
||||
const searchItems = ref<SearchItemProps[]>([])
|
||||
const searchItems = ref<SearchProps[]>([])
|
||||
// 当前查询条件
|
||||
const terms = reactive<Terms>({ terms: [] })
|
||||
|
||||
const columnOptionMap = new Map()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const expandChange = () => {
|
||||
expand.value = !expand.value
|
||||
|
@ -132,10 +133,12 @@ const searchParams = reactive({
|
|||
|
||||
const handleItems = () => {
|
||||
searchItems.value = []
|
||||
columnOptionMap.clear()
|
||||
props.columns!.forEach((item, index) => {
|
||||
if (item.search && Object.keys(item.search).length) {
|
||||
columnOptionMap.set(item.dataIndex, item.search)
|
||||
searchItems.value.push({
|
||||
...omit(item.search, ['rename']),
|
||||
...item.search,
|
||||
sortIndex: item.search.first ? 0 : index + 1,
|
||||
title: item.title,
|
||||
column: item.dataIndex,
|
||||
|
@ -144,6 +147,92 @@ const handleItems = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const itemValueChange = (value: SearchItemData, index: number) => {
|
||||
if (index < 4) { // 第一组数据
|
||||
set(terms.terms, [0, 'terms', index - 1], value)
|
||||
} else { // 第二组数据
|
||||
set(terms.terms, [1, 'terms', index - 4], value)
|
||||
}
|
||||
}
|
||||
|
||||
const addUrlParams = () => {
|
||||
urlParams.q = JSON.stringify(terms)
|
||||
urlParams.target = props.target
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理termType为like,nlike的值
|
||||
* @param v
|
||||
*/
|
||||
const handleLikeValue = (v: string) => {
|
||||
if (isString(v)) {
|
||||
return v.split('').reduce((pre: string, next: string) => {
|
||||
let _next = next
|
||||
if (next === '\\') {
|
||||
_next = '\\\\'
|
||||
} else if (next === '%') {
|
||||
_next = '\\%'
|
||||
}
|
||||
return pre + _next
|
||||
}, '')
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理为外部使用
|
||||
*/
|
||||
const handleParamsFormat = () => {
|
||||
// 过滤掉terms中value无效的item
|
||||
const cloneParams = cloneDeep(terms)
|
||||
return {
|
||||
terms: cloneParams.terms.map(item => {
|
||||
if (item.terms) {
|
||||
item.terms = item.terms.filter(iItem => iItem && iItem.value)
|
||||
.map(iItem => {
|
||||
// 处理handleValue和rename
|
||||
const _item = columnOptionMap.get(iItem.column)
|
||||
if (_item.rename) {
|
||||
iItem.column = _item.rename
|
||||
}
|
||||
|
||||
if (_item.handleValue && isFunction(_item.handleValue)) {
|
||||
iItem.value = _item.handleValue(iItem.value)
|
||||
}
|
||||
|
||||
if (['like','nlike'].includes(iItem.termType) && !!iItem.value) {
|
||||
iItem.value = `%${handleLikeValue(iItem.value)}%`
|
||||
}
|
||||
return iItem
|
||||
})
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
const searchSubmit = () => {
|
||||
emit('search', handleParamsFormat())
|
||||
if (props.type === 'advanced') {
|
||||
addUrlParams()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置查询
|
||||
*/
|
||||
const reset = () => {
|
||||
terms.terms = []
|
||||
expand.value = false
|
||||
if (props.type === 'advanced') {
|
||||
urlParams.q = null
|
||||
urlParams.target = null
|
||||
}
|
||||
}
|
||||
|
||||
watch(width, (value) => {
|
||||
if (value < 1000) {
|
||||
layout.value = 'vertical'
|
||||
|
@ -154,6 +243,41 @@ watch(width, (value) => {
|
|||
}
|
||||
})
|
||||
|
||||
const historyItemClick = (content: string) => {
|
||||
try {
|
||||
terms.terms = JSON.parse(content)?.terms || []
|
||||
if (terms.terms.length === 2) {
|
||||
expand.value = true
|
||||
}
|
||||
addUrlParams()
|
||||
} catch (e) {
|
||||
console.warn(`Search组件中handleUrlParams处理JSON时异常:【${e}】`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理URL中的查询参数
|
||||
* @param _params
|
||||
*/
|
||||
const handleUrlParams = (_params: UrlParam) => {
|
||||
// URL中的target和props的一致,则还原查询参数
|
||||
if (_params.target === props.target && _params.q) {
|
||||
try {
|
||||
terms.terms = JSON.parse(_params.q)?.terms || []
|
||||
if (terms.terms.length === 2) {
|
||||
expand.value = true
|
||||
}
|
||||
emit('search', handleParamsFormat())
|
||||
} catch (e) {
|
||||
console.warn(`Search组件中handleUrlParams处理JSON时异常:【${e}】`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
handleUrlParams(urlParams)
|
||||
})
|
||||
|
||||
handleItems()
|
||||
|
||||
</script>
|
||||
|
@ -253,4 +377,5 @@ handleItems()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,107 @@
|
|||
# Search组件
|
||||
|
||||
- 需要结合Table使用
|
||||
|
||||
## 属性
|
||||
|
||||
| 名称 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| columns | 查询下拉列表 | JColumnsProps[] | [] |
|
||||
| type | 查询模式 | 'advanced', 'simple' | 'advanced' |
|
||||
| target | 查询组件唯一key | String | |
|
||||
| search | 查询回调事件 | Function | |
|
||||
|
||||
> JColumnsProps[*].search
|
||||
|
||||
| 名称 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| rename | 用来重命名查询字段值 | String | |
|
||||
| type | 查询值组件类型 | 'select', 'number', 'string', 'treeSelect', 'date', 'time' | |
|
||||
| options | Select和TreeSelect组件下拉值 | Array, Promise | |
|
||||
| first | 控制查询字段下拉默认值,默认为name即名称 | Boolean | |
|
||||
| defaultTermType | 查询条件 | String | |
|
||||
| handleValue | 处理单个查询value值 | Function | |
|
||||
|
||||
## 基础用法
|
||||
|
||||
> columns中包含search属性才会出现在查询下拉中
|
||||
|
||||
```vue
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
}
|
||||
}
|
||||
]
|
||||
const search = (params) => {
|
||||
}
|
||||
<Search
|
||||
:columns='columns'
|
||||
target='device'
|
||||
@search='search'
|
||||
/>
|
||||
```
|
||||
|
||||
> rename的作用在于search抛出params会根据rename修改数据中column的值
|
||||
|
||||
```vue
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
rename: 'TestName'
|
||||
}
|
||||
}
|
||||
]
|
||||
const search = (params) => {
|
||||
terms: [
|
||||
{
|
||||
column: 'TestName',
|
||||
value: '',
|
||||
termType: 'like'
|
||||
}
|
||||
]
|
||||
}
|
||||
<Search
|
||||
:columns='columns'
|
||||
target='device'
|
||||
@search='search'
|
||||
/>
|
||||
```
|
||||
|
||||
> defaultTermType的作用在于设置查询条件,相关条件参考util中的termType
|
||||
|
||||
```vue
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
defaultTermType: 'gt'
|
||||
}
|
||||
}
|
||||
]
|
||||
const search = (params) => {
|
||||
terms: [
|
||||
{
|
||||
column: 'TestName',
|
||||
value: '',
|
||||
termType: 'gt'
|
||||
}
|
||||
]
|
||||
}
|
||||
<Search
|
||||
:columns='columns'
|
||||
target='device'
|
||||
@search='search'
|
||||
/>
|
||||
```
|
|
@ -1,4 +1,4 @@
|
|||
export interface SearchProps {
|
||||
export interface SearchBaseProps {
|
||||
rename?: string
|
||||
type?: 'select' | 'number' | 'string' | 'treeSelect' | 'date' | 'time'
|
||||
format?: string
|
||||
|
@ -7,15 +7,43 @@ export interface SearchProps {
|
|||
defaultTermType?: string // 默认 eq
|
||||
title?: ColumnType.title
|
||||
sortIndex?: number
|
||||
handleValue?: (value: SearchItemData) => any
|
||||
}
|
||||
|
||||
export interface SearchItemProps {
|
||||
rename?: SearchBaseProps['rename']
|
||||
title: string
|
||||
column: ColumnType.dataIndex
|
||||
}
|
||||
|
||||
export interface SearchItemData {
|
||||
column: ColumnType.dataIndex
|
||||
rename?: string
|
||||
value: any
|
||||
termType: string
|
||||
type?: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface SearchItemProps extends SearchProps, SearchItemData {}
|
||||
export interface TermsItem {
|
||||
terms: SearchItemData[]
|
||||
}
|
||||
|
||||
export interface Terms {
|
||||
terms: TermsItem[]
|
||||
}
|
||||
|
||||
export interface SortItem {
|
||||
name: string
|
||||
order?: 'desc' | 'asc'
|
||||
value?: any
|
||||
}
|
||||
|
||||
export interface SearchHistoryList {
|
||||
content?: string
|
||||
name: string
|
||||
id: string
|
||||
key: string
|
||||
}
|
||||
|
||||
export interface SearchProps extends SearchBaseProps, SearchItemProps {
|
||||
|
||||
}
|
||||
|
|
|
@ -204,9 +204,13 @@ const JTable = defineComponent<JTableProps>({
|
|||
loading.value = false
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
handleSearch(props.params)
|
||||
})
|
||||
watch(
|
||||
() => props.params,
|
||||
(newValue) => {
|
||||
handleSearch(newValue)
|
||||
},
|
||||
{deep: true, immediate: true}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
window.onresize = () => {
|
||||
|
@ -266,7 +270,7 @@ const JTable = defineComponent<JTableProps>({
|
|||
onClose={() => {
|
||||
emit('cancelSelect')
|
||||
}}
|
||||
closeText={<a>取消选择</a>}
|
||||
closeText={<a-button type="link">取消选择</a-button>}
|
||||
/>
|
||||
</div> : null
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { SearchItemProps } from 'components/Search/types'
|
||||
import { SearchProps } from 'components/Search/types'
|
||||
import { ColumnType } from 'ant-design-vue/es/table'
|
||||
|
||||
export interface JColumnsProps extends ColumnType{
|
||||
scopedSlots?: boolean;
|
||||
search: SearchItemProps
|
||||
search: SearchProps
|
||||
}
|
|
@ -45,7 +45,7 @@
|
|||
<template #addonAfter>
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="action"
|
||||
:action="FILE_UPLOAD"
|
||||
:headers="headers"
|
||||
:showUploadList="false"
|
||||
@change="handleFileChange"
|
||||
|
@ -89,6 +89,7 @@ import GeoComponent from '@/components/GeoComponent/index.vue';
|
|||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { ItemData, ITypes } from './types';
|
||||
import { FILE_UPLOAD } from '@/api/comm';
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:modelValue', data: string | number | boolean): void;
|
||||
|
@ -161,7 +162,6 @@ const handleItemModalSubmit = () => {
|
|||
};
|
||||
|
||||
// 文件上传
|
||||
const action = ref<string>(`${BASE_API_PATH}/file/static`);
|
||||
const headers = ref({ [TOKEN_KEY]: LocalStore.get(TOKEN_KEY) });
|
||||
const handleFileChange = (info: UploadChangeParam<UploadFile<any>>) => {
|
||||
if (info.file.status === 'done') {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { TOKEN_KEY } from '@/utils/variable'
|
||||
import { Terms } from 'components/Search/types'
|
||||
|
||||
/**
|
||||
* 静态图片资源处理
|
||||
|
@ -36,3 +37,23 @@ export const LocalStore = {
|
|||
export const getToken = () => {
|
||||
return LocalStore.get(TOKEN_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* TreeSelect过滤
|
||||
* @param value 过滤值
|
||||
* @param treeNode
|
||||
* @param key
|
||||
*/
|
||||
export const filterTreeSelectNode = (value: string, treeNode: any, key: string = 'name'): boolean => {
|
||||
return treeNode[key]?.includes(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select过滤
|
||||
* @param value 过滤值
|
||||
* @param option
|
||||
* @param key
|
||||
*/
|
||||
export const filterSelectNode = (value: string, option: any, key: string = 'label'): boolean => {
|
||||
return option[key]?.includes(value)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
export default function encodeQuery(params: any) {
|
||||
if (!params) return {};
|
||||
const queryParam = {
|
||||
// pageIndex: 0,
|
||||
current: params.current,
|
||||
};
|
||||
const { terms, sorts } = params;
|
||||
Object.keys(params).forEach((key: string) => {
|
||||
if (key === 'terms') {
|
||||
let index = 0;
|
||||
if (!terms) return;
|
||||
Object.keys(terms).forEach((k: string) => {
|
||||
if (
|
||||
!(
|
||||
terms[k] === '' ||
|
||||
terms[k] === undefined ||
|
||||
terms[k].length === 0 ||
|
||||
terms[k] === {} ||
|
||||
terms[k] === null
|
||||
)
|
||||
) {
|
||||
if (k.indexOf('$LIKE') > -1 && terms[k].toString().indexOf('%') === -1) {
|
||||
terms[k] = `%${terms[k]}%`;
|
||||
}
|
||||
if (k.indexOf('$IN') > -1) {
|
||||
terms[k] = terms[k].toString();
|
||||
} else if (k.indexOf('$START') > -1) {
|
||||
terms[k] = `%${terms[k]}`;
|
||||
} else if (k.indexOf('$END') > -1) {
|
||||
terms[k] = `${terms[k]}%`;
|
||||
}
|
||||
if (k.indexOf('@') > -1) {
|
||||
const temp = k.split('@');
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
queryParam[`terms[${index}].column`] = temp[0];
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
queryParam[`terms[${index}].type`] = temp[1];
|
||||
} else {
|
||||
queryParam[`terms[${index}].column`] = k;
|
||||
}
|
||||
queryParam[`terms[${index}].value`] = terms[k];
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
} else if (key === 'sorts') {
|
||||
// 当前Ant Design排序只支持单字段排序
|
||||
if (!sorts) return;
|
||||
Object.keys(sorts).forEach((s, index) => {
|
||||
queryParam[`sorts[${index}].name`] = s;
|
||||
queryParam[`sorts[${index}].order`] = sorts[s].replace('end', '');
|
||||
});
|
||||
// if (Object.keys(sorts).length > 0) {
|
||||
// queryParam[`sorts[0].name`] = sorts.field;
|
||||
// queryParam[`sorts[0].order`] = (sorts.order || '').replace('end', '');
|
||||
// }
|
||||
} else {
|
||||
queryParam[key] = params[key];
|
||||
}
|
||||
});
|
||||
|
||||
// queryParam.pageIndex = current - 1;
|
||||
|
||||
return queryParam;
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
import moment from "moment";
|
||||
import { LocalStore } from "./comm";
|
||||
import { TOKEN_KEY } from "./variable";
|
||||
|
||||
/**
|
||||
* 把数据下载成JSON
|
||||
* @param record
|
||||
|
@ -19,3 +23,33 @@ export const downloadObject = (record: Record<string, any>, fileName: string, fo
|
|||
//移除
|
||||
document.body.removeChild(ghostLink);
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param url 下载链接
|
||||
* @param params 参数
|
||||
*/
|
||||
export const downloadFile = (url: string, params?: Record<string, any>) => {
|
||||
const formElement = document.createElement('form');
|
||||
formElement.style.display = 'display:none;';
|
||||
formElement.method = 'GET';
|
||||
formElement.action = url;
|
||||
// 添加参数
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key: string) => {
|
||||
const inputElement = document.createElement('input');
|
||||
inputElement.type = 'hidden';
|
||||
inputElement.name = key;
|
||||
inputElement.value = params[key];
|
||||
formElement.appendChild(inputElement);
|
||||
});
|
||||
}
|
||||
const inputElement = document.createElement('input');
|
||||
inputElement.type = 'hidden';
|
||||
inputElement.name = ':X_Access_Token';
|
||||
inputElement.value = LocalStore.get(TOKEN_KEY);
|
||||
formElement.appendChild(inputElement);
|
||||
document.body.appendChild(formElement);
|
||||
formElement.submit();
|
||||
document.body.removeChild(formElement);
|
||||
};
|
|
@ -2,13 +2,22 @@
|
|||
<div class='search'>
|
||||
<Search
|
||||
:columns='columns'
|
||||
target='device-instance-search'
|
||||
@search='search'
|
||||
/>
|
||||
<Search
|
||||
type='simple'
|
||||
:columns='columns'
|
||||
target='product'
|
||||
@search='search'
|
||||
/>
|
||||
<Search type='simple' />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name='demoSearch'>
|
||||
|
||||
import { category } from '../../api/device/product'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
|
@ -17,20 +26,59 @@ const columns = [
|
|||
search: {
|
||||
rename: 'deviceId',
|
||||
type: 'select',
|
||||
handValue: (v) => {
|
||||
options: [
|
||||
{
|
||||
label: '测试1',
|
||||
value: 'test1'
|
||||
},
|
||||
{
|
||||
label: '测试2',
|
||||
value: 'test2'
|
||||
},
|
||||
{
|
||||
label: '测试3',
|
||||
value: 'test3'
|
||||
},
|
||||
],
|
||||
handleValue: (v) => {
|
||||
return '123'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'sortIndex',
|
||||
key: 'sortIndex',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'number',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'date',
|
||||
key: 'date',
|
||||
search: {
|
||||
type: 'date',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '时间2',
|
||||
dataIndex: 'date2',
|
||||
key: 'date2',
|
||||
search: {
|
||||
type: 'time',
|
||||
defaultTermType: 'lt'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'classifiedName',
|
||||
|
@ -38,9 +86,13 @@ const columns = [
|
|||
search: {
|
||||
first: true,
|
||||
type: 'treeSelect',
|
||||
// options: async () => {
|
||||
// return await
|
||||
// }
|
||||
options: async () => {
|
||||
return new Promise((res) => {
|
||||
category().then(resp => {
|
||||
res(resp.result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -51,6 +103,9 @@ const columns = [
|
|||
scopedSlots: true,
|
||||
}
|
||||
]
|
||||
const search = (params) => {
|
||||
console.log(params)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="导出" @ok="handleOk" @cancel="handleCancel">
|
||||
<div style="background-color: rgb(236, 237, 238)">
|
||||
<p style="padding: 10px">
|
||||
<AIcon type="ExclamationCircleOutlined" />
|
||||
选择单个产品时可导出其下属设备的详细数据,不选择产品时导出所有设备的基础数据。
|
||||
</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px">
|
||||
<a-form :layout="'vertical'">
|
||||
<a-form-item label="产品">
|
||||
<a-select showSearch v-model:value="modelRef.product" placeholder="请选择产品">
|
||||
<a-select-option :value="item.id" v-for="item in productList" :key="item.id" :title="item.name"></a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件格式">
|
||||
<a-radio-group button-style="solid" v-model:value="modelRef.fileType" placeholder="请选择文件格式">
|
||||
<a-radio-button value="xlsx">xlsx</a-radio-button>
|
||||
<a-radio-button value="csv">csv</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { queryNoPagingPost } from '@/api/device/product'
|
||||
import { downloadFile } from '@/utils/utils'
|
||||
import encodeQuery from '@/utils/encodeQuery'
|
||||
import { BASE_API_PATH } from '@/utils/variable'
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
})
|
||||
const modelRef = reactive({
|
||||
product: undefined,
|
||||
fileType: 'xlsx'
|
||||
});
|
||||
|
||||
const productList = ref<Record<string, any>[]>([])
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
queryNoPagingPost({paging: false}).then(resp => {
|
||||
if(resp.status === 200){
|
||||
productList.value = resp.result as Record<string, any>[]
|
||||
}
|
||||
})
|
||||
},
|
||||
{immediate: true, deep: true}
|
||||
)
|
||||
|
||||
const handleOk = () => {
|
||||
const params = encodeQuery(props.data);
|
||||
if(modelRef.product){
|
||||
downloadFile(
|
||||
`${BASE_API_PATH}/device/instance/${modelRef.product}/export.${modelRef.fileType}`,
|
||||
params
|
||||
);
|
||||
} else {
|
||||
downloadFile(`${BASE_API_PATH}/device/instance/export.${modelRef.fileType}`, params);
|
||||
}
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="导入" @ok="handleOk" @cancel="handleCancel">
|
||||
123
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const handleOk = () => {
|
||||
|
||||
}
|
||||
const handleCancel = () => {
|
||||
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="当前进度" @ok="handleOk" @cancel="handleCancel">
|
||||
<div>
|
||||
<a-badge v-if="flag" status="processing" text="进行中" />
|
||||
<a-badge v-else status="success" text="已完成" />
|
||||
</div>
|
||||
<p>总数量:{{count}}</p>
|
||||
<a></a>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { downloadFile } from '@/utils/utils'
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const props = defineProps({
|
||||
api: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const eventSource = ref<Record<string, any>>({})
|
||||
const count = ref<number>(0)
|
||||
const flag = ref<boolean>(false)
|
||||
const errMessage = ref<string>('')
|
||||
const isSource = ref<boolean>(false)
|
||||
const id = ref<string>('')
|
||||
|
||||
const handleOk = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const getData = () => {
|
||||
|
||||
}
|
||||
|
||||
watch(() => props.api,
|
||||
() => {
|
||||
getData()
|
||||
},
|
||||
{deep: true, immediate: true}
|
||||
)
|
||||
</script>
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<JTable ref="instanceRef" :columns="columns" :request="query" :defaultParams="{sorts: [{name: 'createTime', order: 'desc'}]}" :params="{pageIndex: 0, pageSize: 12}">
|
||||
<JTable
|
||||
ref="instanceRef"
|
||||
:columns="columns"
|
||||
:request="query"
|
||||
:defaultParams="{sorts: [{name: 'createTime', order: 'desc'}]}"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange
|
||||
}"
|
||||
@cancelSelect="cancelSelect"
|
||||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
|
@ -8,13 +19,33 @@
|
|||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">1st menu item</a>
|
||||
<a-button @click="exportVisible = true"><AIcon type="ExportOutlined" />批量导出设备</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">2nd menu item</a>
|
||||
<a-button @click="importVisible = true"><AIcon type="ImportOutlined" />批量导入设备</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">3rd menu item</a>
|
||||
<a-popconfirm @confirm="activeAllDevice" title="确认激活全部设备?">
|
||||
<a-button type="primary" ghost><AIcon type="CheckCircleOutlined" />激活全部设备</a-button>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button @click="syncDeviceStatus" type="primary"><AIcon type="SyncOutlined" />同步设备状态</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="_selectedRowKeys.length">
|
||||
<a-popconfirm @confirm="delSelectedDevice" title="已启用的设备无法删除,确认删除选中的禁用状态设备?">
|
||||
<a-button type="primary" danger><AIcon type="DeleteOutlined" />删除选中设备</a-button>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="_selectedRowKeys.length" title="确认激活选中设备?">
|
||||
<a-popconfirm @confirm="activeSelectedDevice" >
|
||||
<a-button type="primary"><AIcon type="CheckOutlined" />激活选中设备</a-button>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="_selectedRowKeys.length">
|
||||
<a-popconfirm @confirm="disabledSelectedDevice" title="确认禁用选中设备?">
|
||||
<a-button type="primary" danger><AIcon type="StopOutlined" />禁用选中设备</a-button>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
|
@ -24,9 +55,10 @@
|
|||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleView"
|
||||
@click="handleClick"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
v-bind="slotProps"
|
||||
:active="_selectedRowKeys.includes(slotProps.id)"
|
||||
:status="slotProps.state.value"
|
||||
:statusText="slotProps.state.text"
|
||||
:statusNames="{
|
||||
|
@ -41,7 +73,7 @@
|
|||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<h3>{{ slotProps.name }}</h3>
|
||||
<h3 @click="handleView(slotProps.id)">{{ slotProps.name }}</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">设备类型</div>
|
||||
|
@ -116,15 +148,30 @@
|
|||
</a-space>
|
||||
</template>
|
||||
</JTable>
|
||||
<Import v-if="importVisible" @close="importVisible = false" />
|
||||
<Export v-if="exportVisible" @close="exportVisible = false" :data="params" />
|
||||
<Process v-if="operationVisible" @close="operationVisible = false" :api="api" :type="type" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { query, _delete, _deploy, _undeploy } from '@/api/device/instance'
|
||||
import { query, _delete, _deploy, _undeploy, batchUndeployDevice, batchDeployDevice, batchDeleteDevice } from '@/api/device/instance'
|
||||
import type { ActionsType } from '@/components/Table/index.vue'
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { getImage, LocalStore } from '@/utils/comm';
|
||||
import { message } from "ant-design-vue";
|
||||
import Import from './Import/index.vue'
|
||||
import Export from './Export/index.vue'
|
||||
import Process from './Process/index.vue'
|
||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({pageIndex: 0, pageSize: 12})
|
||||
const _selectedRowKeys = ref<string[]>([])
|
||||
const importVisible = ref<boolean>(false)
|
||||
const exportVisible = ref<boolean>(false)
|
||||
const current = ref<Record<string, any>>({})
|
||||
const operationVisible = ref<boolean>(false)
|
||||
const api = ref<string>('')
|
||||
const type = ref<string>('')
|
||||
|
||||
const statusMap = new Map();
|
||||
statusMap.set('online', 'processing');
|
||||
|
@ -173,12 +220,45 @@ const columns = [
|
|||
}
|
||||
]
|
||||
|
||||
const paramsFormat = (config: any, _terms: any, name?: string) => {
|
||||
if (config?.terms && Array.isArray(config.terms) && config?.terms.length > 0) {
|
||||
(config?.terms || []).map((item: any, index: number) => {
|
||||
if (item?.type) {
|
||||
_terms[`${name ? `${name}.` : ''}terms[${index}].type`] = item.type;
|
||||
}
|
||||
paramsFormat(item, _terms, `${name ? `${name}.` : ''}terms[${index}]`);
|
||||
});
|
||||
} else if (!config?.terms && Object.keys(config).length > 0) {
|
||||
Object.keys(config).forEach((key) => {
|
||||
if (config[key]) {
|
||||
_terms[`${name ? `${name}.` : ''}${key}`] = config[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const handleParams = (config: any) => {
|
||||
const _terms: any = {};
|
||||
paramsFormat(config, _terms);
|
||||
const url = new URLSearchParams();
|
||||
Object.keys(_terms).forEach((key) => {
|
||||
url.append(key, _terms[key]);
|
||||
});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
const handleAdd = () => {
|
||||
message.warn('123')
|
||||
message.warn('新增')
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
const handleView = (dt: any) => {
|
||||
|
||||
// message.warn('查看')
|
||||
}
|
||||
|
||||
const getActions = (data: Partial<Record<string, any>>, type: 'card' | 'table'): ActionsType[] => {
|
||||
|
@ -257,4 +337,61 @@ const getActions = (data: Partial<Record<string, any>>, type: 'card' | 'table'):
|
|||
return actions
|
||||
}
|
||||
|
||||
const onSelectChange = (keys: string[]) => {
|
||||
_selectedRowKeys.value = [...keys]
|
||||
}
|
||||
|
||||
const cancelSelect = () => {
|
||||
_selectedRowKeys.value = []
|
||||
}
|
||||
|
||||
const handleClick = (dt: any) => {
|
||||
if(_selectedRowKeys.value.includes(dt.id)) {
|
||||
const _index = _selectedRowKeys.value.findIndex(i => i === dt.id)
|
||||
_selectedRowKeys.value.splice(_index, 1)
|
||||
} else {
|
||||
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id]
|
||||
}
|
||||
}
|
||||
|
||||
const activeAllDevice = () => {
|
||||
type.value = 'active'
|
||||
const activeAPI = `/${BASE_API_PATH}/device-instance/deploy?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&${handleParams(params)}`;
|
||||
api.value = activeAPI
|
||||
operationVisible.value = true
|
||||
}
|
||||
|
||||
const syncDeviceStatus = () => {
|
||||
type.value = 'sync'
|
||||
const syncAPI = `/${BASE_API_PATH}/device-instance/state/_sync?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&${handleParams(params)}`;
|
||||
api.value = syncAPI
|
||||
operationVisible.value = true
|
||||
}
|
||||
|
||||
const delSelectedDevice = async () => {
|
||||
const resp = await batchDeleteDevice(_selectedRowKeys.value)
|
||||
if(resp.status === 200){
|
||||
message.success('操作成功!')
|
||||
_selectedRowKeys.value = []
|
||||
instanceRef.value?.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const activeSelectedDevice = async () => {
|
||||
const resp = await batchDeployDevice(_selectedRowKeys.value)
|
||||
if(resp.status === 200){
|
||||
message.success('操作成功!')
|
||||
_selectedRowKeys.value = []
|
||||
instanceRef.value?.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const disabledSelectedDevice = async () => {
|
||||
const resp = await batchUndeployDevice(_selectedRowKeys.value)
|
||||
if(resp.status === 200){
|
||||
message.success('操作成功!')
|
||||
_selectedRowKeys.value = []
|
||||
instanceRef.value?.reload()
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -313,7 +313,6 @@ const formData = ref<ConfigFormData>({
|
|||
configuration: {
|
||||
appKey: '',
|
||||
appSecret: '',
|
||||
url: '',
|
||||
},
|
||||
description: '',
|
||||
name: '',
|
||||
|
@ -325,13 +324,20 @@ const formData = ref<ConfigFormData>({
|
|||
watch(
|
||||
() => formData.value.type,
|
||||
(val) => {
|
||||
formData.value.configuration = CONFIG_FIELD_MAP[val];
|
||||
// formData.value.configuration = Object.values<any>(CONFIG_FIELD_MAP[val])[0];
|
||||
msgType.value = MSG_TYPE[val];
|
||||
|
||||
formData.value.provider = msgType.value[0].value;
|
||||
},
|
||||
);
|
||||
|
||||
computed(() =>
|
||||
Object.assign(
|
||||
formData.value.configuration,
|
||||
CONFIG_FIELD_MAP[formData.value.type][formData.value.provider],
|
||||
),
|
||||
);
|
||||
|
||||
// 验证规则
|
||||
const formRules = ref({
|
||||
type: [{ required: true, message: '请选择通知方式' }],
|
||||
|
|
|
@ -3,8 +3,7 @@ export interface IHeaders {
|
|||
key: string;
|
||||
value: string;
|
||||
}
|
||||
export type ConfigFormData = {
|
||||
configuration: {
|
||||
export interface IConfiguration {
|
||||
// 钉钉
|
||||
appKey?: string;
|
||||
appSecret?: string;
|
||||
|
@ -30,7 +29,36 @@ export type ConfigFormData = {
|
|||
// webhook
|
||||
// url?: string;
|
||||
headers?: IHeaders[];
|
||||
};
|
||||
}
|
||||
export type ConfigFormData = {
|
||||
configuration: IConfiguration;
|
||||
// configuration: {
|
||||
// // 钉钉
|
||||
// appKey?: string;
|
||||
// appSecret?: string;
|
||||
// url?: string;
|
||||
// // 微信
|
||||
// corpId?: string;
|
||||
// corpSecret?: string;
|
||||
// // 邮件
|
||||
// host?: string;
|
||||
// port?: number;
|
||||
// ssl?: boolean;
|
||||
// sender?: string;
|
||||
// username?: string;
|
||||
// password?: string;
|
||||
// // 语音
|
||||
// regionId?: string;
|
||||
// accessKeyId?: string;
|
||||
// secret?: string;
|
||||
// // 短信
|
||||
// regionId?: string;
|
||||
// accessKeyId?: string;
|
||||
// secret?: string;
|
||||
// // webhook
|
||||
// // url?: string;
|
||||
// headers?: IHeaders[];
|
||||
// };
|
||||
description: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<!-- webhook请求头可编辑表格 -->
|
||||
<template>
|
||||
<div class="attachment-wrapper">
|
||||
<div
|
||||
class="attachment-item"
|
||||
v-for="(item, index) in fileList"
|
||||
:key="index"
|
||||
>
|
||||
<a-input v-model:value="item.name">
|
||||
<template #addonAfter>
|
||||
<a-upload
|
||||
name="file"
|
||||
:action="FILE_UPLOAD"
|
||||
:headers="{
|
||||
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
|
||||
}"
|
||||
:showUploadList="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<upload-outlined />
|
||||
</a-upload>
|
||||
</template>
|
||||
</a-input>
|
||||
<delete-outlined @click="handleDelete" style="cursor: pointer" />
|
||||
</div>
|
||||
|
||||
<a-button
|
||||
type="dashed"
|
||||
@click="handleAdd"
|
||||
style="width: 100%; margin-top: 5px"
|
||||
>
|
||||
<template #icon>
|
||||
<plus-outlined />
|
||||
</template>
|
||||
添加
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Attachments">
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
UploadOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { PropType } from 'vue';
|
||||
import { IAttachments } from '../../types';
|
||||
import { FILE_UPLOAD } from '@/api/comm';
|
||||
import { LocalStore } from '@/utils/comm';
|
||||
import { TOKEN_KEY } from '@/utils/variable';
|
||||
import { UploadChangeParam } from 'ant-design-vue';
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:attachments', data: IAttachments[]): void;
|
||||
};
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const props = defineProps({
|
||||
attachments: {
|
||||
type: Array as PropType<IAttachments[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const handleChange = (info: UploadChangeParam) => {
|
||||
if (info.file.status === 'done') {
|
||||
const result = info.file.response?.result;
|
||||
console.log('result: ', result);
|
||||
}
|
||||
};
|
||||
|
||||
const fileList = ref<IAttachments[]>([]);
|
||||
watch(
|
||||
() => props.attachments,
|
||||
(val) => {
|
||||
fileList.value = val;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
const idx = fileList.value.findIndex((f) => f.id === id);
|
||||
fileList.value.splice(idx, 1);
|
||||
emit('update:attachments', fileList.value);
|
||||
};
|
||||
const handleAdd = () => {
|
||||
fileList.value.push({
|
||||
id: fileList.value.length,
|
||||
name: '',
|
||||
location: '',
|
||||
});
|
||||
emit('update:attachments', fileList.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.attachment-wrapper {
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -31,40 +31,46 @@
|
|||
<a-form-item
|
||||
label="类型"
|
||||
v-bind="validateInfos.provider"
|
||||
v-if="formData.type !== 'email'"
|
||||
v-if="
|
||||
formData.type !== 'email' &&
|
||||
formData.type !== 'webhook'
|
||||
"
|
||||
>
|
||||
<RadioCard
|
||||
:options="msgType"
|
||||
v-model="formData.provider"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="绑定配置"
|
||||
v-bind="validateInfos.configId"
|
||||
v-if="formData.type !== 'email'"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="formData.configId"
|
||||
placeholder="请选择绑定配置"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(item, index) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<!-- 钉钉 -->
|
||||
<template v-if="formData.type === 'dingTalk'">
|
||||
<template
|
||||
v-if="formData.provider === 'dingTalkMessage'"
|
||||
>
|
||||
<a-form-item
|
||||
label="AppKey"
|
||||
v-bind="
|
||||
validateInfos['configuration.appKey']
|
||||
"
|
||||
label="AgentId"
|
||||
v-bind="validateInfos['template.agentId']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.appKey
|
||||
"
|
||||
placeholder="请输入AppKey"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="AppSecret"
|
||||
v-bind="
|
||||
validateInfos['configuration.appSecret']
|
||||
"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.appSecret
|
||||
formData.template.agentId
|
||||
"
|
||||
placeholder="请输入AppSecret"
|
||||
/>
|
||||
|
@ -76,155 +82,340 @@
|
|||
"
|
||||
>
|
||||
<a-form-item
|
||||
label="webHook"
|
||||
v-bind="validateInfos['configuration.url']"
|
||||
label="消息类型"
|
||||
v-bind="
|
||||
validateInfos['template.messageType']
|
||||
"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="
|
||||
formData.template.messageType
|
||||
"
|
||||
placeholder="请选择消息类型"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(
|
||||
item, index
|
||||
) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<template
|
||||
v-if="
|
||||
formData.template.messageType ===
|
||||
'markdown'
|
||||
"
|
||||
>
|
||||
<a-form-item
|
||||
label="标题"
|
||||
v-bind="
|
||||
validateInfos[
|
||||
'template.markdown.title'
|
||||
]
|
||||
"
|
||||
>
|
||||
<!-- <a-input
|
||||
v-model:value="
|
||||
formData.template.markdown
|
||||
?.title
|
||||
"
|
||||
placeholder="请输入标题"
|
||||
/> -->
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- <template
|
||||
v-if="
|
||||
formData.template.messageType === 'link'
|
||||
"
|
||||
>
|
||||
<a-form-item
|
||||
label="标题"
|
||||
v-bind="
|
||||
validateInfos['template.link.title']
|
||||
"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.url
|
||||
formData.template.link?.title
|
||||
"
|
||||
placeholder="请输入webHook"
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图片链接">
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.template.link?.picUrl
|
||||
"
|
||||
placeholder="请输入图片链接"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="内容链接">
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.template.link
|
||||
?.messageUrl
|
||||
"
|
||||
placeholder="请输入内容链接"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template> -->
|
||||
</template>
|
||||
</template>
|
||||
<!-- 微信 -->
|
||||
<template v-if="formData.type === 'weixin'">
|
||||
<a-form-item
|
||||
label="corpId"
|
||||
v-bind="validateInfos['configuration.corpId']"
|
||||
label="AgentId"
|
||||
v-bind="validateInfos['template.agentId']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.corpId
|
||||
"
|
||||
placeholder="请输入corpId"
|
||||
v-model:value="formData.template.agentId"
|
||||
placeholder="请输入agentId"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="corpSecret"
|
||||
v-bind="
|
||||
validateInfos['configuration.corpSecret']
|
||||
"
|
||||
>
|
||||
<a-input
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="收信人">
|
||||
<a-select
|
||||
v-model:value="
|
||||
formData.configuration.corpSecret
|
||||
formData.template.toUser
|
||||
"
|
||||
placeholder="请输入corpSecret"
|
||||
/>
|
||||
placeholder="请选择收信人"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(
|
||||
item, index
|
||||
) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="收信部门">
|
||||
<a-select
|
||||
v-model:value="
|
||||
formData.template.toParty
|
||||
"
|
||||
placeholder="请选择收信部门"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(
|
||||
item, index
|
||||
) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="标签推送">
|
||||
<a-select
|
||||
v-model:value="formData.template.toTag"
|
||||
placeholder="请选择标签推送"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(item, index) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 邮件 -->
|
||||
<template v-if="formData.type === 'email'">
|
||||
<a-form-item
|
||||
label="服务器地址"
|
||||
v-bind="validateInfos['configuration.host']"
|
||||
>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.host
|
||||
"
|
||||
placeholder="请输入服务器地址"
|
||||
/>
|
||||
<a-input-number
|
||||
v-model:value="
|
||||
formData.configuration.port
|
||||
"
|
||||
/>
|
||||
<a-checkbox
|
||||
v-model:value="
|
||||
formData.configuration.ssl
|
||||
"
|
||||
>
|
||||
开启SSL
|
||||
</a-checkbox>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="发件人"
|
||||
v-bind="validateInfos['configuration.sender']"
|
||||
label="标题"
|
||||
v-bind="validateInfos['template.subject']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.sender
|
||||
"
|
||||
placeholder="请输入发件人"
|
||||
v-model:value="formData.template.subject"
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="用户名"
|
||||
v-bind="validateInfos['configuration.username']"
|
||||
<a-form-item label="收件人">
|
||||
<a-select
|
||||
v-model:value="formData.template.sendTo"
|
||||
placeholder="请选择收件人"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.username
|
||||
"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
<a-select-option
|
||||
v-for="(item, index) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码"
|
||||
v-bind="validateInfos['configuration.password']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.password
|
||||
<a-form-item label="附件信息">
|
||||
<Attachments
|
||||
v-model:attachments="
|
||||
formData.template.attachments
|
||||
"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 语音/短信 -->
|
||||
<template
|
||||
v-if="
|
||||
formData.type === 'voice' ||
|
||||
formData.type === 'sms'
|
||||
"
|
||||
>
|
||||
<!-- 语音 -->
|
||||
<template v-if="formData.type === 'voice'">
|
||||
<a-form-item
|
||||
label="AccessKeyId"
|
||||
label="类型"
|
||||
v-bind="validateInfos['template.templateType']"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="
|
||||
formData.template.templateType
|
||||
"
|
||||
placeholder="请选择类型"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(item, index) in VOICE_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="模板ID"
|
||||
v-bind="
|
||||
validateInfos['configuration.accessKeyId']
|
||||
validateInfos[
|
||||
'template.templateCode'
|
||||
]
|
||||
"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.accessKeyId
|
||||
formData.template.templateCode
|
||||
"
|
||||
placeholder="请输入AccessKeyId"
|
||||
placeholder="请输入模板ID"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="被叫号码">
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.template.calledNumber
|
||||
"
|
||||
placeholder="请输入被叫号码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="被叫显号">
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.template.calledShowNumbers
|
||||
"
|
||||
placeholder="请输入被叫显号"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="播放次数">
|
||||
<a-input
|
||||
v-model:value="formData.template.playTimes"
|
||||
placeholder="请输入播放次数"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Secret"
|
||||
v-bind="validateInfos['configuration.secret']"
|
||||
label="模板内容"
|
||||
v-if="formData.template.templateType === 'tts'"
|
||||
>
|
||||
<a-textarea
|
||||
v-model:value="formData.template.ttsCode"
|
||||
show-count
|
||||
:rows="5"
|
||||
placeholder="内容中的变量将用于阿里云语音验证码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 短信 -->
|
||||
<template v-if="formData.type === 'sms'">
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="模板"
|
||||
v-bind="validateInfos['template.code']"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="
|
||||
formData.template.code
|
||||
"
|
||||
placeholder="请选择模板"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(
|
||||
item, index
|
||||
) in ROBOT_MSG_TYPE"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="收信人">
|
||||
<a-input
|
||||
v-model:value="
|
||||
formData.configuration.secret
|
||||
formData.template.phoneNumber
|
||||
"
|
||||
placeholder="Secret"
|
||||
placeholder="请输入收信人"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
label="签名"
|
||||
v-bind="validateInfos['template.signName']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.template.signName"
|
||||
placeholder="请输入签名"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- webhook -->
|
||||
<template v-if="formData.type === 'webhook'">
|
||||
<a-form-item
|
||||
label="Webhook"
|
||||
v-bind="validateInfos['configuration.url']"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.configuration.url"
|
||||
placeholder="请输入Webhook"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="请求头">
|
||||
<!-- <EditTable
|
||||
v-model:headers="
|
||||
formData.configuration.headers
|
||||
<a-form-item label="请求体">
|
||||
<a-radio-group
|
||||
v-model:value="
|
||||
formData.template.contextAsBody
|
||||
"
|
||||
/> -->
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
<a-radio :value="true">默认</a-radio>
|
||||
<a-radio :value="false">自定义</a-radio>
|
||||
</a-radio-group>
|
||||
<a-textarea
|
||||
v-model:value="formData.template.body"
|
||||
placeholder="请求体中的数据来自于发送通知时指定的所有变量"
|
||||
v-if="formData.template.contextAsBody"
|
||||
disabled
|
||||
:rows="5"
|
||||
/>
|
||||
<div v-else style="height: 400px">
|
||||
<MonacoEditor
|
||||
theme="vs"
|
||||
v-model:modelValue="
|
||||
formData.template.body
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="说明">
|
||||
|
@ -263,12 +454,15 @@ import { message } from 'ant-design-vue';
|
|||
import { TemplateFormData } from '../types';
|
||||
import {
|
||||
NOTICE_METHOD,
|
||||
CONFIG_FIELD_MAP,
|
||||
TEMPLATE_FIELD_MAP,
|
||||
MSG_TYPE,
|
||||
ROBOT_MSG_TYPE,
|
||||
VOICE_TYPE,
|
||||
} from '@/views/notice/const';
|
||||
// import EditTable from './components/EditTable.vue';
|
||||
import configApi from '@/api/notice/config';
|
||||
import templateApi from '@/api/notice/template';
|
||||
import Doc from './doc/index';
|
||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||
import Attachments from './components/Attachments.vue'
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
@ -290,30 +484,34 @@ const msgType = ref([
|
|||
|
||||
// 表单数据
|
||||
const formData = ref<TemplateFormData>({
|
||||
description: '',
|
||||
template: {},
|
||||
name: '',
|
||||
provider: '',
|
||||
type: NOTICE_METHOD[2].value,
|
||||
template: {
|
||||
subject: '',
|
||||
sendTo: [],
|
||||
attachments: [],
|
||||
message: '',
|
||||
text: '',
|
||||
},
|
||||
type: 'email',
|
||||
provider: 'embedded',
|
||||
description: '',
|
||||
variableDefinitions: [],
|
||||
});
|
||||
|
||||
// 根据通知方式展示对应的字段
|
||||
watch(
|
||||
() => formData.value.type,
|
||||
(val) => {
|
||||
formData.value.configuration = CONFIG_FIELD_MAP[val];
|
||||
// formData.value.template = TEMPLATE_FIELD_MAP[val];
|
||||
msgType.value = MSG_TYPE[val];
|
||||
|
||||
formData.value.provider = msgType.value[0].value;
|
||||
console.log('formData.value.template: ', formData.value.template);
|
||||
},
|
||||
);
|
||||
|
||||
computed(() => {
|
||||
console.log('formData.value.type: ', formData.value.type);
|
||||
Object.assign(
|
||||
formData.value.template,
|
||||
TEMPLATE_FIELD_MAP[formData.value.type][formData.value.provider],
|
||||
);
|
||||
});
|
||||
|
||||
// 验证规则
|
||||
const formRules = ref({
|
||||
type: [{ required: true, message: '请选择通知方式' }],
|
||||
|
@ -322,58 +520,23 @@ const formRules = ref({
|
|||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
provider: [{ required: true, message: '请选择类型' }],
|
||||
configId: [{ required: true, message: '请选择绑定配置' }],
|
||||
// 钉钉
|
||||
'configuration.appKey': [
|
||||
{ required: true, message: '请输入AppKey' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'configuration.appSecret': [
|
||||
{ required: true, message: '请输入AppSecret' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
// 'configuration.url': [{ required: true, message: '请输入WebHook' }],
|
||||
'template.agentId': [{ required: true, message: '请输入agentId' }],
|
||||
'template.messageType': [{ required: true, message: '请选择消息类型' }],
|
||||
'template.markdown.title': [{ required: true, message: '请输入标题' }],
|
||||
// 'template.url': [{ required: true, message: '请输入WebHook' }],
|
||||
// 微信
|
||||
'configuration.corpId': [
|
||||
{ required: true, message: '请输入corpId' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'configuration.corpSecret': [
|
||||
{ required: true, message: '请输入corpSecret' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
// 阿里云语音/短信
|
||||
'configuration.regionId': [
|
||||
{ required: true, message: '请输入RegionId' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'configuration.accessKeyId': [
|
||||
{ required: true, message: '请输入AccessKeyId' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'configuration.secret': [
|
||||
{ required: true, message: '请输入Secret' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
// 'template.agentId': [{ required: true, message: '请输入agentId' }],
|
||||
// 邮件
|
||||
'configuration.host': [{ required: true, message: '请输入服务器地址' }],
|
||||
'configuration.sender': [{ required: true, message: '请输入发件人' }],
|
||||
'configuration.username': [
|
||||
{ required: true, message: '请输入用户名' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'configuration.password': [
|
||||
{ required: true, message: '请输入密码' },
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
],
|
||||
'template.subject': [{ required: true, message: '请输入标题' }],
|
||||
// 阿里云语音
|
||||
'template.templateType': [{ required: true, message: '请选择类型' }],
|
||||
'template.templateCode': [{ required: true, message: '请输入模板ID' }],
|
||||
// 短信
|
||||
'template.code': [{ required: true, message: '请选择模板' }],
|
||||
'template.signName': [{ required: true, message: '请输入签名' }],
|
||||
// webhook
|
||||
'configuration.url': [
|
||||
{ required: true, message: '请输入Webhook' },
|
||||
{
|
||||
pattern:
|
||||
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/,
|
||||
message: 'Webhook需要是一个合法的URL',
|
||||
},
|
||||
],
|
||||
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||
});
|
||||
|
||||
|
@ -390,12 +553,12 @@ watch(
|
|||
);
|
||||
|
||||
const getDetail = async () => {
|
||||
const res = await configApi.detail(route.params.id as string);
|
||||
const res = await templateApi.detail(route.params.id as string);
|
||||
// console.log('res: ', res);
|
||||
formData.value = res.result;
|
||||
// console.log('formData.value: ', formData.value);
|
||||
};
|
||||
getDetail();
|
||||
// getDetail();
|
||||
|
||||
/**
|
||||
* 表单提交
|
||||
|
@ -404,19 +567,19 @@ const btnLoading = ref<boolean>(false);
|
|||
const handleSubmit = () => {
|
||||
validate()
|
||||
.then(async () => {
|
||||
// console.log('formData.value: ', formData.value);
|
||||
console.log('formData.value: ', formData.value);
|
||||
btnLoading.value = true;
|
||||
let res;
|
||||
if (!formData.value.id) {
|
||||
res = await configApi.save(formData.value);
|
||||
} else {
|
||||
res = await configApi.update(formData.value);
|
||||
}
|
||||
// console.log('res: ', res);
|
||||
if (res?.success) {
|
||||
message.success('保存成功');
|
||||
router.back();
|
||||
}
|
||||
// let res;
|
||||
// if (!formData.value.id) {
|
||||
// res = await templateApi.save(formData.value);
|
||||
// } else {
|
||||
// res = await templateApi.update(formData.value);
|
||||
// }
|
||||
// // console.log('res: ', res);
|
||||
// if (res?.success) {
|
||||
// message.success('保存成功');
|
||||
// router.back();
|
||||
// }
|
||||
btnLoading.value = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
|
@ -3,6 +3,20 @@
|
|||
<div class="page-container">通知模板</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import templateApi from '@/api/notice/template';
|
||||
|
||||
const getList = async () => {
|
||||
const res = await templateApi.list({
|
||||
current: 1,
|
||||
pageIndex: 0,
|
||||
pageSize: 12,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
terms: [],
|
||||
});
|
||||
console.log('res: ', res);
|
||||
};
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface IHeaders {
|
|||
interface IAttachments {
|
||||
location: string;
|
||||
name: string;
|
||||
id?: number;
|
||||
}
|
||||
interface IVariableDefinitions {
|
||||
id: string;
|
||||
|
@ -16,14 +17,6 @@ interface IVariableDefinitions {
|
|||
}
|
||||
|
||||
export type TemplateFormData = {
|
||||
name: string;
|
||||
type: string;
|
||||
provider: string;
|
||||
description: string;
|
||||
id?: string;
|
||||
creatorId?: string;
|
||||
createTime?: number;
|
||||
configId?: string;
|
||||
template: {
|
||||
// 钉钉消息
|
||||
agentId?: string;
|
||||
|
@ -41,7 +34,7 @@ export type TemplateFormData = {
|
|||
text: string;
|
||||
};
|
||||
// 微信
|
||||
agentId?: string;
|
||||
// agentId?: string;
|
||||
// message?: string;
|
||||
toParty?: string;
|
||||
toUser?: string;
|
||||
|
@ -69,6 +62,13 @@ export type TemplateFormData = {
|
|||
contextAsBody?: boolean;
|
||||
body?: string;
|
||||
};
|
||||
name: string;
|
||||
type: string;
|
||||
provider: string;
|
||||
description: string;
|
||||
variableDefinitions: IVariableDefinitions[];
|
||||
|
||||
id?: string;
|
||||
creatorId?: string;
|
||||
createTime?: number;
|
||||
configId?: string;
|
||||
};
|
|
@ -33,7 +33,7 @@ export const NOTICE_METHOD: INoticeMethod[] = [
|
|||
},
|
||||
];
|
||||
|
||||
// 消息类型
|
||||
// 类型
|
||||
export const MSG_TYPE = {
|
||||
dingTalk: [
|
||||
{
|
||||
|
@ -93,36 +93,52 @@ export const MSG_TYPE = {
|
|||
// 配置
|
||||
export const CONFIG_FIELD_MAP = {
|
||||
dingTalk: {
|
||||
appKey: undefined,
|
||||
appSecret: undefined,
|
||||
url: undefined,
|
||||
dingTalkMessage: {
|
||||
appKey: '',
|
||||
appSecret: '',
|
||||
},
|
||||
dingTalkRobotWebHook: {
|
||||
url: '',
|
||||
}
|
||||
},
|
||||
weixin: {
|
||||
corpId: undefined,
|
||||
corpSecret: undefined,
|
||||
corpMessage: {
|
||||
corpId: '',
|
||||
corpSecret: '',
|
||||
},
|
||||
// officialMessage: {},
|
||||
},
|
||||
email: {
|
||||
host: undefined,
|
||||
embedded: {
|
||||
host: '',
|
||||
port: 25,
|
||||
ssl: false,
|
||||
sender: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
sender: '',
|
||||
username: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
regionId: undefined,
|
||||
accessKeyId: undefined,
|
||||
secret: undefined,
|
||||
aliyun: {
|
||||
regionId: '',
|
||||
accessKeyId: '',
|
||||
secret: '',
|
||||
}
|
||||
},
|
||||
sms: {
|
||||
regionId: undefined,
|
||||
accessKeyId: undefined,
|
||||
secret: undefined,
|
||||
aliyunSms: {
|
||||
regionId: '',
|
||||
accessKeyId: '',
|
||||
secret: '',
|
||||
}
|
||||
},
|
||||
webhook: {
|
||||
http: {
|
||||
url: undefined,
|
||||
headers: [],
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// 模板
|
||||
|
@ -187,8 +203,20 @@ export const TEMPLATE_FIELD_MAP = {
|
|||
},
|
||||
webhook: {
|
||||
http: {
|
||||
contextAsBody: false,
|
||||
contextAsBody: true,
|
||||
body: ''
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 钉钉机器人-消息类型
|
||||
export const ROBOT_MSG_TYPE = [
|
||||
{ label: 'markdown', value: 'markdown' },
|
||||
{ label: 'text', value: 'text' },
|
||||
{ label: 'link', value: 'link' },
|
||||
]
|
||||
// 语音通知类型
|
||||
export const VOICE_TYPE = [
|
||||
{ label: '语音通知', value: 'voice' },
|
||||
{ label: '语音验证码', value: 'tts' },
|
||||
]
|
Loading…
Reference in New Issue