fix: 刷新表格

This commit is contained in:
100011797 2023-01-17 11:34:38 +08:00
parent 9e3857302b
commit eb7eaef064
11 changed files with 4372 additions and 4017 deletions

View File

@ -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",

View File

@ -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)

View File

@ -21,7 +21,11 @@ const iconKeys = [
'StopOutlined',
'CheckOutlined',
'CloseOutlined',
'DownOutlined'
'DownOutlined',
'ImportOutlined',
'ExportOutlined',
'SyncOutlined',
'ExclamationCircleOutlined'
]
const Icon = (props: {type: string}) => {

View File

@ -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
}

64
src/utils/encodeQuery.ts Normal file
View File

@ -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;
}

View File

@ -1,3 +1,7 @@
import moment from "moment";
import { LocalStore } from "./comm";
import { TOKEN_KEY } from "./variable";
/**
* JSON
* @param record
@ -18,4 +22,34 @@ export const downloadObject = (record: Record<string, any>, fileName: string, fo
ghostLink.click();
//移除
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);
};

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

7952
yarn.lock

File diff suppressed because it is too large Load Diff